Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JsonGen] Support for case convention switching #136

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 50 additions & 16 deletions JsonGenerator/source/class_emitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def IsObjectRestricted(argument):

def IsObjectOptionalOrOpaque(argument):
_by_required = ("required" in argument.parent.schema and argument.json_name not in argument.parent.schema["required"])
return (argument.schema.get("opaque") or argument.schema.get("@optional") or _by_required) and not argument.optional
return (argument.schema.get("opaque") or argument.schema.get("optional") or _by_required) and not argument.optional

def IsObjectOptional(argument):
if argument.optional or IsObjectOptionalOrOpaque(argument):
Expand Down Expand Up @@ -62,13 +62,47 @@ def join(self):
if (len(self.__cond) == 1):
return self.__cond[0]
elif (len(self.__cond) > 1):
return ((" %s " % ("&&" if self.__reverse else "||")).join(self.__cond))
return ((" %s " % ("&&" if self.__reverse else "||")).join(['(' + x + ')' for x in self.__cond]))
else:
return self.__comp[2]

def extend(self, element, pos=None):
self.__cond.insert(pos if pos else len(self.__cond), element)

def check_set(self, argument, pos=None):
if argument.optional:
self.__cond.insert(pos if pos else len(self.__cond), "%s.IsSet() == true" % argument.temp_name)

def check_not_set(self, argument, pos=None):
if argument.optional:
self.__cond.insert(pos if pos else len(self.__cond), "%s.IsSet() == false" % argument.temp_name)

def check_null(self, argument, pos=None):
name = argument.temp_name
if argument.optional:
name += ".Value()"
if isinstance(argument, JsonInteger):
self.__cond.insert(pos if pos else len(self.__cond), "%s == 0" % name)
elif isinstance(argument, JsonString):
self.__cond.insert(pos if pos else len(self.__cond), "%s.empty() == true" % name)
elif argument.iterator:
self.__cond.insert(pos if pos else len(self.__cond), "%s == nullptr" % name)
else:
assert False, "invalid type"

def check_not_null(self, argument, pos=None):
name = argument.temp_name
if argument.optional:
name += ".Value()"
if isinstance(argument, JsonInteger):
self.__cond.insert(pos if pos else len(self.__cond), "%s != 0" % name)
elif isinstance(argument, JsonString):
self.__cond.insert(pos if pos else len(self.__cond), "%s.empty() == false" % name)
elif argument.iterator:
self.__cond.insert(pos if pos else len(self.__cond), "%s != nullptr" % name)
else:
assert False, "invalid type"

def append(self, argument, relay=None, override=None, test_set=None):
if test_set == None:
test_set = self.__test_set
Expand All @@ -82,19 +116,19 @@ def append(self, argument, relay=None, override=None, test_set=None):
if test_set:
if IsObjectOptional(relay):
if self.__reverse:
self.__cond.append("((%s.IsSet() == false) || (%s.IsDataValid() == true))" % (name, name))
self.__cond.append("(%s.IsSet() == false) || (%s.IsDataValid() == true)" % (name, name))
else:
self.__cond.append("((%s.IsSet() == true) && (%s.IsDataValid() == false))" % (name, name))
self.__cond.append("(%s.IsSet() == true) && (%s.IsDataValid() == false)" % (name, name))
else:
if self.__reverse:
self.__cond.append("((%s.IsSet() == true) && (%s.IsDataValid() == true))" % (name, name))
self.__cond.append("(%s.IsSet() == true) && (%s.IsDataValid() == true)" % (name, name))
else:
self.__cond.append("((%s.IsSet() == false) || (%s.IsDataValid() == false))" % (name, name))
self.__cond.append("(%s.IsSet() == false) || (%s.IsDataValid() == false)" % (name, name))
else:
if self.__reverse:
self.__cond.append("(%s.IsDataValid() == true)" % name)
self.__cond.append("%s.IsDataValid() == true" % name)
else:
self.__cond.append("(%s.IsDataValid() == false)" % name)
self.__cond.append("%s.IsDataValid() == false" % name)

elif IsObjectRestricted(argument):
tests = []
Expand All @@ -104,24 +138,24 @@ def append(self, argument, relay=None, override=None, test_set=None):
if isinstance(relay, JsonString):
if range:
if range[0] != 0:
tests.append("(%s%s.size() %s %s)" % (name, (".Value()" if self.__json else ""), self.__comp[0], range[0]))
tests.append("%s%s.size() %s %s" % (name, (".Value()" if self.__json else ""), self.__comp[0], range[0]))

tests.append("(%s%s.size() %s %s)" % (name, (".Value()" if self.__json else ""), self.__comp[1], range[1]))
tests.append("%s%s.size() %s %s" % (name, (".Value()" if self.__json else ""), self.__comp[1], range[1]))

elif not isinstance(relay, JsonObject):
if range:
if range[0] or relay.schema.get("signed"):
tests.append("(%s %s %s)" % (name, self.__comp[0], range[0]))
tests.append("%s %s %s" % (name, self.__comp[0], range[0]))

tests.append("(%s %s %s)" % (name, self.__comp[1], range[1]))
tests.append("%s %s %s" % (name, self.__comp[1], range[1]))

if tests:
if test_set and self.__json:
if IsObjectOptional(argument):
if self.__reverse:
self.__cond.append("((%s.IsSet() == false) || (%s))" % (name, " &&".join(tests)))
self.__cond.append("(%s.IsSet() == false) || (%s)" % (name, " &&".join(tests)))
else:
self.__cond.append("((%s.IsSet() == true) && (%s))" % (name, " || ".join(tests)))
self.__cond.append("(%s.IsSet() == true) && (%s)" % (name, " || ".join(tests)))
else:
if self.__reverse:
self.__cond.append("((%s.IsSet() == true) && (%s))" % (name, " && ".join(tests)))
Expand All @@ -131,7 +165,7 @@ def append(self, argument, relay=None, override=None, test_set=None):
self.__cond.extend(tests)

elif test_set and self.__json and not IsObjectOptional(relay):
self.__cond.append("(%s.IsSet() == %s)" % (name, self.__comp[2]))
self.__cond.append("%s.IsSet() == %s" % (name, self.__comp[2]))

def ProcessEnums(log, action=None):
count = 0
Expand Down Expand Up @@ -240,7 +274,7 @@ def __EmitAssignment(json_obj, other, type, optional_type=False):
_optional_or_opaque = IsObjectOptionalOrOpaque(prop)

if (prop.optional and not prop.default_value):
emit.Line("if (%s.%s.IsSet() == true) {" % (other, prop.actual_name))
emit.Line("if (%s.%s.IsSet() == true) {" % (other, _prop_name))
emit.Indent()

elif _optional_or_opaque:
Expand Down
42 changes: 34 additions & 8 deletions JsonGenerator/source/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ class RpcFormat(Enum):
EXTENDED = "uncompliant-extended"
COLLAPSED = "uncompliant-collapsed"

class CaseConvention(Enum):
STANDARD = "standard"
LEGACY = "legacy"
KEEP = "keep"
CUSTOM = "custom"

DEFAULT_CASE_CONVENTION = CaseConvention.STANDARD

RPC_FORMAT = RpcFormat.COMPLIANT
RPC_FORMAT_FORCED = False

Expand Down Expand Up @@ -90,6 +98,7 @@ def Parse(cmdline):
global CLASSNAME_FROM_REF
global LEGACY_ALT
global AUTO_PREFIX
global DEFAULT_CASE_CONVENTION

argparser = argparse.ArgumentParser(
description='Generate JSON C++ classes, stub code and API documentation from JSON definition files and C++ header files',
Expand Down Expand Up @@ -195,9 +204,16 @@ def Parse(cmdline):
dest="format",
type=str,
action="store",
default="flexible",
default="default-compliant",
choices=["default-compliant", "force-compliant", "default-uncompliant-extended", "force-uncompliant-extended", "default-uncompliant-collapsed", "force-uncompliant-collapsed"],
help="select JSON-RPC data format (default: default-compliant)")
cpp_group.add_argument("--case-convention",
dest="case_convention",
type=str,
metavar="CONVENTION",
action="store",
default=DEFAULT_CASE_CONVENTION.value,
help="select JSON-RPC case convention (default: %s)" % DEFAULT_CASE_CONVENTION.value)

data_group = argparser.add_argument_group("C++ output arguments (optional)")
data_group.add_argument(
Expand Down Expand Up @@ -264,6 +280,13 @@ def Parse(cmdline):
action="store",
default=INDENT_SIZE,
help="code indentation in spaces (default: %i)" % INDENT_SIZE)
data_group.add_argument("--framework-namespace",
dest="framework_namespace",
metavar="NS",
type=str,
action="store",
default=FRAMEWORK_NAMESPACE,
help="set framework namespace")

doc_group = argparser.add_argument_group("Documentation output arguments (optional)")
doc_group.add_argument("--no-style-warnings",
Expand Down Expand Up @@ -292,13 +315,6 @@ def Parse(cmdline):
help="override interface source file revision to the commit id specified")

ts_group = argparser.add_argument_group("Troubleshooting arguments (optional)")
doc_group.add_argument("--framework-namespace",
dest="framework_namespace",
metavar="NS",
type=str,
action="store",
default=FRAMEWORK_NAMESPACE,
help="set framework namespace")
ts_group.add_argument("--verbose",
dest="verbose",
action="store_true",
Expand Down Expand Up @@ -334,6 +350,16 @@ def Parse(cmdline):
INTERFACE_SOURCE_REVISION = args.source_revision
AUTO_PREFIX = args.auto_prefix

if args.case_convention == "standard":
DEFAULT_CASE_CONVENTION = CaseConvention.STANDARD
elif args.case_convention == "legacy" or args.case_convention == "legacy_lowercase":
DEFAULT_CASE_CONVENTION = CaseConvention.LEGACY
elif args.case_convention == "keep":
DEFAULT_CASE_CONVENTION = CaseConvention.KEEP
else:
print("Invalid case convention")
exit(1)

if args.framework_namespace:
FRAMEWORK_NAMESPACE = args.framework_namespace
INTERFACE_NAMESPACES = ["::%s::Exchange::JSONRPC" % FRAMEWORK_NAMESPACE, "::%s::Exchange" % FRAMEWORK_NAMESPACE]
Expand Down
14 changes: 10 additions & 4 deletions JsonGenerator/source/documentation_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,16 @@ def _TableObj(name, obj, parentName="", parent=None, prefix="", parentOptional=F

# include information about enum values in description
enum = ""
default_enum = None
if "enum" in obj and "ids" in obj:
enums = []
endmarker = obj.get("@endmarker")
for i,e in enumerate(obj["ids"]):
if e == endmarker:
break;
enums.append(obj["enum"][i])
if "default" in obj and e == obj["default"]:
default_enum = enums[-1]

if enums:
enum = ' (must be one of the following: *%s*)' % (", ".join(sorted(enums)))
Expand Down Expand Up @@ -150,7 +153,10 @@ def _TableObj(name, obj, parentName="", parent=None, prefix="", parentOptional=F
row = row[:-1]

if "default" in obj:
row += " (default: " + (italics("%s") % str(obj["default"]) + ")")
if "enum" in obj and default_enum:
row += " (default: " + (italics("%s") % str(enums[obj["ids"].index(obj["default"])]) + ")")
else:
row += " (default: " + (italics("%s") % str(obj["default"]) + ")")

if obj["type"] == "number":
# correct number to integer
Expand All @@ -164,9 +170,9 @@ def _TableObj(name, obj, parentName="", parent=None, prefix="", parentOptional=F
if d["type"] == "string" or is_buffer:
str_text = "Decoded data" if d.get("encode") else "String"
if d["range"][0]:
row += italics("%s length must be in range [%s..%s] bytes." % (str_text, d["range"][0], d["range"][1]))
row += italics("%s length must be in range [%s..%s] chars." % (str_text, d["range"][0], d["range"][1]))
else:
row += italics("%s length must be at most %s bytes." % (str_text, d["range"][1]))
row += italics("%s length must be at most %s chars." % (str_text, d["range"][1]))
else:
row += italics("Value must be in range [%s..%s]." % (d["range"][0], d["range"][1]))

Expand Down Expand Up @@ -209,7 +215,7 @@ def ExampleObj(name, obj, root=False):
return "$deprecated"

obj_type = obj["type"]
default = obj["example"] if "example" in obj else obj["default"] if "default" in obj else ""
default = obj["example"] if "example" in obj else obj["default"] if ("default" in obj and "enum" not in obj) else ""

if not default and "enum" in obj:
default = obj["enum"][0]
Expand Down
37 changes: 34 additions & 3 deletions JsonGenerator/source/emitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
# limitations under the License.

class Emitter():
def __init__(self, file_name, indent_size, max_line_length = 160):
def __init__(self, file_name, indent_size, max_line_length = 220, autoindent=False):
self.file = open(file_name, "w") if file_name else None
self.indent_size = indent_size
self.indent = 0
self.threshold = max_line_length
self.lines = []
self.__autoindent = autoindent

def __del__(self):
pass
Expand All @@ -36,10 +37,20 @@ def __exit__(self, exc_type, exc_value, traceback):

def Line(self, text = ""):
if text != "":
text = str(text).rstrip()

if self.__autoindent and text[-1] == '{':
self.Indent()

commented = "// " if "//" in text else ""
text = (" " * self.indent) + str(text)
text = (" " * self.indent) + text
iteration = 1

if self.__autoindent and len(self.lines) and self.lines[-1].endswith("}"):
self.lines.append("")

unindent = (text == "}")

while len(text) > (self.threshold * iteration):
index = text.rfind(",", 0, self.threshold * iteration)
if index > 0:
Expand All @@ -50,10 +61,12 @@ def Line(self, text = ""):

self.lines.append(text)

if self.__autoindent and unindent:
self.Unindent()

elif len(self.lines) and self.lines[-1] != "":
self.lines.append("")


def Indent(self):
self.indent += self.indent_size

Expand All @@ -76,3 +89,21 @@ def Flush(self):
if self.file:
for line in self.lines:
self.file.write(line + "\n")

def EnterBlock(self, conditions=None):
if conditions:
if conditions.count():
if len(self.lines) and self.lines[-1] != "":
self.lines.append("")
self.Line("if (%s) {" % conditions.join())
self.Indent()
else:
self.Line("{")
self.Indent()

def ExitBlock(self, conditions=None):
if conditions:
if not conditions.count():
return
self.Unindent()
self.Line("}")
Loading