From 90d0c4ffd3621086306d2b15a635b063cfa3db94 Mon Sep 17 00:00:00 2001 From: TB Dev Date: Tue, 5 Mar 2019 00:30:24 -0500 Subject: [PATCH 01/11] Java generator for the catsbuffer --- generators/All.py | 4 +- generators/java/Helpers.py | 120 ++++++++++ generators/java/JavaClassGenerator.py | 309 +++++++++++++++++++++++++ generators/java/JavaEnumGenerator.py | 78 +++++++ generators/java/JavaFileGenerator.py | 62 +++++ generators/java/JavaMethodGenerator.py | 21 ++ 6 files changed, 593 insertions(+), 1 deletion(-) create mode 100755 generators/java/Helpers.py create mode 100644 generators/java/JavaClassGenerator.py create mode 100755 generators/java/JavaEnumGenerator.py create mode 100755 generators/java/JavaFileGenerator.py create mode 100755 generators/java/JavaMethodGenerator.py diff --git a/generators/All.py b/generators/All.py index 58ae92e7..d9e3b38f 100644 --- a/generators/All.py +++ b/generators/All.py @@ -1,5 +1,7 @@ from generators.cpp_builder.BuilderGenerator import BuilderGenerator +from generators.java.JavaFileGenerator import JavaFileGenerator AVAILABLE_GENERATORS = { - 'cpp_builder': BuilderGenerator + 'cpp_builder': BuilderGenerator, + 'java': JavaFileGenerator } diff --git a/generators/java/Helpers.py b/generators/java/Helpers.py new file mode 100755 index 00000000..d404ba3c --- /dev/null +++ b/generators/java/Helpers.py @@ -0,0 +1,120 @@ +import os +from enum import Enum + +class TypeDescriptorType(Enum): + Byte = 'byte' + Struct = 'struct' + Enum = 'enum' + + +def is_builtin_type(typename, size): + # byte up to long are passed as 'byte' with size set to proper value + return not isinstance(size, str) and TypeDescriptorType.Byte.value == typename and size <= 8 + + +class AttributeKind(Enum): + SIMPLE = 1 + BUFFER = 2 + ARRAY = 3 + CUSTOM = 4 + UNKNOWN = 100 + + +def get_attribute_size(schema, attribute): + if 'size' not in attribute and attribute['type'] != TypeDescriptorType.Byte.value and attribute['type'] != TypeDescriptorType.Enum.value: + attr = schema[attribute['type']] + if 'size' in attr: + return attr['size'] + else: + return 1 + return attribute['size'] + + +def get_attribute_kind(schema, attribute): + attribute_type = attribute['type'] + if attribute_type == TypeDescriptorType.Struct.value or attribute_type == TypeDescriptorType.Enum.value: + return AttributeKind.CUSTOM + if 'size' not in attribute: + type_descriptor = schema[attribute_type] + return get_attribute_kind(schema, type_descriptor) + + attribute_size = attribute['size'] + + if isinstance(attribute_size, str): + if attribute_size.endswith('Size'): + return AttributeKind.BUFFER + + if attribute_size.endswith('Count'): + return AttributeKind.ARRAY + + if is_builtin_type(attribute_type, attribute_size): + return AttributeKind.SIMPLE + + return AttributeKind.BUFFER + + +class TypeDescriptorDisposition(Enum): + Inline = 'inline' + Const = 'const' + + +def indent(code, n_indents=1): + return ' ' * 4 * n_indents + code + + +def get_attribute_if_size(attribute_name, attributes): + for attribute in attributes: + if 'size' in attribute and attribute['size'] == attribute_name: + return attribute['name'] + + return None + + +def get_attribute_where_property_equal(schema, attributes, attribute_name, attribute_value): + for attribute in attributes: + if attribute_name in attribute and attribute[attribute_name] == attribute_value: + return attribute + if 'disposition' in attribute and attribute['disposition'] == TypeDescriptorDisposition.Inline.value: + value = get_attribute_where_property_equal(schema, schema[attribute['type']]['layout'], attribute_name, attribute_value) + if value is not None: + return value + + return None + +def get_builtin_type(size): + builtin_types = {1: 'byte', 2: 'short', 4: 'int', 8: 'long'} + builtin_type = builtin_types[size] + return builtin_type + + +def get_read_method_name(size): + if isinstance(size, str) or size > 8: + method_name = 'readFully' + else: + typesize_methodname = {1: 'readByte', + 2: 'readShort', 4: 'readInt', 8: 'readLong'} + method_name = typesize_methodname[size] + return method_name + + +def get_write_method_name(size): + if isinstance(size, str) or size > 8: + method_name = 'write' + else: + typesize_methodname = {1: 'writeByte', + 2: 'writeShort', 4: 'writeInt', 8: 'writeLong'} + method_name = typesize_methodname[size] + return method_name + + +def get_generated_type(schema, attribute): + typename = attribute['type'] + attribute_kind = get_attribute_kind(schema, attribute) + if attribute_kind == AttributeKind.SIMPLE: + return get_builtin_type(get_attribute_size(schema, attribute)) + elif attribute_kind == AttributeKind.BUFFER: + return 'ByteBuffer' + elif attribute_kind == AttributeKind.ARRAY: + return 'java.util.ArrayList<{0}>'.format(typename) + + return typename \ No newline at end of file diff --git a/generators/java/JavaClassGenerator.py b/generators/java/JavaClassGenerator.py new file mode 100644 index 00000000..d744a878 --- /dev/null +++ b/generators/java/JavaClassGenerator.py @@ -0,0 +1,309 @@ +import os +from enum import Enum +from .Helpers import * +from .JavaMethodGenerator import JavaMethodGenerator + +class JavaClassGenerator: + @staticmethod + def get_generated_class_name(name): + return '{}'.format(name) + + def _get_generated_getter_name(self, attribute): + return 'get{}'.format(attribute.capitalize()) + + def _get_generated_setter_name(self, attribute_name): + return 'set{}'.format(attribute_name.capitalize()) + + def __init__(self, name, schema, class_schema): + self.class_name = JavaClassGenerator.get_generated_class_name(name) + self.class_output = ['public class {0} {{'.format(self.class_name)] + self.load_from_binary_method = None + self.serialize_method = None + self.schema = schema + self.class_schema = class_schema + self.privates = [] + + def _set_declarations(self): + self.class_output += [indent(line) for line in self.privates] + [''] + + def _add_private_declaration(self, attribute): + attribute_name = attribute['name'] + var_type = get_generated_type(self.schema, attribute) + self.privates += ['private {0} {1};'.format(var_type, attribute_name)] + + def _add_simple_getter(self, attribute, new_getter): + new_getter.add_instructions( + ['return this.{0}'.format(attribute['name'])]) + + def _add_buffer_getter(self, attribute, new_getter): + new_getter.add_instructions( + ['return this.{0}'.format(attribute['name'])]) + + def _add_array_getter(self, attribute, new_getter): + return_type = get_generated_type(self.schema, attribute) + new_getter.add_instructions( + ['return ({0})this.{1}'.format(return_type, attribute['name'])]) + + def _add_method_condition(self, attribute, method_writer): + if 'condition' in attribute: + condition_type_attribute = get_attribute_where_property_equal(self.schema, self.class_schema, 'name', attribute['condition']) + condition_type_prefix = '' + if condition_type_attribute is not None: + condition_type_prefix = '{0}.'.format(condition_type_attribute['type']) + + method_writer.add_instructions(['if ({0} != {1}{2})'.format( + attribute['condition'], condition_type_prefix, attribute['condition_value'].upper())], False) + method_writer.add_instructions( + [indent('throw new java.lang.IllegalStateException()')]) + method_writer.add_instructions([''], False) + + def _add_getter(self, attribute): + attribute_name = attribute['name'] + return_type = get_generated_type(self.schema, attribute) + new_getter = JavaMethodGenerator( + 'public', return_type, self._get_generated_getter_name(attribute_name), []) + self._add_method_condition(attribute, new_getter) + + getters = { + AttributeKind.SIMPLE: self._add_simple_getter, + AttributeKind.BUFFER: self._add_buffer_getter, + AttributeKind.ARRAY: self._add_array_getter, + AttributeKind.CUSTOM: self._add_simple_getter + } + attribute_kind = get_attribute_kind(self.schema, attribute) + getters[attribute_kind](attribute, new_getter) + self._add_method(new_getter) + + def _add_simple_setter(self, attribute, new_setter): + new_setter.add_instructions( + ['this.{0} = {0}'.format(attribute['name'])]) + + def _add_buffer_setter(self, attribute, new_setter): + attribute_size = get_attribute_size(self.schema, attribute) + attribute_name = attribute['name'] + new_setter.add_instructions( + ['if ({0} == null)'.format(attribute_name)], False) + new_setter.add_instructions( + [indent('throw new NullPointerException("{0}")'.format(attribute_name))]) + new_setter.add_instructions([''], False) + if not isinstance(attribute_size, str): + new_setter.add_instructions( + ['if ({0}.array().length != {1})'.format(attribute_name, attribute_size)], False) + new_setter.add_instructions( + [indent('throw new IllegalArgumentException("{0} should be {1} bytes")'.format(attribute_name, attribute_size))]) + new_setter.add_instructions([''], False) + new_setter.add_instructions( + ['this.{0} = {0}'.format(attribute_name)]) + + def _add_array_setter(self, attribute, new_setter): + new_setter.add_instructions( + ['this.{0} = {0}'.format(attribute['name'])]) + + def _add_setter(self, attribute): + attribute_name = attribute['name'] + return_type = get_generated_type(self.schema, attribute) + new_setter = JavaMethodGenerator('public', 'void', self._get_generated_setter_name( + attribute_name), [return_type + ' ' + attribute_name]) + self._add_method_condition(attribute, new_setter) + + setters = { + AttributeKind.SIMPLE: self._add_simple_setter, + AttributeKind.BUFFER: self._add_buffer_setter, + AttributeKind.ARRAY: self._add_array_setter, + AttributeKind.CUSTOM: self._add_simple_setter + } + + attribute_kind = get_attribute_kind(self.schema, attribute) + setters[attribute_kind](attribute, new_setter) + self._add_method(new_setter) + + def _add_getter_setter(self, attribute): + self._add_getter(attribute) + self._add_setter(attribute) + self._add_private_declaration(attribute) + + def _add_method(self, method): + self.class_output += [indent(line) + for line in method.get_method()] + [''] + + def _recurse_inlines(self, generate_attribute_method, attributes): + for attribute in attributes: + if 'disposition' in attribute: + if attribute['disposition'] == TypeDescriptorDisposition.Inline.value: + self._recurse_inlines( + generate_attribute_method, self.schema[attribute['type']]['layout']) + elif attribute['disposition'] == TypeDescriptorDisposition.Const.value: + pass + else: + generate_attribute_method( + attribute, get_attribute_if_size(attribute['name'], attributes), attributes) + + def _add_attribute_condition_if_needed(self, attribute, class_schema, method_writer, obj_prefix): + if 'condition' in attribute: + condition_type_attribute = get_attribute_where_property_equal(self.schema, class_schema, 'name', attribute['condition']) + condition_type_prefix = '' + if condition_type_attribute is not None: + condition_type_prefix = '{0}.'.format(condition_type_attribute['type']) + + method_writer.add_instructions(['if ({0}{1}() == {2}{3})'.format( + obj_prefix, self._get_generated_getter_name(attribute['condition']), condition_type_prefix, attribute['condition_value'].upper())], False) + return True + return False + + def _load_from_binary_simple(self, attribute, class_attributes): + indent_required = self._add_attribute_condition_if_needed(attribute, class_attributes, self.load_from_binary_method, 'obj.') + line = 'obj.{0}(stream.{1}())'.format(self._get_generated_setter_name( + attribute['name']), get_read_method_name(get_attribute_size(self.schema, attribute))) + self.load_from_binary_method.add_instructions([indent(line) if indent_required else line]) + + def _load_from_binary_buffer(self, attribute, clas_attributes): + attribute_name = attribute['name'] + attribute_size = get_attribute_size(self.schema, attribute) + self.load_from_binary_method.add_instructions( + ['obj.{0} = ByteBuffer.allocate({1})'.format(attribute_name, attribute_size)]) + self.load_from_binary_method.add_instructions([ + 'stream.{0}(obj.{1}.array())'.format( + get_read_method_name(attribute_size), attribute_name) + ]) + + def _load_from_binary_array(self, attribute, class_attributes): + attribute_typename = attribute['type'] + attribute_sizename = attribute['size'] + attribute_name = attribute['name'] + self.load_from_binary_method.add_instructions(['java.util.ArrayList<{1}> {0} = new java.util.ArrayList<{1}>({2})'.format( + attribute_name, attribute_typename, attribute_sizename)]) + self.load_from_binary_method.add_instructions([ + 'for (int i = 0; i < {0}; i++) {{'.format(attribute_sizename)], False) + + if attribute_typename == TypeDescriptorType.Byte.value: + self.load_from_binary_method.add_instructions([indent( + '{0}.add(stream.{1}())'.format(attribute_name, get_read_method_name(1)))]) + else: + self.load_from_binary_method.add_instructions([indent( + '{0}.add({1}.loadFromBinary(stream))'.format(attribute_name, attribute_typename))]) + self.load_from_binary_method.add_instructions(['}'], False) + self.load_from_binary_method.add_instructions(['obj.{0}({1})'.format( + self._get_generated_setter_name(attribute['name']), attribute_name)]) + + def _load_from_binary_custom(self, attribute, class_attributes): + self.load_from_binary_method.add_instructions([ + 'obj.{0}({1}.loadFromBinary(stream))'.format( + self._get_generated_setter_name(attribute['name']), attribute['type']) + ]) + + def _generate_load_from_binary_attributes(self, attribute, sizeof_attribute_name, class_attributes): + attribute_name = attribute['name'] + if sizeof_attribute_name is not None: + self.load_from_binary_method.add_instructions([ + '{0} {1} = stream.{2}()'.format(get_generated_type(self.schema, attribute), + attribute['name'], get_read_method_name(attribute['size'])) + ]) + else: + load_attribute = { + AttributeKind.SIMPLE: self._load_from_binary_simple, + AttributeKind.BUFFER: self._load_from_binary_buffer, + AttributeKind.ARRAY: self._load_from_binary_array, + AttributeKind.CUSTOM: self._load_from_binary_custom + } + + attribute_kind = get_attribute_kind(self.schema, attribute) + load_attribute[attribute_kind](attribute, class_attributes) + + def _generate_load_from_binary_method(self, attributes): + self.load_from_binary_method = JavaMethodGenerator( + 'public', self.class_name, 'loadFromBinary', ['DataInput stream'], 'throws Exception', True) + self.load_from_binary_method.add_instructions( + ['{0} obj = new {0}()'.format(self.class_name)]) + self._recurse_inlines( + self._generate_load_from_binary_attributes, attributes) + self.load_from_binary_method.add_instructions(['return obj']) + self._add_method(self.load_from_binary_method) + + def _serialize_attribute_simple(self, attribute, class_attributes): + indent_required = self._add_attribute_condition_if_needed(attribute, class_attributes, self.serialize_method, 'this.') + line = 'stream.{0}(this.{1}())'.format(get_write_method_name(get_attribute_size(self.schema, attribute)), + self._get_generated_getter_name(attribute['name'])) + self.serialize_method.add_instructions([indent(line) if indent_required else line]) + + def _serialize_attribute_buffer(self, attribute, clas_attributes): + attribute_name = attribute['name'] + attribute_size = get_attribute_size(self.schema, attribute) + self.serialize_method.add_instructions([ + 'stream.{0}(this.{1}.array(), 0, this.{1}.array().length)'.format( + get_write_method_name(attribute_size), attribute_name) + ]) + + def _serialize_attribute_array(self, attribute, class_attributes): + attribute_typename = attribute['type'] + attribute_size = attribute['size'] + attribute_name = attribute['name'] + self.serialize_method.add_instructions([ + 'for (int i = 0; i < this.{0}.size(); i++) {{'.format(attribute_name) + ], False) + + if attribute_typename == TypeDescriptorType.Byte.value: + self.serialize_method.add_instructions([indent( + 'stream.{0}(this.{1}.get(i))'.format(get_write_method_name(1), attribute_name))]) + else: + self.serialize_method.add_instructions([indent( + 'byte[] ser = this.{0}.get(i).serialize()'.format(attribute_name))]) + self.serialize_method.add_instructions([indent( + 'stream.{0}(ser, 0, ser.length)'.format(get_write_method_name(attribute_size)))]) + self.serialize_method.add_instructions(['}'], False) + + def _serialize_attribute_custom(self, attribute, class_attributes): + self.serialize_method.add_instructions([ + 'byte[] {0} = this.{1}().serialize()'.format(attribute['name'], + self._get_generated_getter_name(attribute['name'])) + ]) + self.serialize_method.add_instructions([ + 'stream.write({0}, 0, {0}.length)'.format(attribute['name']) + ]) + + def _generate_serialize_attributes(self, attribute, sizeof_attribute_name, class_attributes): + attribute_name = attribute['name'] + if sizeof_attribute_name is not None: + line = 'stream.{0}(this.{1}'.format( + get_write_method_name(get_attribute_size(self.schema, attribute)), sizeof_attribute_name) + if attribute_name.endswith('Count'): + line += '.size())' + else: + line += '.array().length)' + self.serialize_method.add_instructions([line]) + else: + serialize_attribute = { + AttributeKind.SIMPLE: self._serialize_attribute_simple, + AttributeKind.BUFFER: self._serialize_attribute_buffer, + AttributeKind.ARRAY: self._serialize_attribute_array, + AttributeKind.CUSTOM: self._serialize_attribute_custom + } + + attribute_kind = get_attribute_kind(self.schema, attribute) + serialize_attribute[attribute_kind](attribute, class_attributes) + + def _generate_serialize_method(self, attributes): + self.serialize_method = JavaMethodGenerator( + 'public', 'byte[]', 'serialize', [], 'throws Exception') + self.serialize_method.add_instructions( + ['ByteArrayOutputStream bos = new ByteArrayOutputStream()']) + self.serialize_method.add_instructions( + ['DataOutputStream stream = new DataOutputStream(bos)']) + self._recurse_inlines(self._generate_serialize_attributes, attributes) + self.serialize_method.add_instructions(['stream.close()']) + self.serialize_method.add_instructions(['return bos.toByteArray()']) + self._add_method(self.serialize_method) + + def _generate_attributes(self, attribute, sizeof_attribute_name, class_attributes): + if sizeof_attribute_name is None: + self._add_getter_setter(attribute) + + def _generate_getter_setter(self, class_layout): + self._recurse_inlines(self._generate_attributes, class_layout) + + def generate(self, class_schema): + class_layout = class_schema['layout'] + self._generate_getter_setter(class_layout) + self._generate_load_from_binary_method(class_layout) + self._generate_serialize_method(class_layout) + self._set_declarations() + return self.class_output + ['}'] diff --git a/generators/java/JavaEnumGenerator.py b/generators/java/JavaEnumGenerator.py new file mode 100755 index 00000000..c23fb38c --- /dev/null +++ b/generators/java/JavaEnumGenerator.py @@ -0,0 +1,78 @@ +import os +from .Helpers import * +from .JavaMethodGenerator import JavaMethodGenerator + +class JavaEnumGenerator: + def __init__(self, name, schema): + self.enum_name = name + self.enum_output = ['public enum {0} {{'.format(self.enum_name)] + self.schema = schema + self.privates = [] + + def _get_type(self, attribute): + return get_builtin_type(attribute['size']) + + def _add_private_declaration(self, attribute): + var_type = self._get_type(attribute) + self.enum_output += [indent('private final {0} value;'.format(var_type))] + [''] + + def _add_enum_values(self, enum_attribute): + enum_attribute_values = enum_attribute['values'] + enum_type = self._get_type(enum_attribute) + num_items = len(enum_attribute_values) + for attribute in enum_attribute_values: + attribute_name = attribute['name'] + line = '{0}(({1}){2})'.format(attribute_name.upper(), enum_type, attribute['value']) + line += ',' if attribute_name != enum_attribute_values[num_items - 1]['name'] else ';' + self.enum_output += [indent(line)] + self.enum_output += [''] + + def _add_constructor(self, attribute): + enum_type = self._get_type(attribute) + constructor_method = JavaMethodGenerator('private', '', self.enum_name, [ + '{0} value'.format(enum_type)]) + constructor_method.add_instructions(['this.value = value']) + self.add_method(constructor_method) + + def _add_load_from_binary_method(self, attribute): + load_from_binary_method = JavaMethodGenerator( + 'public', self.enum_name, 'loadFromBinary', ['DataInput stream'], 'throws Exception', True) + load_from_binary_method.add_instructions( + ['{0} val = stream.{1}()'.format(self._get_type(attribute), get_read_method_name(attribute['size']))]) + load_from_binary_method.add_instructions( + ['for ({0} current : {0}.values()) {{'.format(self.enum_name)], False) + load_from_binary_method.add_instructions( + [indent('if (val == current.value)')], False) + load_from_binary_method.add_instructions( + [indent('return current', 2)]) + load_from_binary_method.add_instructions( + ['}'], False) + load_from_binary_method.add_instructions( + ['throw new RuntimeException(val + " was not a backing value for {0}.")'.format(self.enum_name)]) + self.add_method(load_from_binary_method) + + def _add_serialize_method(self, attribute): + serialize_method = JavaMethodGenerator( + 'public', 'byte[]', 'serialize', [], 'throws Exception') + serialize_method.add_instructions( + ['ByteArrayOutputStream bos = new ByteArrayOutputStream()']) + serialize_method.add_instructions( + ['DataOutputStream stream = new DataOutputStream(bos)']) + serialize_method.add_instructions([ + 'stream.{0}(this.value)'.format(get_write_method_name(attribute['size'])) + ]) + serialize_method.add_instructions(['stream.close()']) + serialize_method.add_instructions(['return bos.toByteArray()']) + self.add_method(serialize_method) + + def add_method(self, method): + self.enum_output += [indent(line) + for line in method.get_method()] + [''] + + def generate(self, attribute): + self._add_enum_values(attribute) + self._add_private_declaration(attribute) + self._add_constructor(attribute) + self._add_load_from_binary_method(attribute) + self._add_serialize_method(attribute) + return self.enum_output + ['}'] \ No newline at end of file diff --git a/generators/java/JavaFileGenerator.py b/generators/java/JavaFileGenerator.py new file mode 100755 index 00000000..dd8fc652 --- /dev/null +++ b/generators/java/JavaFileGenerator.py @@ -0,0 +1,62 @@ +import os +from enum import Enum +from generators.Descriptor import Descriptor +from .Helpers import * +from .JavaMethodGenerator import JavaMethodGenerator +from .JavaEnumGenerator import JavaEnumGenerator +from .JavaClassGenerator import JavaClassGenerator + +class JavaFileGenerator: + def __init__(self, schema, options): + self.schema = schema + self.current = None + self.options = options + self.code = [] + + def __iter__(self): + self.current = self.generate() + return self + + def __next__(self): + self.code = [] + code, name = next(self.current) + return Descriptor(name + '.java', code) + + def prepend_copyright(self, copyright_file): + if os.path.isfile(copyright_file): + with open(copyright_file) as header: + self.code = [line.strip() for line in header] + [''] + + def set_import(self): + self.code += ['import java.lang.*;'] + self.code += ['import java.io.*;'] + self.code += ['import java.nio.*;'] + self.code += ['import catapult.builders.*;'] + [''] + + def set_package(self): + self.code += ['package catapult.builders;'] + [''] + + def generate(self): + + for type_descriptor, value in self.schema.items(): + self.code = [] + self.prepend_copyright(self.options['copyright']) + self.set_package() + self.set_import() + attribute_type = value['type'] + + if attribute_type == TypeDescriptorType.Byte.value: + # Typeless environment, values will be directly assigned + pass + elif attribute_type == TypeDescriptorType.Enum.value: + new_enum = JavaEnumGenerator(type_descriptor, self.schema) + self.code += new_enum.generate(value) + yield self.code, type_descriptor + elif attribute_type == TypeDescriptorType.Struct.value: + if type_descriptor.endswith('Body'): # skip all the inline classes + continue + new_class = JavaClassGenerator(type_descriptor, self.schema, value['layout']) + self.code += new_class.generate(value) + yield self.code, JavaClassGenerator.get_generated_class_name(type_descriptor) + + return diff --git a/generators/java/JavaMethodGenerator.py b/generators/java/JavaMethodGenerator.py new file mode 100755 index 00000000..55b75880 --- /dev/null +++ b/generators/java/JavaMethodGenerator.py @@ -0,0 +1,21 @@ +import os +from .Helpers import indent + +class JavaMethodGenerator: + def __init__(self, scope, return_type, name, params, exception_list = '', static = False): + self.name = name + if static: + self.method_output = ['{0} static {1} {2}({3}) {4} {{'.format( + scope, return_type, self.name, ', '.join(params), exception_list)] + else: + self.method_output = ['{0} {1} {2}({3}) {4} {{'.format( + scope, return_type, self.name, ', '.join(params), exception_list)] + + def add_instructions(self, instructions, add_semicolon=True): + for instruction in instructions: + if add_semicolon: + instruction += ';' + self.method_output.append(indent(instruction)) + + def get_method(self): + return self.method_output + ['}'] \ No newline at end of file From 5f5155be390d24c8364979841b24c036e23f1d72 Mon Sep 17 00:00:00 2001 From: TB Dev Date: Wed, 27 Mar 2019 11:08:29 -0400 Subject: [PATCH 02/11] Add Little Endian serialization and append Builder to all generated types --- generators/java/Helpers.py | 36 ++++++++-- generators/java/JavaClassGenerator.py | 54 ++++++++------ generators/java/JavaEnumGenerator.py | 49 ++++++++----- generators/java/JavaFileGenerator.py | 28 +++++--- .../java/Test/JavaGeneratorSerializeTest.java | 72 +++++++++++++++++++ 5 files changed, 189 insertions(+), 50 deletions(-) create mode 100644 generators/java/Test/JavaGeneratorSerializeTest.java diff --git a/generators/java/Helpers.py b/generators/java/Helpers.py index d404ba3c..379072f0 100755 --- a/generators/java/Helpers.py +++ b/generators/java/Helpers.py @@ -6,11 +6,21 @@ class TypeDescriptorType(Enum): Struct = 'struct' Enum = 'enum' +def is_struct_type(typename): + return typename == TypeDescriptorType.Struct.value + +def is_enum_type(typename): + return typename == TypeDescriptorType.Enum.value + +def is_byte_type(typename): + return typename == TypeDescriptorType.Byte.value + +def get_generated_class_name(typename): + return typename + "Builder" def is_builtin_type(typename, size): # byte up to long are passed as 'byte' with size set to proper value - return not isinstance(size, str) and TypeDescriptorType.Byte.value == typename and size <= 8 - + return not isinstance(size, str) and is_byte_type(typename) and size <= 8 class AttributeKind(Enum): SIMPLE = 1 @@ -21,7 +31,7 @@ class AttributeKind(Enum): def get_attribute_size(schema, attribute): - if 'size' not in attribute and attribute['type'] != TypeDescriptorType.Byte.value and attribute['type'] != TypeDescriptorType.Enum.value: + if 'size' not in attribute and not is_byte_type(attribute['type']) and not is_enum_type(attribute['type']): attr = schema[attribute['type']] if 'size' in attr: return attr['size'] @@ -32,7 +42,7 @@ def get_attribute_size(schema, attribute): def get_attribute_kind(schema, attribute): attribute_type = attribute['type'] - if attribute_type == TypeDescriptorType.Struct.value or attribute_type == TypeDescriptorType.Enum.value: + if is_struct_type(attribute_type) or is_enum_type(attribute_type): return AttributeKind.CUSTOM if 'size' not in attribute: type_descriptor = schema[attribute_type] @@ -97,12 +107,25 @@ def get_read_method_name(size): return method_name +def get_reverse_method_name_if_needed(size): + if isinstance(size, str) or size > 8 or size == 1: + method_name = '{0}' + else: + typesize_methodname = {2: 'Short.reverseBytes({0})', + 4: 'Integer.reverseBytes({0})', + 8: 'Long.reverseBytes({0})'} + method_name = typesize_methodname[size] + return method_name + + def get_write_method_name(size): if isinstance(size, str) or size > 8: method_name = 'write' else: typesize_methodname = {1: 'writeByte', - 2: 'writeShort', 4: 'writeInt', 8: 'writeLong'} + 2: 'writeShort', + 4: 'writeInt', + 8: 'writeLong'} method_name = typesize_methodname[size] return method_name @@ -110,6 +133,9 @@ def get_write_method_name(size): def get_generated_type(schema, attribute): typename = attribute['type'] attribute_kind = get_attribute_kind(schema, attribute) + if not is_byte_type(typename): + typename = get_generated_class_name(typename) + if attribute_kind == AttributeKind.SIMPLE: return get_builtin_type(get_attribute_size(schema, attribute)) elif attribute_kind == AttributeKind.BUFFER: diff --git a/generators/java/JavaClassGenerator.py b/generators/java/JavaClassGenerator.py index d744a878..9e0e26ff 100644 --- a/generators/java/JavaClassGenerator.py +++ b/generators/java/JavaClassGenerator.py @@ -6,7 +6,7 @@ class JavaClassGenerator: @staticmethod def get_generated_class_name(name): - return '{}'.format(name) + return get_generated_class_name(name) def _get_generated_getter_name(self, attribute): return 'get{}'.format(attribute.capitalize()) @@ -14,7 +14,7 @@ def _get_generated_getter_name(self, attribute): def _get_generated_setter_name(self, attribute_name): return 'set{}'.format(attribute_name.capitalize()) - def __init__(self, name, schema, class_schema): + def __init__(self, name, schema, class_schema, enum_list): self.class_name = JavaClassGenerator.get_generated_class_name(name) self.class_output = ['public class {0} {{'.format(self.class_name)] self.load_from_binary_method = None @@ -22,6 +22,7 @@ def __init__(self, name, schema, class_schema): self.schema = schema self.class_schema = class_schema self.privates = [] + self.enum_list = enum_list def _set_declarations(self): self.class_output += [indent(line) for line in self.privates] + [''] @@ -49,7 +50,7 @@ def _add_method_condition(self, attribute, method_writer): condition_type_attribute = get_attribute_where_property_equal(self.schema, self.class_schema, 'name', attribute['condition']) condition_type_prefix = '' if condition_type_attribute is not None: - condition_type_prefix = '{0}.'.format(condition_type_attribute['type']) + condition_type_prefix = '{0}.'.format(get_generated_class_name(condition_type_attribute['type'])) method_writer.add_instructions(['if ({0} != {1}{2})'.format( attribute['condition'], condition_type_prefix, attribute['condition_value'].upper())], False) @@ -133,6 +134,10 @@ def _recurse_inlines(self, generate_attribute_method, attributes): self._recurse_inlines( generate_attribute_method, self.schema[attribute['type']]['layout']) elif attribute['disposition'] == TypeDescriptorDisposition.Const.value: + # add dymanic enum if present in this class + enum_name = attribute['type'] + if enum_name in self.enum_list: + self.enum_list[enum_name].add_enum_value(self.class_name, attribute['value']) pass else: generate_attribute_method( @@ -143,7 +148,7 @@ def _add_attribute_condition_if_needed(self, attribute, class_schema, method_wri condition_type_attribute = get_attribute_where_property_equal(self.schema, class_schema, 'name', attribute['condition']) condition_type_prefix = '' if condition_type_attribute is not None: - condition_type_prefix = '{0}.'.format(condition_type_attribute['type']) + condition_type_prefix = '{0}.'.format(get_generated_class_name(condition_type_attribute['type'])) method_writer.add_instructions(['if ({0}{1}() == {2}{3})'.format( obj_prefix, self._get_generated_getter_name(attribute['condition']), condition_type_prefix, attribute['condition_value'].upper())], False) @@ -152,8 +157,11 @@ def _add_attribute_condition_if_needed(self, attribute, class_schema, method_wri def _load_from_binary_simple(self, attribute, class_attributes): indent_required = self._add_attribute_condition_if_needed(attribute, class_attributes, self.load_from_binary_method, 'obj.') - line = 'obj.{0}(stream.{1}())'.format(self._get_generated_setter_name( - attribute['name']), get_read_method_name(get_attribute_size(self.schema, attribute))) + size = get_attribute_size(self.schema, attribute) + read_method_name = 'stream.{0}()'.format(get_read_method_name(size)) + reverse_byte_method = get_reverse_method_name_if_needed(size).format(read_method_name) + line = 'obj.{0}({1})'.format(self._get_generated_setter_name( + attribute['name']), reverse_byte_method) self.load_from_binary_method.add_instructions([indent(line) if indent_required else line]) def _load_from_binary_buffer(self, attribute, clas_attributes): @@ -171,16 +179,16 @@ def _load_from_binary_array(self, attribute, class_attributes): attribute_sizename = attribute['size'] attribute_name = attribute['name'] self.load_from_binary_method.add_instructions(['java.util.ArrayList<{1}> {0} = new java.util.ArrayList<{1}>({2})'.format( - attribute_name, attribute_typename, attribute_sizename)]) + attribute_name, get_generated_class_name(attribute_typename), attribute_sizename)]) self.load_from_binary_method.add_instructions([ 'for (int i = 0; i < {0}; i++) {{'.format(attribute_sizename)], False) - if attribute_typename == TypeDescriptorType.Byte.value: + if is_byte_type(attribute_typename): self.load_from_binary_method.add_instructions([indent( '{0}.add(stream.{1}())'.format(attribute_name, get_read_method_name(1)))]) else: self.load_from_binary_method.add_instructions([indent( - '{0}.add({1}.loadFromBinary(stream))'.format(attribute_name, attribute_typename))]) + '{0}.add({1}.loadFromBinary(stream))'.format(attribute_name, get_generated_class_name(attribute_typename)))]) self.load_from_binary_method.add_instructions(['}'], False) self.load_from_binary_method.add_instructions(['obj.{0}({1})'.format( self._get_generated_setter_name(attribute['name']), attribute_name)]) @@ -188,15 +196,18 @@ def _load_from_binary_array(self, attribute, class_attributes): def _load_from_binary_custom(self, attribute, class_attributes): self.load_from_binary_method.add_instructions([ 'obj.{0}({1}.loadFromBinary(stream))'.format( - self._get_generated_setter_name(attribute['name']), attribute['type']) + self._get_generated_setter_name(attribute['name']), get_generated_class_name(attribute['type'])) ]) def _generate_load_from_binary_attributes(self, attribute, sizeof_attribute_name, class_attributes): attribute_name = attribute['name'] if sizeof_attribute_name is not None: + read_method_name = 'stream.{0}()'.format(get_read_method_name(attribute['size'])) + size = get_attribute_size(self.schema, attribute) + reverse_byte_method = get_reverse_method_name_if_needed(size).format(read_method_name) self.load_from_binary_method.add_instructions([ - '{0} {1} = stream.{2}()'.format(get_generated_type(self.schema, attribute), - attribute['name'], get_read_method_name(attribute['size'])) + '{0} {1} = {2}'.format(get_generated_type(self.schema, attribute), + attribute_name, reverse_byte_method) ]) else: load_attribute = { @@ -221,8 +232,9 @@ def _generate_load_from_binary_method(self, attributes): def _serialize_attribute_simple(self, attribute, class_attributes): indent_required = self._add_attribute_condition_if_needed(attribute, class_attributes, self.serialize_method, 'this.') - line = 'stream.{0}(this.{1}())'.format(get_write_method_name(get_attribute_size(self.schema, attribute)), - self._get_generated_getter_name(attribute['name'])) + size = get_attribute_size(self.schema, attribute) + reverse_byte_method = get_reverse_method_name_if_needed(size).format('this.' + self._get_generated_getter_name(attribute['name'] + '()')) + line = 'stream.{0}({1})'.format(get_write_method_name(size), reverse_byte_method) self.serialize_method.add_instructions([indent(line) if indent_required else line]) def _serialize_attribute_buffer(self, attribute, clas_attributes): @@ -241,7 +253,7 @@ def _serialize_attribute_array(self, attribute, class_attributes): 'for (int i = 0; i < this.{0}.size(); i++) {{'.format(attribute_name) ], False) - if attribute_typename == TypeDescriptorType.Byte.value: + if is_byte_type(attribute_typename): self.serialize_method.add_instructions([indent( 'stream.{0}(this.{1}.get(i))'.format(get_write_method_name(1), attribute_name))]) else: @@ -263,12 +275,12 @@ def _serialize_attribute_custom(self, attribute, class_attributes): def _generate_serialize_attributes(self, attribute, sizeof_attribute_name, class_attributes): attribute_name = attribute['name'] if sizeof_attribute_name is not None: - line = 'stream.{0}(this.{1}'.format( - get_write_method_name(get_attribute_size(self.schema, attribute)), sizeof_attribute_name) - if attribute_name.endswith('Count'): - line += '.size())' - else: - line += '.array().length)' + size = get_attribute_size(self.schema, attribute) + size_extension = '.size()' if attribute_name.endswith('Count') else '.array().length' + full_property_name = '({0}){1}'.format(get_builtin_type(size), 'this.' + sizeof_attribute_name + size_extension) + reverse_byte_method = get_reverse_method_name_if_needed(size).format(full_property_name) + line = 'stream.{0}({1})'.format( + get_write_method_name(size), reverse_byte_method) self.serialize_method.add_instructions([line]) else: serialize_attribute = { diff --git a/generators/java/JavaEnumGenerator.py b/generators/java/JavaEnumGenerator.py index c23fb38c..dc88e510 100755 --- a/generators/java/JavaEnumGenerator.py +++ b/generators/java/JavaEnumGenerator.py @@ -1,13 +1,16 @@ -import os from .Helpers import * from .JavaMethodGenerator import JavaMethodGenerator class JavaEnumGenerator: - def __init__(self, name, schema): - self.enum_name = name + def __init__(self, name, schema, attribute): + self.enum_name = get_generated_class_name(name) self.enum_output = ['public enum {0} {{'.format(self.enum_name)] self.schema = schema self.privates = [] + self.attribute = attribute + self.enum_values = {} + + self._add_enum_values(self.attribute) def _get_type(self, attribute): return get_builtin_type(attribute['size']) @@ -18,13 +21,18 @@ def _add_private_declaration(self, attribute): def _add_enum_values(self, enum_attribute): enum_attribute_values = enum_attribute['values'] - enum_type = self._get_type(enum_attribute) - num_items = len(enum_attribute_values) - for attribute in enum_attribute_values: - attribute_name = attribute['name'] - line = '{0}(({1}){2})'.format(attribute_name.upper(), enum_type, attribute['value']) - line += ',' if attribute_name != enum_attribute_values[num_items - 1]['name'] else ';' + for current_attribute in enum_attribute_values: + self.add_enum_value(current_attribute['name'], current_attribute['value']) + + def _write_enum_values(self): + enum_type = self._get_type(self.attribute) + enum_length = len(self.enum_values) + count = 1 + for name, value in self.enum_values.items(): + line = '{0}(({1}){2})'.format(name.upper(), enum_type, value) + line += ',' if count < enum_length else ';' self.enum_output += [indent(line)] + count += 1 self.enum_output += [''] def _add_constructor(self, attribute): @@ -39,6 +47,10 @@ def _add_load_from_binary_method(self, attribute): 'public', self.enum_name, 'loadFromBinary', ['DataInput stream'], 'throws Exception', True) load_from_binary_method.add_instructions( ['{0} val = stream.{1}()'.format(self._get_type(attribute), get_read_method_name(attribute['size']))]) + size = get_attribute_size(self.schema, attribute) + reverse_byte_method = get_reverse_method_name_if_needed(size).format('val') + load_from_binary_method.add_instructions( + ['val = {0}'.format(reverse_byte_method)]) load_from_binary_method.add_instructions( ['for ({0} current : {0}.values()) {{'.format(self.enum_name)], False) load_from_binary_method.add_instructions( @@ -58,8 +70,10 @@ def _add_serialize_method(self, attribute): ['ByteArrayOutputStream bos = new ByteArrayOutputStream()']) serialize_method.add_instructions( ['DataOutputStream stream = new DataOutputStream(bos)']) + size = get_attribute_size(self.schema, attribute) + reverse_byte_method = get_reverse_method_name_if_needed(size).format('this.value') serialize_method.add_instructions([ - 'stream.{0}(this.value)'.format(get_write_method_name(attribute['size'])) + 'stream.{0}({1})'.format(get_write_method_name(size), reverse_byte_method) ]) serialize_method.add_instructions(['stream.close()']) serialize_method.add_instructions(['return bos.toByteArray()']) @@ -69,10 +83,13 @@ def add_method(self, method): self.enum_output += [indent(line) for line in method.get_method()] + [''] - def generate(self, attribute): - self._add_enum_values(attribute) - self._add_private_declaration(attribute) - self._add_constructor(attribute) - self._add_load_from_binary_method(attribute) - self._add_serialize_method(attribute) + def add_enum_value(self, name, value): + self.enum_values[name] = value + + def generate(self): + self._write_enum_values() + self._add_private_declaration(self.attribute) + self._add_constructor(self.attribute) + self._add_load_from_binary_method(self.attribute) + self._add_serialize_method(self.attribute) return self.enum_output + ['}'] \ No newline at end of file diff --git a/generators/java/JavaFileGenerator.py b/generators/java/JavaFileGenerator.py index dd8fc652..bd7b3777 100755 --- a/generators/java/JavaFileGenerator.py +++ b/generators/java/JavaFileGenerator.py @@ -7,6 +7,8 @@ from .JavaClassGenerator import JavaClassGenerator class JavaFileGenerator: + enum_class_list = {} + def __init__(self, schema, options): self.schema = schema self.current = None @@ -45,18 +47,28 @@ def generate(self): self.set_import() attribute_type = value['type'] - if attribute_type == TypeDescriptorType.Byte.value: + if is_byte_type(attribute_type): # Typeless environment, values will be directly assigned pass - elif attribute_type == TypeDescriptorType.Enum.value: - new_enum = JavaEnumGenerator(type_descriptor, self.schema) - self.code += new_enum.generate(value) - yield self.code, type_descriptor - elif attribute_type == TypeDescriptorType.Struct.value: + elif is_enum_type(attribute_type): + JavaFileGenerator.enum_class_list[type_descriptor] = JavaEnumGenerator(type_descriptor, self.schema, value) + #self.code += new_enum.generate(value) + #yield self.code, type_descriptor + pass + elif is_struct_type(attribute_type): if type_descriptor.endswith('Body'): # skip all the inline classes continue - new_class = JavaClassGenerator(type_descriptor, self.schema, value['layout']) + new_class = JavaClassGenerator(type_descriptor, self.schema, value['layout'], JavaFileGenerator.enum_class_list) self.code += new_class.generate(value) - yield self.code, JavaClassGenerator.get_generated_class_name(type_descriptor) + yield self.code, get_generated_class_name(type_descriptor) + + # write all the enum last just in case there are 'dymanic values' + for type_descriptor, enum_class in JavaFileGenerator.enum_class_list.items(): + self.code = [] + self.prepend_copyright(self.options['copyright']) + self.set_package() + self.set_import() + self.code += enum_class.generate() + yield self.code, get_generated_class_name(type_descriptor) return diff --git a/generators/java/Test/JavaGeneratorSerializeTest.java b/generators/java/Test/JavaGeneratorSerializeTest.java new file mode 100644 index 00000000..4d0df65a --- /dev/null +++ b/generators/java/Test/JavaGeneratorSerializeTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2019 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.*; +import java.io.*; +import java.nio.*; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import catapult.builders.*; +import jdk.jfr.Timestamp; + +public class JavaGeneratorSerializeTest { + + @Test + public static void testMosaicProperty() + { + MosaicPropertyBuilder test = new MosaicPropertyBuilder(); + test.setId(MosaicPropertyIdBuilder.DURATION); + test.setValue(5); + byte[] ser = test.serialize(); + ByteArrayInputStream bs = new ByteArrayInputStream(ser); + DataInput di = new DataInputStream(bs); + MosaicPropertyBuilder test2 = MosaicPropertyBuilder.loadFromBinary(di); + assertEquals(test.getId(), test2.getId()); + assertEquals(test.getValue(), test2.getValue()); + } + + @Test + public static void testTransferTx() + { + TransferTransactionBuilder test = new TransferTransactionBuilder(); + test.setSize(5); + java.nio.ByteBuffer bb = java.nio.ByteBuffer.allocate(64); + bb.put(new byte[64]); + test.setSignature(bb); + bb = java.nio.ByteBuffer.allocate(32); + bb.put(new byte[32]); + test.setSigner(bb); + test.setVersion((short)2); + test.setType(EntityTypeBuilder.RESERVED); + test.setFee(10); + test.setDeadline(100); + ByteBuffer bb1 = java.nio.ByteBuffer.allocate(25); + bb1.put(new byte[25]); + test.setRecipient(bb1); + ByteBuffer bb2 = java.nio.ByteBuffer.allocate(30); + bb2.put(new byte[30]); + test.setMessage(bb2); + java.util.ArrayList mosaics = new java.util.ArrayList(5); + mosaics.add(new UnresolvedMosaicBuilder()); + test.setMosaics(mosaics); + byte[] ser = test.serialize(); + ByteArrayInputStream bs = new ByteArrayInputStream(ser); + DataInput di = new DataInputStream(bs); + TransferTransactionBuilder test2 = TransferTransactionBuilder.loadFromBinary(di); + assertEquals(test.getSize(), test2.getSize()); + } +} From 44acd251222f1d4d6570bb25f1a4f34ae24f6ad3 Mon Sep 17 00:00:00 2001 From: TB Dev Date: Thu, 4 Apr 2019 15:16:53 -0400 Subject: [PATCH 03/11] Fix pylint errors --- generators/java/Helpers.py | 44 +++--- generators/java/JavaClassGenerator.py | 182 +++++++++++++++---------- generators/java/JavaEnumGenerator.py | 48 ++++--- generators/java/JavaFileGenerator.py | 21 +-- generators/java/JavaMethodGenerator.py | 9 +- 5 files changed, 187 insertions(+), 117 deletions(-) diff --git a/generators/java/Helpers.py b/generators/java/Helpers.py index 379072f0..4b3e1fa5 100755 --- a/generators/java/Helpers.py +++ b/generators/java/Helpers.py @@ -1,28 +1,36 @@ -import os from enum import Enum + class TypeDescriptorType(Enum): + """Type descriptor enum""" Byte = 'byte' Struct = 'struct' Enum = 'enum' + def is_struct_type(typename): return typename == TypeDescriptorType.Struct.value + def is_enum_type(typename): return typename == TypeDescriptorType.Enum.value + def is_byte_type(typename): return typename == TypeDescriptorType.Byte.value + def get_generated_class_name(typename): - return typename + "Builder" + return typename + 'Builder' + def is_builtin_type(typename, size): # byte up to long are passed as 'byte' with size set to proper value return not isinstance(size, str) and is_byte_type(typename) and size <= 8 + class AttributeKind(Enum): + """Attribute type enum""" SIMPLE = 1 BUFFER = 2 ARRAY = 3 @@ -31,12 +39,13 @@ class AttributeKind(Enum): def get_attribute_size(schema, attribute): - if 'size' not in attribute and not is_byte_type(attribute['type']) and not is_enum_type(attribute['type']): + if ('size' not in attribute and not is_byte_type(attribute['type']) + and not is_enum_type(attribute['type'])): attr = schema[attribute['type']] if 'size' in attr: return attr['size'] - else: - return 1 + return 1 + return attribute['size'] @@ -53,7 +62,7 @@ def get_attribute_kind(schema, attribute): if isinstance(attribute_size, str): if attribute_size.endswith('Size'): return AttributeKind.BUFFER - + if attribute_size.endswith('Count'): return AttributeKind.ARRAY @@ -80,17 +89,20 @@ def get_attribute_if_size(attribute_name, attributes): return None -def get_attribute_where_property_equal(schema, attributes, attribute_name, attribute_value): +def get_attribute_property_equal(schema, attributes, attribute_name, attribute_value): for attribute in attributes: if attribute_name in attribute and attribute[attribute_name] == attribute_value: return attribute - if 'disposition' in attribute and attribute['disposition'] == TypeDescriptorDisposition.Inline.value: - value = get_attribute_where_property_equal(schema, schema[attribute['type']]['layout'], attribute_name, attribute_value) + if ('disposition' in attribute and + attribute['disposition'] == TypeDescriptorDisposition.Inline.value): + value = get_attribute_property_equal( + schema, schema[attribute['type']]['layout'], attribute_name, attribute_value) if value is not None: return value return None + def get_builtin_type(size): builtin_types = {1: 'byte', 2: 'short', 4: 'int', 8: 'long'} builtin_type = builtin_types[size] @@ -107,12 +119,12 @@ def get_read_method_name(size): return method_name -def get_reverse_method_name_if_needed(size): +def get_reverse_method_name(size): if isinstance(size, str) or size > 8 or size == 1: method_name = '{0}' else: - typesize_methodname = {2: 'Short.reverseBytes({0})', - 4: 'Integer.reverseBytes({0})', + typesize_methodname = {2: 'Short.reverseBytes({0})', + 4: 'Integer.reverseBytes({0})', 8: 'Long.reverseBytes({0})'} method_name = typesize_methodname[size] return method_name @@ -123,8 +135,8 @@ def get_write_method_name(size): method_name = 'write' else: typesize_methodname = {1: 'writeByte', - 2: 'writeShort', - 4: 'writeInt', + 2: 'writeShort', + 4: 'writeInt', 8: 'writeLong'} method_name = typesize_methodname[size] return method_name @@ -135,7 +147,7 @@ def get_generated_type(schema, attribute): attribute_kind = get_attribute_kind(schema, attribute) if not is_byte_type(typename): typename = get_generated_class_name(typename) - + if attribute_kind == AttributeKind.SIMPLE: return get_builtin_type(get_attribute_size(schema, attribute)) elif attribute_kind == AttributeKind.BUFFER: @@ -143,4 +155,4 @@ def get_generated_type(schema, attribute): elif attribute_kind == AttributeKind.ARRAY: return 'java.util.ArrayList<{0}>'.format(typename) - return typename \ No newline at end of file + return typename diff --git a/generators/java/JavaClassGenerator.py b/generators/java/JavaClassGenerator.py index 9e0e26ff..a04efbd6 100644 --- a/generators/java/JavaClassGenerator.py +++ b/generators/java/JavaClassGenerator.py @@ -1,19 +1,44 @@ -import os -from enum import Enum -from .Helpers import * +from .Helpers import get_generated_class_name, get_builtin_type, indent, get_attribute_size +from .Helpers import get_read_method_name, get_reverse_method_name, get_write_method_name +from .Helpers import get_generated_type, get_attribute_property_equal, AttributeKind, is_byte_type +from .Helpers import get_attribute_kind, TypeDescriptorDisposition, get_attribute_if_size from .JavaMethodGenerator import JavaMethodGenerator + +def get_generated_getter_name(attribute): + return 'get{}'.format(attribute.capitalize()) + + +def get_generated_setter_name(attribute_name): + return 'set{}'.format(attribute_name.capitalize()) + + +def add_simple_getter(attribute, new_getter): + new_getter.add_instructions( + ['return this.{0}'.format(attribute['name'])]) + + +def add_buffer_getter(attribute, new_getter): + new_getter.add_instructions( + ['return this.{0}'.format(attribute['name'])]) + + +def add_simple_setter(attribute, new_setter): + new_setter.add_instructions( + ['this.{0} = {0}'.format(attribute['name'])]) + +def add_array_setter(attribute, new_setter): + new_setter.add_instructions( + ['this.{0} = {0}'.format(attribute['name'])]) + + class JavaClassGenerator: + # pylint: disable=too-many-instance-attributes + """Java class generator""" @staticmethod def get_generated_class_name(name): return get_generated_class_name(name) - def _get_generated_getter_name(self, attribute): - return 'get{}'.format(attribute.capitalize()) - - def _get_generated_setter_name(self, attribute_name): - return 'set{}'.format(attribute_name.capitalize()) - def __init__(self, name, schema, class_schema, enum_list): self.class_name = JavaClassGenerator.get_generated_class_name(name) self.class_output = ['public class {0} {{'.format(self.class_name)] @@ -32,14 +57,6 @@ def _add_private_declaration(self, attribute): var_type = get_generated_type(self.schema, attribute) self.privates += ['private {0} {1};'.format(var_type, attribute_name)] - def _add_simple_getter(self, attribute, new_getter): - new_getter.add_instructions( - ['return this.{0}'.format(attribute['name'])]) - - def _add_buffer_getter(self, attribute, new_getter): - new_getter.add_instructions( - ['return this.{0}'.format(attribute['name'])]) - def _add_array_getter(self, attribute, new_getter): return_type = get_generated_type(self.schema, attribute) new_getter.add_instructions( @@ -47,13 +64,16 @@ def _add_array_getter(self, attribute, new_getter): def _add_method_condition(self, attribute, method_writer): if 'condition' in attribute: - condition_type_attribute = get_attribute_where_property_equal(self.schema, self.class_schema, 'name', attribute['condition']) + condition_type_attribute = get_attribute_property_equal( + self.schema, self.class_schema, 'name', attribute['condition']) condition_type_prefix = '' if condition_type_attribute is not None: - condition_type_prefix = '{0}.'.format(get_generated_class_name(condition_type_attribute['type'])) + condition_type_prefix = '{0}.'.format( + get_generated_class_name(condition_type_attribute['type'])) method_writer.add_instructions(['if ({0} != {1}{2})'.format( - attribute['condition'], condition_type_prefix, attribute['condition_value'].upper())], False) + attribute['condition'], condition_type_prefix, + attribute['condition_value'].upper())], False) method_writer.add_instructions( [indent('throw new java.lang.IllegalStateException()')]) method_writer.add_instructions([''], False) @@ -62,23 +82,19 @@ def _add_getter(self, attribute): attribute_name = attribute['name'] return_type = get_generated_type(self.schema, attribute) new_getter = JavaMethodGenerator( - 'public', return_type, self._get_generated_getter_name(attribute_name), []) + 'public', return_type, get_generated_getter_name(attribute_name), []) self._add_method_condition(attribute, new_getter) getters = { - AttributeKind.SIMPLE: self._add_simple_getter, - AttributeKind.BUFFER: self._add_buffer_getter, + AttributeKind.SIMPLE: add_simple_getter, + AttributeKind.BUFFER: add_buffer_getter, AttributeKind.ARRAY: self._add_array_getter, - AttributeKind.CUSTOM: self._add_simple_getter + AttributeKind.CUSTOM: add_simple_getter } attribute_kind = get_attribute_kind(self.schema, attribute) getters[attribute_kind](attribute, new_getter) self._add_method(new_getter) - def _add_simple_setter(self, attribute, new_setter): - new_setter.add_instructions( - ['this.{0} = {0}'.format(attribute['name'])]) - def _add_buffer_setter(self, attribute, new_setter): attribute_size = get_attribute_size(self.schema, attribute) attribute_name = attribute['name'] @@ -91,27 +107,24 @@ def _add_buffer_setter(self, attribute, new_setter): new_setter.add_instructions( ['if ({0}.array().length != {1})'.format(attribute_name, attribute_size)], False) new_setter.add_instructions( - [indent('throw new IllegalArgumentException("{0} should be {1} bytes")'.format(attribute_name, attribute_size))]) + [indent('throw new IllegalArgumentException("{0} should be {1} bytes")' + .format(attribute_name, attribute_size))]) new_setter.add_instructions([''], False) new_setter.add_instructions( ['this.{0} = {0}'.format(attribute_name)]) - def _add_array_setter(self, attribute, new_setter): - new_setter.add_instructions( - ['this.{0} = {0}'.format(attribute['name'])]) - def _add_setter(self, attribute): attribute_name = attribute['name'] return_type = get_generated_type(self.schema, attribute) - new_setter = JavaMethodGenerator('public', 'void', self._get_generated_setter_name( + new_setter = JavaMethodGenerator('public', 'void', get_generated_setter_name( attribute_name), [return_type + ' ' + attribute_name]) self._add_method_condition(attribute, new_setter) setters = { - AttributeKind.SIMPLE: self._add_simple_setter, + AttributeKind.SIMPLE: add_simple_setter, AttributeKind.BUFFER: self._add_buffer_setter, - AttributeKind.ARRAY: self._add_array_setter, - AttributeKind.CUSTOM: self._add_simple_setter + AttributeKind.ARRAY: add_array_setter, + AttributeKind.CUSTOM: add_simple_setter } attribute_kind = get_attribute_kind(self.schema, attribute) @@ -126,7 +139,7 @@ def _add_getter_setter(self, attribute): def _add_method(self, method): self.class_output += [indent(line) for line in method.get_method()] + [''] - + def _recurse_inlines(self, generate_attribute_method, attributes): for attribute in attributes: if 'disposition' in attribute: @@ -137,34 +150,43 @@ def _recurse_inlines(self, generate_attribute_method, attributes): # add dymanic enum if present in this class enum_name = attribute['type'] if enum_name in self.enum_list: - self.enum_list[enum_name].add_enum_value(self.class_name, attribute['value']) - pass + self.enum_list[enum_name].add_enum_value( + self.class_name, attribute['value']) else: generate_attribute_method( attribute, get_attribute_if_size(attribute['name'], attributes), attributes) - def _add_attribute_condition_if_needed(self, attribute, class_schema, method_writer, obj_prefix): + def _add_attribute_condition_if_needed(self, attribute, class_schema, + method_writer, obj_prefix): if 'condition' in attribute: - condition_type_attribute = get_attribute_where_property_equal(self.schema, class_schema, 'name', attribute['condition']) + condition_type_attribute = get_attribute_property_equal( + self.schema, class_schema, 'name', attribute['condition']) condition_type_prefix = '' if condition_type_attribute is not None: - condition_type_prefix = '{0}.'.format(get_generated_class_name(condition_type_attribute['type'])) + condition_type_prefix = '{0}.'.format( + get_generated_class_name(condition_type_attribute['type'])) method_writer.add_instructions(['if ({0}{1}() == {2}{3})'.format( - obj_prefix, self._get_generated_getter_name(attribute['condition']), condition_type_prefix, attribute['condition_value'].upper())], False) + obj_prefix, get_generated_getter_name( + attribute['condition']), + condition_type_prefix, attribute['condition_value'].upper())], False) return True return False def _load_from_binary_simple(self, attribute, class_attributes): - indent_required = self._add_attribute_condition_if_needed(attribute, class_attributes, self.load_from_binary_method, 'obj.') + indent_required = self._add_attribute_condition_if_needed( + attribute, class_attributes, self.load_from_binary_method, 'obj.') size = get_attribute_size(self.schema, attribute) read_method_name = 'stream.{0}()'.format(get_read_method_name(size)) - reverse_byte_method = get_reverse_method_name_if_needed(size).format(read_method_name) - line = 'obj.{0}({1})'.format(self._get_generated_setter_name( - attribute['name']), reverse_byte_method) - self.load_from_binary_method.add_instructions([indent(line) if indent_required else line]) + reverse_byte_method = get_reverse_method_name( + size).format(read_method_name) + line = 'obj.{0}({1})'.format(get_generated_setter_name( + attribute['name']), reverse_byte_method) + self.load_from_binary_method.add_instructions( + [indent(line) if indent_required else line]) def _load_from_binary_buffer(self, attribute, clas_attributes): + # pylint: disable=unused-argument attribute_name = attribute['name'] attribute_size = get_attribute_size(self.schema, attribute) self.load_from_binary_method.add_instructions( @@ -175,11 +197,13 @@ def _load_from_binary_buffer(self, attribute, clas_attributes): ]) def _load_from_binary_array(self, attribute, class_attributes): + # pylint: disable=unused-argument attribute_typename = attribute['type'] attribute_sizename = attribute['size'] attribute_name = attribute['name'] - self.load_from_binary_method.add_instructions(['java.util.ArrayList<{1}> {0} = new java.util.ArrayList<{1}>({2})'.format( - attribute_name, get_generated_class_name(attribute_typename), attribute_sizename)]) + self.load_from_binary_method.add_instructions( + ['java.util.ArrayList<{1}> {0} = new java.util.ArrayList<{1}>({2})'.format( + attribute_name, get_generated_class_name(attribute_typename), attribute_sizename)]) self.load_from_binary_method.add_instructions([ 'for (int i = 0; i < {0}; i++) {{'.format(attribute_sizename)], False) @@ -188,26 +212,32 @@ def _load_from_binary_array(self, attribute, class_attributes): '{0}.add(stream.{1}())'.format(attribute_name, get_read_method_name(1)))]) else: self.load_from_binary_method.add_instructions([indent( - '{0}.add({1}.loadFromBinary(stream))'.format(attribute_name, get_generated_class_name(attribute_typename)))]) + '{0}.add({1}.loadFromBinary(stream))'.format( + attribute_name, get_generated_class_name(attribute_typename)))]) self.load_from_binary_method.add_instructions(['}'], False) self.load_from_binary_method.add_instructions(['obj.{0}({1})'.format( - self._get_generated_setter_name(attribute['name']), attribute_name)]) + get_generated_setter_name(attribute['name']), attribute_name)]) def _load_from_binary_custom(self, attribute, class_attributes): + # pylint: disable=unused-argument self.load_from_binary_method.add_instructions([ 'obj.{0}({1}.loadFromBinary(stream))'.format( - self._get_generated_setter_name(attribute['name']), get_generated_class_name(attribute['type'])) + get_generated_setter_name(attribute['name']), + get_generated_class_name(attribute['type'])) ]) - def _generate_load_from_binary_attributes(self, attribute, sizeof_attribute_name, class_attributes): + def _generate_load_from_binary_attributes(self, attribute, + sizeof_attribute_name, class_attributes): attribute_name = attribute['name'] if sizeof_attribute_name is not None: - read_method_name = 'stream.{0}()'.format(get_read_method_name(attribute['size'])) + read_method_name = 'stream.{0}()'.format( + get_read_method_name(attribute['size'])) size = get_attribute_size(self.schema, attribute) - reverse_byte_method = get_reverse_method_name_if_needed(size).format(read_method_name) + reverse_byte_method = get_reverse_method_name( + size).format(read_method_name) self.load_from_binary_method.add_instructions([ '{0} {1} = {2}'.format(get_generated_type(self.schema, attribute), - attribute_name, reverse_byte_method) + attribute_name, reverse_byte_method) ]) else: load_attribute = { @@ -222,7 +252,8 @@ def _generate_load_from_binary_attributes(self, attribute, sizeof_attribute_name def _generate_load_from_binary_method(self, attributes): self.load_from_binary_method = JavaMethodGenerator( - 'public', self.class_name, 'loadFromBinary', ['DataInput stream'], 'throws Exception', True) + 'public', self.class_name, 'loadFromBinary', + ['DataInput stream'], 'throws Exception', True) self.load_from_binary_method.add_instructions( ['{0} obj = new {0}()'.format(self.class_name)]) self._recurse_inlines( @@ -231,13 +262,18 @@ def _generate_load_from_binary_method(self, attributes): self._add_method(self.load_from_binary_method) def _serialize_attribute_simple(self, attribute, class_attributes): - indent_required = self._add_attribute_condition_if_needed(attribute, class_attributes, self.serialize_method, 'this.') + indent_required = self._add_attribute_condition_if_needed( + attribute, class_attributes, self.serialize_method, 'this.') size = get_attribute_size(self.schema, attribute) - reverse_byte_method = get_reverse_method_name_if_needed(size).format('this.' + self._get_generated_getter_name(attribute['name'] + '()')) - line = 'stream.{0}({1})'.format(get_write_method_name(size), reverse_byte_method) - self.serialize_method.add_instructions([indent(line) if indent_required else line]) + reverse_byte_method = get_reverse_method_name(size).format( + 'this.' + get_generated_getter_name(attribute['name'] + '()')) + line = 'stream.{0}({1})'.format( + get_write_method_name(size), reverse_byte_method) + self.serialize_method.add_instructions( + [indent(line) if indent_required else line]) def _serialize_attribute_buffer(self, attribute, clas_attributes): + # pylint: disable=unused-argument attribute_name = attribute['name'] attribute_size = get_attribute_size(self.schema, attribute) self.serialize_method.add_instructions([ @@ -246,6 +282,7 @@ def _serialize_attribute_buffer(self, attribute, clas_attributes): ]) def _serialize_attribute_array(self, attribute, class_attributes): + # pylint: disable=unused-argument attribute_typename = attribute['type'] attribute_size = attribute['size'] attribute_name = attribute['name'] @@ -264,9 +301,10 @@ def _serialize_attribute_array(self, attribute, class_attributes): self.serialize_method.add_instructions(['}'], False) def _serialize_attribute_custom(self, attribute, class_attributes): + # pylint: disable=unused-argument self.serialize_method.add_instructions([ - 'byte[] {0} = this.{1}().serialize()'.format(attribute['name'], - self._get_generated_getter_name(attribute['name'])) + 'byte[] {0} = this.{1}().serialize()' + .format(attribute['name'], get_generated_getter_name(attribute['name'])) ]) self.serialize_method.add_instructions([ 'stream.write({0}, 0, {0}.length)'.format(attribute['name']) @@ -276,11 +314,14 @@ def _generate_serialize_attributes(self, attribute, sizeof_attribute_name, class attribute_name = attribute['name'] if sizeof_attribute_name is not None: size = get_attribute_size(self.schema, attribute) - size_extension = '.size()' if attribute_name.endswith('Count') else '.array().length' - full_property_name = '({0}){1}'.format(get_builtin_type(size), 'this.' + sizeof_attribute_name + size_extension) - reverse_byte_method = get_reverse_method_name_if_needed(size).format(full_property_name) + size_extension = '.size()' if attribute_name.endswith( + 'Count') else '.array().length' + full_property_name = '({0}){1}'.format(get_builtin_type( + size), 'this.' + sizeof_attribute_name + size_extension) + reverse_byte_method = get_reverse_method_name( + size).format(full_property_name) line = 'stream.{0}({1})'.format( - get_write_method_name(size), reverse_byte_method) + get_write_method_name(size), reverse_byte_method) self.serialize_method.add_instructions([line]) else: serialize_attribute = { @@ -306,9 +347,10 @@ def _generate_serialize_method(self, attributes): self._add_method(self.serialize_method) def _generate_attributes(self, attribute, sizeof_attribute_name, class_attributes): + # pylint: disable=unused-argument if sizeof_attribute_name is None: self._add_getter_setter(attribute) - + def _generate_getter_setter(self, class_layout): self._recurse_inlines(self._generate_attributes, class_layout) diff --git a/generators/java/JavaEnumGenerator.py b/generators/java/JavaEnumGenerator.py index dc88e510..eefacc8a 100755 --- a/generators/java/JavaEnumGenerator.py +++ b/generators/java/JavaEnumGenerator.py @@ -1,7 +1,13 @@ -from .Helpers import * +from .Helpers import get_generated_class_name, get_builtin_type, indent, get_attribute_size +from .Helpers import get_read_method_name, get_reverse_method_name, get_write_method_name from .JavaMethodGenerator import JavaMethodGenerator -class JavaEnumGenerator: +def get_type(attribute): + return get_builtin_type(attribute['size']) + +class JavaEnumGenerator(): + """Java enum generator""" + def __init__(self, name, schema, attribute): self.enum_name = get_generated_class_name(name) self.enum_output = ['public enum {0} {{'.format(self.enum_name)] @@ -12,20 +18,20 @@ def __init__(self, name, schema, attribute): self._add_enum_values(self.attribute) - def _get_type(self, attribute): - return get_builtin_type(attribute['size']) def _add_private_declaration(self, attribute): - var_type = self._get_type(attribute) - self.enum_output += [indent('private final {0} value;'.format(var_type))] + [''] + var_type = get_type(attribute) + self.enum_output += [ + indent('private final {0} value;'.format(var_type))] + [''] def _add_enum_values(self, enum_attribute): enum_attribute_values = enum_attribute['values'] for current_attribute in enum_attribute_values: - self.add_enum_value(current_attribute['name'], current_attribute['value']) - + self.add_enum_value( + current_attribute['name'], current_attribute['value']) + def _write_enum_values(self): - enum_type = self._get_type(self.attribute) + enum_type = get_type(self.attribute) enum_length = len(self.enum_values) count = 1 for name, value in self.enum_values.items(): @@ -36,19 +42,22 @@ def _write_enum_values(self): self.enum_output += [''] def _add_constructor(self, attribute): - enum_type = self._get_type(attribute) + enum_type = get_type(attribute) constructor_method = JavaMethodGenerator('private', '', self.enum_name, [ - '{0} value'.format(enum_type)]) + '{0} value'.format(enum_type)]) constructor_method.add_instructions(['this.value = value']) self.add_method(constructor_method) def _add_load_from_binary_method(self, attribute): load_from_binary_method = JavaMethodGenerator( - 'public', self.enum_name, 'loadFromBinary', ['DataInput stream'], 'throws Exception', True) + 'public', self.enum_name, 'loadFromBinary', + ['DataInput stream'], 'throws Exception', True) load_from_binary_method.add_instructions( - ['{0} val = stream.{1}()'.format(self._get_type(attribute), get_read_method_name(attribute['size']))]) + ['{0} val = stream.{1}()'.format( + get_type(attribute), get_read_method_name(attribute['size']))]) size = get_attribute_size(self.schema, attribute) - reverse_byte_method = get_reverse_method_name_if_needed(size).format('val') + reverse_byte_method = get_reverse_method_name( + size).format('val') load_from_binary_method.add_instructions( ['val = {0}'.format(reverse_byte_method)]) load_from_binary_method.add_instructions( @@ -60,7 +69,8 @@ def _add_load_from_binary_method(self, attribute): load_from_binary_method.add_instructions( ['}'], False) load_from_binary_method.add_instructions( - ['throw new RuntimeException(val + " was not a backing value for {0}.")'.format(self.enum_name)]) + ['throw new RuntimeException(val + " was not a backing value for {0}.")' + .format(self.enum_name)]) self.add_method(load_from_binary_method) def _add_serialize_method(self, attribute): @@ -71,9 +81,11 @@ def _add_serialize_method(self, attribute): serialize_method.add_instructions( ['DataOutputStream stream = new DataOutputStream(bos)']) size = get_attribute_size(self.schema, attribute) - reverse_byte_method = get_reverse_method_name_if_needed(size).format('this.value') + reverse_byte_method = get_reverse_method_name( + size).format('this.value') serialize_method.add_instructions([ - 'stream.{0}({1})'.format(get_write_method_name(size), reverse_byte_method) + 'stream.{0}({1})'.format( + get_write_method_name(size), reverse_byte_method) ]) serialize_method.add_instructions(['stream.close()']) serialize_method.add_instructions(['return bos.toByteArray()']) @@ -92,4 +104,4 @@ def generate(self): self._add_constructor(self.attribute) self._add_load_from_binary_method(self.attribute) self._add_serialize_method(self.attribute) - return self.enum_output + ['}'] \ No newline at end of file + return self.enum_output + ['}'] diff --git a/generators/java/JavaFileGenerator.py b/generators/java/JavaFileGenerator.py index bd7b3777..230c7d42 100755 --- a/generators/java/JavaFileGenerator.py +++ b/generators/java/JavaFileGenerator.py @@ -1,14 +1,14 @@ import os -from enum import Enum from generators.Descriptor import Descriptor -from .Helpers import * -from .JavaMethodGenerator import JavaMethodGenerator +from .Helpers import is_byte_type, is_enum_type, is_struct_type, get_generated_class_name from .JavaEnumGenerator import JavaEnumGenerator from .JavaClassGenerator import JavaClassGenerator + class JavaFileGenerator: + """Java file generator""" enum_class_list = {} - + def __init__(self, schema, options): self.schema = schema self.current = None @@ -51,14 +51,15 @@ def generate(self): # Typeless environment, values will be directly assigned pass elif is_enum_type(attribute_type): - JavaFileGenerator.enum_class_list[type_descriptor] = JavaEnumGenerator(type_descriptor, self.schema, value) - #self.code += new_enum.generate(value) - #yield self.code, type_descriptor - pass + JavaFileGenerator.enum_class_list[type_descriptor] = JavaEnumGenerator( + type_descriptor, self.schema, value) elif is_struct_type(attribute_type): - if type_descriptor.endswith('Body'): # skip all the inline classes + # skip all the inline classes + if type_descriptor.endswith('Body'): continue - new_class = JavaClassGenerator(type_descriptor, self.schema, value['layout'], JavaFileGenerator.enum_class_list) + new_class = JavaClassGenerator( + type_descriptor, self.schema, value['layout'], + JavaFileGenerator.enum_class_list) self.code += new_class.generate(value) yield self.code, get_generated_class_name(type_descriptor) diff --git a/generators/java/JavaMethodGenerator.py b/generators/java/JavaMethodGenerator.py index 55b75880..d7d8c4f8 100755 --- a/generators/java/JavaMethodGenerator.py +++ b/generators/java/JavaMethodGenerator.py @@ -1,8 +1,11 @@ -import os from .Helpers import indent + class JavaMethodGenerator: - def __init__(self, scope, return_type, name, params, exception_list = '', static = False): + """Java method generator""" + + def __init__(self, scope, return_type, name, params, exception_list='', static=False): + # pylint: disable-msg=too-many-arguments self.name = name if static: self.method_output = ['{0} static {1} {2}({3}) {4} {{'.format( @@ -18,4 +21,4 @@ def add_instructions(self, instructions, add_semicolon=True): self.method_output.append(indent(instruction)) def get_method(self): - return self.method_output + ['}'] \ No newline at end of file + return self.method_output + ['}'] From f9fe2b4f45a3a0d89d48f3eb79e03024b3320068 Mon Sep 17 00:00:00 2001 From: TB Dev Date: Thu, 4 Apr 2019 15:28:29 -0400 Subject: [PATCH 04/11] Fix pylint errors(2) --- generators/java/Helpers.py | 4 ++-- generators/java/JavaClassGenerator.py | 1 + generators/java/JavaEnumGenerator.py | 3 ++- generators/java/JavaFileGenerator.py | 3 --- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/generators/java/Helpers.py b/generators/java/Helpers.py index 4b3e1fa5..bb8d3248 100755 --- a/generators/java/Helpers.py +++ b/generators/java/Helpers.py @@ -150,9 +150,9 @@ def get_generated_type(schema, attribute): if attribute_kind == AttributeKind.SIMPLE: return get_builtin_type(get_attribute_size(schema, attribute)) - elif attribute_kind == AttributeKind.BUFFER: + if attribute_kind == AttributeKind.BUFFER: return 'ByteBuffer' - elif attribute_kind == AttributeKind.ARRAY: + if attribute_kind == AttributeKind.ARRAY: return 'java.util.ArrayList<{0}>'.format(typename) return typename diff --git a/generators/java/JavaClassGenerator.py b/generators/java/JavaClassGenerator.py index a04efbd6..7857a61e 100644 --- a/generators/java/JavaClassGenerator.py +++ b/generators/java/JavaClassGenerator.py @@ -27,6 +27,7 @@ def add_simple_setter(attribute, new_setter): new_setter.add_instructions( ['this.{0} = {0}'.format(attribute['name'])]) + def add_array_setter(attribute, new_setter): new_setter.add_instructions( ['this.{0} = {0}'.format(attribute['name'])]) diff --git a/generators/java/JavaEnumGenerator.py b/generators/java/JavaEnumGenerator.py index eefacc8a..569e9ea8 100755 --- a/generators/java/JavaEnumGenerator.py +++ b/generators/java/JavaEnumGenerator.py @@ -2,9 +2,11 @@ from .Helpers import get_read_method_name, get_reverse_method_name, get_write_method_name from .JavaMethodGenerator import JavaMethodGenerator + def get_type(attribute): return get_builtin_type(attribute['size']) + class JavaEnumGenerator(): """Java enum generator""" @@ -18,7 +20,6 @@ def __init__(self, name, schema, attribute): self._add_enum_values(self.attribute) - def _add_private_declaration(self, attribute): var_type = get_type(attribute) self.enum_output += [ diff --git a/generators/java/JavaFileGenerator.py b/generators/java/JavaFileGenerator.py index 230c7d42..78e082fe 100755 --- a/generators/java/JavaFileGenerator.py +++ b/generators/java/JavaFileGenerator.py @@ -39,7 +39,6 @@ def set_package(self): self.code += ['package catapult.builders;'] + [''] def generate(self): - for type_descriptor, value in self.schema.items(): self.code = [] self.prepend_copyright(self.options['copyright']) @@ -71,5 +70,3 @@ def generate(self): self.set_import() self.code += enum_class.generate() yield self.code, get_generated_class_name(type_descriptor) - - return From 424fad425631b2c0db2f1666fad8a1f354df3eba Mon Sep 17 00:00:00 2001 From: WayonB Date: Tue, 23 Apr 2019 00:58:57 -0400 Subject: [PATCH 05/11] Address comments --- .travis.yml | 1 + generators/java/Helpers.py | 23 +- generators/java/JavaClassGenerator.py | 483 +++++++++++------- .../java/JavaDefineTypeClassGenerator.py | 56 ++ generators/java/JavaEnumGenerator.py | 124 ++--- generators/java/JavaFileGenerator.py | 29 +- generators/java/JavaGeneratorBase.py | 109 ++++ generators/java/JavaMethodGenerator.py | 20 +- 8 files changed, 567 insertions(+), 278 deletions(-) create mode 100644 generators/java/JavaDefineTypeClassGenerator.py create mode 100644 generators/java/JavaGeneratorBase.py diff --git a/.travis.yml b/.travis.yml index 9b3eeaaf..a88fe5a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,3 +9,4 @@ script: - pycodestyle --config=.pycodestyle . - python3 -m unittest discover -v - bash ./scripts/generate_all.sh cpp_builder + - bash ./scripts/generate_all.sh java diff --git a/generators/java/Helpers.py b/generators/java/Helpers.py index bb8d3248..82653580 100755 --- a/generators/java/Helpers.py +++ b/generators/java/Helpers.py @@ -49,13 +49,12 @@ def get_attribute_size(schema, attribute): return attribute['size'] -def get_attribute_kind(schema, attribute): +def get_attribute_kind(attribute): attribute_type = attribute['type'] if is_struct_type(attribute_type) or is_enum_type(attribute_type): return AttributeKind.CUSTOM if 'size' not in attribute: - type_descriptor = schema[attribute_type] - return get_attribute_kind(schema, type_descriptor) + return AttributeKind.CUSTOM attribute_size = attribute['size'] @@ -81,12 +80,10 @@ def indent(code, n_indents=1): return ' ' * 4 * n_indents + code -def get_attribute_if_size(attribute_name, attributes): - for attribute in attributes: - if 'size' in attribute and attribute['size'] == attribute_name: - return attribute['name'] - - return None +def get_attribute_if_size(attribute_name, attributes, schema): + value = get_attribute_property_equal( + schema, attributes, 'size', attribute_name) + return value['name'] if value is not None else None def get_attribute_property_equal(schema, attributes, attribute_name, attribute_value): @@ -144,7 +141,7 @@ def get_write_method_name(size): def get_generated_type(schema, attribute): typename = attribute['type'] - attribute_kind = get_attribute_kind(schema, attribute) + attribute_kind = get_attribute_kind(attribute) if not is_byte_type(typename): typename = get_generated_class_name(typename) @@ -156,3 +153,9 @@ def get_generated_type(schema, attribute): return 'java.util.ArrayList<{0}>'.format(typename) return typename + + +def get_comments_if_present(comment): + if comment: + return '/** {0} */'.format(comment) + return None diff --git a/generators/java/JavaClassGenerator.py b/generators/java/JavaClassGenerator.py index 7857a61e..cc47cefc 100644 --- a/generators/java/JavaClassGenerator.py +++ b/generators/java/JavaClassGenerator.py @@ -2,61 +2,72 @@ from .Helpers import get_read_method_name, get_reverse_method_name, get_write_method_name from .Helpers import get_generated_type, get_attribute_property_equal, AttributeKind, is_byte_type from .Helpers import get_attribute_kind, TypeDescriptorDisposition, get_attribute_if_size +from .Helpers import get_comments_if_present, is_builtin_type from .JavaMethodGenerator import JavaMethodGenerator +from .JavaGeneratorBase import JavaGeneratorBase -def get_generated_getter_name(attribute): - return 'get{}'.format(attribute.capitalize()) +def capitalize_first_character(string): + return string[0].upper() + string[1:] -def get_generated_setter_name(attribute_name): - return 'set{}'.format(attribute_name.capitalize()) - - -def add_simple_getter(attribute, new_getter): - new_getter.add_instructions( - ['return this.{0}'.format(attribute['name'])]) +class JavaClassGenerator(JavaGeneratorBase): + """Java class generator""" + def __init__(self, name, schema, class_schema, enum_list): + super(JavaClassGenerator, self).__init__(name, schema, class_schema) -def add_buffer_getter(attribute, new_getter): - new_getter.add_instructions( - ['return this.{0}'.format(attribute['name'])]) + self.enum_list = enum_list + self.class_type = 'class' + if 'layout' in self.class_schema: + self._foreach_attributes( + self.class_schema['layout'], self._find_base_callback) -def add_simple_setter(attribute, new_setter): - new_setter.add_instructions( - ['this.{0} = {0}'.format(attribute['name'])]) + def _find_base_callback(self, attribute): + if ('disposition' in attribute and + attribute['disposition'] == TypeDescriptorDisposition.Inline.value and + self.should_generate_class(attribute['type'])): + self.base_class_name = attribute['type'] + return True + return False + def _should_declaration(self, attribute): + return not self.is_count_size_field(attribute) and attribute['name'] != 'size' -def add_array_setter(attribute, new_setter): - new_setter.add_instructions( - ['this.{0} = {0}'.format(attribute['name'])]) + def _add_private_declarations(self): + self._recurse_foreach_attribute( + self.class_schema['layout'], self._add_private_declaration) + self.class_output += [''] + def _add_private_declaration(self, attribute): + if not self.is_count_size_field(attribute): + line = get_comments_if_present(attribute['comments']) + if line is not None: + self.class_output += [indent(line)] + attribute_name = attribute['name'] + var_type = get_generated_type(self.schema, attribute) + scope = 'private' if attribute_name != 'size' else 'protected' + self.class_output += [ + indent('{0} {1} {2};'.format(scope, var_type, attribute_name))] -class JavaClassGenerator: - # pylint: disable=too-many-instance-attributes - """Java class generator""" @staticmethod - def get_generated_class_name(name): - return get_generated_class_name(name) + def _get_generated_getter_name(attribute_name): + return 'get{0}'.format(capitalize_first_character(attribute_name)) - def __init__(self, name, schema, class_schema, enum_list): - self.class_name = JavaClassGenerator.get_generated_class_name(name) - self.class_output = ['public class {0} {{'.format(self.class_name)] - self.load_from_binary_method = None - self.serialize_method = None - self.schema = schema - self.class_schema = class_schema - self.privates = [] - self.enum_list = enum_list + @staticmethod + def _get_generated_setter_name(attribute_name): + return 'set{0}'.format(capitalize_first_character(attribute_name)) - def _set_declarations(self): - self.class_output += [indent(line) for line in self.privates] + [''] + @staticmethod + def _add_simple_getter(attribute, new_getter): + new_getter.add_instructions( + ['return this.{0}'.format(attribute['name'])]) - def _add_private_declaration(self, attribute): - attribute_name = attribute['name'] - var_type = get_generated_type(self.schema, attribute) - self.privates += ['private {0} {1};'.format(var_type, attribute_name)] + @staticmethod + def _add_buffer_getter(attribute, new_getter): + new_getter.add_instructions( + ['return this.{0}'.format(attribute['name'])]) def _add_array_getter(self, attribute, new_getter): return_type = get_generated_type(self.schema, attribute) @@ -66,7 +77,7 @@ def _add_array_getter(self, attribute, new_getter): def _add_method_condition(self, attribute, method_writer): if 'condition' in attribute: condition_type_attribute = get_attribute_property_equal( - self.schema, self.class_schema, 'name', attribute['condition']) + self.schema, self.class_schema['layout'], 'name', attribute['condition']) condition_type_prefix = '' if condition_type_attribute is not None: condition_type_prefix = '{0}.'.format( @@ -83,19 +94,29 @@ def _add_getter(self, attribute): attribute_name = attribute['name'] return_type = get_generated_type(self.schema, attribute) new_getter = JavaMethodGenerator( - 'public', return_type, get_generated_getter_name(attribute_name), []) + 'public', return_type, self._get_generated_getter_name(attribute_name), []) self._add_method_condition(attribute, new_getter) getters = { - AttributeKind.SIMPLE: add_simple_getter, - AttributeKind.BUFFER: add_buffer_getter, + AttributeKind.SIMPLE: self._add_simple_getter, + AttributeKind.BUFFER: self._add_buffer_getter, AttributeKind.ARRAY: self._add_array_getter, - AttributeKind.CUSTOM: add_simple_getter + AttributeKind.CUSTOM: self._add_simple_getter } - attribute_kind = get_attribute_kind(self.schema, attribute) + attribute_kind = get_attribute_kind(attribute) getters[attribute_kind](attribute, new_getter) self._add_method(new_getter) + @staticmethod + def _add_simple_setter(attribute, new_setter): + new_setter.add_instructions( + ['this.{0} = {0}'.format(attribute['name'])]) + + @staticmethod + def _add_array_setter(attribute, new_setter): + new_setter.add_instructions( + ['this.{0} = {0}'.format(attribute['name'])]) + def _add_buffer_setter(self, attribute, new_setter): attribute_size = get_attribute_size(self.schema, attribute) attribute_name = attribute['name'] @@ -110,133 +131,144 @@ def _add_buffer_setter(self, attribute, new_setter): new_setter.add_instructions( [indent('throw new IllegalArgumentException("{0} should be {1} bytes")' .format(attribute_name, attribute_size))]) - new_setter.add_instructions([''], False) + new_setter.add_instructions([''], False) new_setter.add_instructions( ['this.{0} = {0}'.format(attribute_name)]) - def _add_setter(self, attribute): - attribute_name = attribute['name'] - return_type = get_generated_type(self.schema, attribute) - new_setter = JavaMethodGenerator('public', 'void', get_generated_setter_name( - attribute_name), [return_type + ' ' + attribute_name]) - self._add_method_condition(attribute, new_setter) - - setters = { - AttributeKind.SIMPLE: add_simple_setter, - AttributeKind.BUFFER: self._add_buffer_setter, - AttributeKind.ARRAY: add_array_setter, - AttributeKind.CUSTOM: add_simple_setter - } - - attribute_kind = get_attribute_kind(self.schema, attribute) - setters[attribute_kind](attribute, new_setter) - self._add_method(new_setter) + @staticmethod + def _add_size_value(attribute, size_list): + kind = get_attribute_kind(attribute) + if kind == AttributeKind.SIMPLE: + size_list.append( + '{0}; // {1}'.format(attribute['size'], attribute['name'])) + elif kind == AttributeKind.BUFFER: + size_list.append( + 'this.{0}.array().length;'.format(attribute['name'])) + elif kind == AttributeKind.ARRAY: + size_list.append('this.{0}.stream().mapToInt(o -> o.getSize()).sum();'. + format(attribute['name'])) + else: + size_list.append('this.{0}.getSize();'.format(attribute['name'])) + + def _calculate_size(self, new_getter): + size_list = [] + size_attribute = get_attribute_property_equal( + self.schema, self.schema['SizePrefixedEntity']['layout'], 'name', 'size') + return_type = get_builtin_type(size_attribute['size']) + if self.base_class_name is not None: + size_list.append('super.getSize();') + self._recurse_foreach_attribute(self.class_schema['layout'], + self._add_size_value, size_list) + new_getter.add_instructions( + ['{0} size = {1}'.format(return_type, size_list[0])], False) + for size in size_list[1:]: + new_getter.add_instructions(['size += {0}'.format(size)], False) + new_getter.add_instructions(['return size']) def _add_getter_setter(self, attribute): - self._add_getter(attribute) - self._add_setter(attribute) - self._add_private_declaration(attribute) + if self._should_declaration(attribute): + self._add_getter(attribute) - def _add_method(self, method): - self.class_output += [indent(line) - for line in method.get_method()] + [''] - - def _recurse_inlines(self, generate_attribute_method, attributes): + def _recurse_foreach_attribute(self, attributes, callback, + context=None, ignore_base_class=True): for attribute in attributes: if 'disposition' in attribute: if attribute['disposition'] == TypeDescriptorDisposition.Inline.value: - self._recurse_inlines( - generate_attribute_method, self.schema[attribute['type']]['layout']) + if ignore_base_class and self.base_class_name == attribute['type']: + continue + self._recurse_foreach_attribute( + self.schema[attribute['type']]['layout'], callback, context) elif attribute['disposition'] == TypeDescriptorDisposition.Const.value: - # add dymanic enum if present in this class + # add dynamic enum if present in this class enum_name = attribute['type'] if enum_name in self.enum_list: self.enum_list[enum_name].add_enum_value( - self.class_name, attribute['value']) + self.builder_class_name, + attribute['value'], + attribute['comments']) else: - generate_attribute_method( - attribute, get_attribute_if_size(attribute['name'], attributes), attributes) + if context is None: + callback(attribute) + else: + callback(attribute, context) - def _add_attribute_condition_if_needed(self, attribute, class_schema, - method_writer, obj_prefix): + def _add_attribute_condition_if_needed(self, attribute, method_writer, obj_prefix): if 'condition' in attribute: condition_type_attribute = get_attribute_property_equal( - self.schema, class_schema, 'name', attribute['condition']) + self.schema, self.class_schema, 'name', attribute['condition']) condition_type_prefix = '' if condition_type_attribute is not None: condition_type_prefix = '{0}.'.format( get_generated_class_name(condition_type_attribute['type'])) method_writer.add_instructions(['if ({0}{1}() == {2}{3})'.format( - obj_prefix, get_generated_getter_name( + obj_prefix, self._get_generated_getter_name( attribute['condition']), condition_type_prefix, attribute['condition_value'].upper())], False) return True return False - def _load_from_binary_simple(self, attribute, class_attributes): + def _load_from_binary_simple(self, attribute, load_from_binary_method): indent_required = self._add_attribute_condition_if_needed( - attribute, class_attributes, self.load_from_binary_method, 'obj.') + attribute, load_from_binary_method, 'this.') size = get_attribute_size(self.schema, attribute) read_method_name = 'stream.{0}()'.format(get_read_method_name(size)) reverse_byte_method = get_reverse_method_name( size).format(read_method_name) - line = 'obj.{0}({1})'.format(get_generated_setter_name( - attribute['name']), reverse_byte_method) - self.load_from_binary_method.add_instructions( + line = 'this.{0} = {1}'.format(attribute['name'], reverse_byte_method) + load_from_binary_method.add_instructions( [indent(line) if indent_required else line]) - def _load_from_binary_buffer(self, attribute, clas_attributes): - # pylint: disable=unused-argument + def _load_from_binary_buffer(self, attribute, load_from_binary_method): attribute_name = attribute['name'] attribute_size = get_attribute_size(self.schema, attribute) - self.load_from_binary_method.add_instructions( - ['obj.{0} = ByteBuffer.allocate({1})'.format(attribute_name, attribute_size)]) - self.load_from_binary_method.add_instructions([ - 'stream.{0}(obj.{1}.array())'.format( + load_from_binary_method.add_instructions( + ['this.{0} = ByteBuffer.allocate({1})'.format(attribute_name, attribute_size)]) + load_from_binary_method.add_instructions([ + 'stream.{0}(this.{1}.array())'.format( get_read_method_name(attribute_size), attribute_name) ]) - def _load_from_binary_array(self, attribute, class_attributes): - # pylint: disable=unused-argument + @staticmethod + def _load_from_binary_array(attribute, load_from_binary_method): attribute_typename = attribute['type'] attribute_sizename = attribute['size'] attribute_name = attribute['name'] - self.load_from_binary_method.add_instructions( - ['java.util.ArrayList<{1}> {0} = new java.util.ArrayList<{1}>({2})'.format( - attribute_name, get_generated_class_name(attribute_typename), attribute_sizename)]) - self.load_from_binary_method.add_instructions([ + load_from_binary_method.add_instructions( + ['this.{0} = new java.util.ArrayList<>({1})'.format( + attribute_name, attribute_sizename)]) + load_from_binary_method.add_instructions([ 'for (int i = 0; i < {0}; i++) {{'.format(attribute_sizename)], False) if is_byte_type(attribute_typename): - self.load_from_binary_method.add_instructions([indent( + load_from_binary_method.add_instructions([indent( '{0}.add(stream.{1}())'.format(attribute_name, get_read_method_name(1)))]) else: - self.load_from_binary_method.add_instructions([indent( + load_from_binary_method.add_instructions([indent( '{0}.add({1}.loadFromBinary(stream))'.format( attribute_name, get_generated_class_name(attribute_typename)))]) - self.load_from_binary_method.add_instructions(['}'], False) - self.load_from_binary_method.add_instructions(['obj.{0}({1})'.format( - get_generated_setter_name(attribute['name']), attribute_name)]) - - def _load_from_binary_custom(self, attribute, class_attributes): - # pylint: disable=unused-argument - self.load_from_binary_method.add_instructions([ - 'obj.{0}({1}.loadFromBinary(stream))'.format( - get_generated_setter_name(attribute['name']), - get_generated_class_name(attribute['type'])) + load_from_binary_method.add_instructions(['}'], False) + + @staticmethod + def _load_from_binary_custom(attribute, load_from_binary_method): + load_from_binary_method.add_instructions([ + 'this.{0} = {1}.loadFromBinary(stream)' + .format(attribute['name'], get_generated_class_name(attribute['type'])) ]) - def _generate_load_from_binary_attributes(self, attribute, - sizeof_attribute_name, class_attributes): + @staticmethod + def is_count_size_field(field): + return field['name'].endswith('Size') or field['name'].endswith('Count') + + def _generate_load_from_binary_attributes(self, attribute, load_from_binary_method): attribute_name = attribute['name'] - if sizeof_attribute_name is not None: + if self.is_count_size_field(attribute): read_method_name = 'stream.{0}()'.format( get_read_method_name(attribute['size'])) size = get_attribute_size(self.schema, attribute) reverse_byte_method = get_reverse_method_name( size).format(read_method_name) - self.load_from_binary_method.add_instructions([ + load_from_binary_method.add_instructions([ '{0} {1} = {2}'.format(get_generated_type(self.schema, attribute), attribute_name, reverse_byte_method) ]) @@ -248,82 +280,74 @@ def _generate_load_from_binary_attributes(self, attribute, AttributeKind.CUSTOM: self._load_from_binary_custom } - attribute_kind = get_attribute_kind(self.schema, attribute) - load_attribute[attribute_kind](attribute, class_attributes) - - def _generate_load_from_binary_method(self, attributes): - self.load_from_binary_method = JavaMethodGenerator( - 'public', self.class_name, 'loadFromBinary', - ['DataInput stream'], 'throws Exception', True) - self.load_from_binary_method.add_instructions( - ['{0} obj = new {0}()'.format(self.class_name)]) - self._recurse_inlines( - self._generate_load_from_binary_attributes, attributes) - self.load_from_binary_method.add_instructions(['return obj']) - self._add_method(self.load_from_binary_method) - - def _serialize_attribute_simple(self, attribute, class_attributes): - indent_required = self._add_attribute_condition_if_needed( - attribute, class_attributes, self.serialize_method, 'this.') + attribute_kind = get_attribute_kind(attribute) + load_attribute[attribute_kind](attribute, load_from_binary_method) + + def _serialize_attribute_simple(self, attribute, serialize_method): + indent_required = self._add_attribute_condition_if_needed(attribute, + serialize_method, 'this.') size = get_attribute_size(self.schema, attribute) reverse_byte_method = get_reverse_method_name(size).format( - 'this.' + get_generated_getter_name(attribute['name'] + '()')) - line = 'stream.{0}({1})'.format( + 'this.' + self._get_generated_getter_name(attribute['name'] + '()')) + line = 'dataOutputStream.{0}({1})'.format( get_write_method_name(size), reverse_byte_method) - self.serialize_method.add_instructions( + serialize_method.add_instructions( [indent(line) if indent_required else line]) - def _serialize_attribute_buffer(self, attribute, clas_attributes): - # pylint: disable=unused-argument + def _serialize_attribute_buffer(self, attribute, serialize_method): attribute_name = attribute['name'] attribute_size = get_attribute_size(self.schema, attribute) - self.serialize_method.add_instructions([ - 'stream.{0}(this.{1}.array(), 0, this.{1}.array().length)'.format( + serialize_method.add_instructions([ + 'dataOutputStream.{0}(this.{1}.array(), 0, this.{1}.array().length)'.format( get_write_method_name(attribute_size), attribute_name) ]) - def _serialize_attribute_array(self, attribute, class_attributes): - # pylint: disable=unused-argument + @staticmethod + def _serialize_attribute_array(attribute, serialize_method): attribute_typename = attribute['type'] attribute_size = attribute['size'] attribute_name = attribute['name'] - self.serialize_method.add_instructions([ + serialize_method.add_instructions([ 'for (int i = 0; i < this.{0}.size(); i++) {{'.format(attribute_name) ], False) if is_byte_type(attribute_typename): - self.serialize_method.add_instructions([indent( - 'stream.{0}(this.{1}.get(i))'.format(get_write_method_name(1), attribute_name))]) + serialize_method.add_instructions([indent( + 'dataOutputStream.{0}(this.{1}.get(i))'.format(get_write_method_name(1), + attribute_name))]) else: - self.serialize_method.add_instructions([indent( + serialize_method.add_instructions([indent( 'byte[] ser = this.{0}.get(i).serialize()'.format(attribute_name))]) - self.serialize_method.add_instructions([indent( - 'stream.{0}(ser, 0, ser.length)'.format(get_write_method_name(attribute_size)))]) - self.serialize_method.add_instructions(['}'], False) + serialize_method.add_instructions([indent( + 'dataOutputStream.{0}(ser, 0, ser.length)'.format( + get_write_method_name(attribute_size)))]) + serialize_method.add_instructions(['}'], False) - def _serialize_attribute_custom(self, attribute, class_attributes): - # pylint: disable=unused-argument - self.serialize_method.add_instructions([ + def _serialize_attribute_custom(self, attribute, serialize_method): + serialize_method.add_instructions([ 'byte[] {0} = this.{1}().serialize()' - .format(attribute['name'], get_generated_getter_name(attribute['name'])) + .format(attribute['name'], self._get_generated_getter_name(attribute['name'])) ]) - self.serialize_method.add_instructions([ - 'stream.write({0}, 0, {0}.length)'.format(attribute['name']) + serialize_method.add_instructions([ + 'dataOutputStream.write({0}, 0, {0}.length)'.format( + attribute['name']) ]) - def _generate_serialize_attributes(self, attribute, sizeof_attribute_name, class_attributes): + def _generate_serialize_attributes(self, attribute, serialize_method): attribute_name = attribute['name'] - if sizeof_attribute_name is not None: + if self.is_count_size_field(attribute): size = get_attribute_size(self.schema, attribute) size_extension = '.size()' if attribute_name.endswith( 'Count') else '.array().length' - full_property_name = '({0}){1}'.format(get_builtin_type( - size), 'this.' + sizeof_attribute_name + size_extension) + full_property_name = '({0}){1}'.format( + get_builtin_type(size), 'this.' + + get_attribute_if_size(attribute['name'], self.class_schema['layout'], + self.schema) + size_extension) reverse_byte_method = get_reverse_method_name( size).format(full_property_name) - line = 'stream.{0}({1})'.format( + line = 'dataOutputStream.{0}({1})'.format( get_write_method_name(size), reverse_byte_method) - self.serialize_method.add_instructions([line]) + serialize_method.add_instructions([line]) else: serialize_attribute = { AttributeKind.SIMPLE: self._serialize_attribute_simple, @@ -332,33 +356,116 @@ def _generate_serialize_attributes(self, attribute, sizeof_attribute_name, class AttributeKind.CUSTOM: self._serialize_attribute_custom } - attribute_kind = get_attribute_kind(self.schema, attribute) - serialize_attribute[attribute_kind](attribute, class_attributes) - - def _generate_serialize_method(self, attributes): - self.serialize_method = JavaMethodGenerator( - 'public', 'byte[]', 'serialize', [], 'throws Exception') - self.serialize_method.add_instructions( - ['ByteArrayOutputStream bos = new ByteArrayOutputStream()']) - self.serialize_method.add_instructions( - ['DataOutputStream stream = new DataOutputStream(bos)']) - self._recurse_inlines(self._generate_serialize_attributes, attributes) - self.serialize_method.add_instructions(['stream.close()']) - self.serialize_method.add_instructions(['return bos.toByteArray()']) - self._add_method(self.serialize_method) - - def _generate_attributes(self, attribute, sizeof_attribute_name, class_attributes): - # pylint: disable=unused-argument - if sizeof_attribute_name is None: - self._add_getter_setter(attribute) - - def _generate_getter_setter(self, class_layout): - self._recurse_inlines(self._generate_attributes, class_layout) - - def generate(self, class_schema): - class_layout = class_schema['layout'] - self._generate_getter_setter(class_layout) - self._generate_load_from_binary_method(class_layout) - self._generate_serialize_method(class_layout) - self._set_declarations() - return self.class_output + ['}'] + attribute_kind = get_attribute_kind(attribute) + serialize_attribute[attribute_kind](attribute, serialize_method) + + def _add_getter_setter_field(self): + self._recurse_foreach_attribute( + self.class_schema['layout'], self._add_getter_setter) + + def _add_public_declarations(self): + self._add_constructor() + self._add_constructor_stream() + self._add_factory_method() + self._add_getter_setter_field() + + def _add_load_from_binary_custom(self, load_from_binary_method): + load_from_binary_method.add_instructions( + ['return new {0}(stream)'.format(self.builder_class_name)]) + + def _add_serialize_custom(self, add_serialize_method): + if self.base_class_name is not None: + add_serialize_method.add_instructions( + ['byte[] superBytes = super.serialize()']) + add_serialize_method.add_instructions( + ['dataOutputStream.write(superBytes, 0, superBytes.length)']) + self._recurse_foreach_attribute(self.class_schema['layout'], + self._generate_serialize_attributes, + add_serialize_method) + + def _add_constructor_stream(self): + load_stream_constructor = JavaMethodGenerator( + 'protected', '', self.builder_class_name, + ['DataInput stream'], 'throws Exception') + if self.base_class_name is not None: + load_stream_constructor.add_instructions(['super(stream)']) + self._recurse_foreach_attribute(self.class_schema['layout'], + self._generate_load_from_binary_attributes, + load_stream_constructor) + + self._add_method(load_stream_constructor) + + def _add_to_variable(self, attribute, param_list): + if self._should_declaration(attribute): + param_list.append('{0}'.format(attribute['name'])) + + def _add_to_param(self, attribute, param_list): + if self._should_declaration(attribute): + attribute_name = attribute['name'] + attribute_type = get_generated_type(self.schema, attribute) + param_list.append('{0} {1}'.format(attribute_type, attribute_name)) + + def _create_list(self, attributes, callback, ignore_base_class=False): + param_list = [] + self._recurse_foreach_attribute(attributes, + callback, + param_list, ignore_base_class) + param_string = param_list[0] + for param in param_list[1:]: + param_string += ', {0}'.format(param) + return param_string + + def _create_param_list(self): + return self._create_list(self.class_schema['layout'], self._add_to_param) + + @staticmethod + def _add_attribute_to_list(attribute, attribute_list): + attribute_list.append(attribute) + + def _add_constructor(self): + constructor_method = JavaMethodGenerator( + 'protected', '', self.builder_class_name, + [self._create_param_list()], 'throws Exception') + + if self.base_class_name is not None: + constructor_method.add_instructions(['super({0})'.format( + self._create_list(self.schema[self.base_class_name]['layout'], + self._add_to_variable))]) + constructor_method.add_instructions([''], False) + + object_attributes = [] + self._recurse_foreach_attribute(self.class_schema['layout'], + self._add_attribute_to_list, + object_attributes) + for attribute in object_attributes: + if 'size' not in attribute or not is_builtin_type(attribute['type'], attribute['size']): + attribute_name = attribute['name'] + constructor_method.add_instructions( + ['if ({0} == null)'.format(attribute_name)], False) + constructor_method.add_instructions( + [indent('throw new NullPointerException("{0}")'.format(attribute_name))]) + constructor_method.add_instructions([''], False) + + for variable in object_attributes: + if self._should_declaration(variable): + constructor_method.add_instructions( + ['this.{0} = {0}'.format(variable['name'])]) + + self._add_method(constructor_method) + + def _add_factory_method(self): + factory = JavaMethodGenerator( + 'public', self.builder_class_name, 'create', + [self._create_param_list()], 'throws Exception', True) + factory.add_instructions(['return new {0}({1})'.format( + self.builder_class_name, self._create_list( + self.class_schema['layout'], self._add_to_variable))]) + self._add_method(factory) + + @staticmethod + def should_generate_class(name): + return (name.startswith('Embedded') + or name.endswith('Transaction') + or name.startswith('Mosaic') + or name.endswith('Mosaic') + or name.endswith('Modification')) diff --git a/generators/java/JavaDefineTypeClassGenerator.py b/generators/java/JavaDefineTypeClassGenerator.py new file mode 100644 index 00000000..4fa4147e --- /dev/null +++ b/generators/java/JavaDefineTypeClassGenerator.py @@ -0,0 +1,56 @@ +# pylint: disable=too-few-public-methods +from .Helpers import get_generated_type, AttributeKind, get_attribute_kind +from .JavaMethodGenerator import JavaMethodGenerator +from .JavaClassGenerator import JavaClassGenerator + + +class JavaDefineTypeClassGenerator(JavaClassGenerator): + """Java define type class generator""" + + def __init__(self, name, schema, class_schema, enum_list): + class_schema['name'] = name[0].lower() + name[1:] + super(JavaDefineTypeClassGenerator, self).__init__( + name, schema, class_schema, enum_list) + + def _add_public_declarations(self): + self._add_constructor(self.class_schema) + self._add_constructor_stream() + self._add_getter_setter(self.class_schema) + + def _add_private_declarations(self): + self._add_private_declaration(self.class_schema) + self.class_output += [''] + + def _add_serialize_custom(self, add_serialize_method): + self._generate_serialize_attributes( + self.class_schema, add_serialize_method) + + def _calculate_size(self, new_getter): + new_getter.add_instructions( + ['return {0}'.format(self.class_schema['size'])]) + + def _add_constructor(self, attribute): + attribute_name = attribute['name'] + return_type = get_generated_type(self.schema, attribute) + new_setter = JavaMethodGenerator('public', '', + self.builder_class_name, + [return_type + ' ' + attribute_name]) + + setters = { + AttributeKind.SIMPLE: self._add_simple_setter, + AttributeKind.BUFFER: self._add_buffer_setter, + AttributeKind.ARRAY: self._add_array_setter, + AttributeKind.CUSTOM: self._add_simple_setter + } + + attribute_kind = get_attribute_kind(attribute) + setters[attribute_kind](attribute, new_setter) + self._add_method(new_setter) + + def _add_constructor_stream(self): + load_stream_constructor = JavaMethodGenerator( + 'public', '', self.builder_class_name, + ['DataInput stream'], 'throws Exception') + self._generate_load_from_binary_attributes( + self.class_schema, load_stream_constructor) + self._add_method(load_stream_constructor) diff --git a/generators/java/JavaEnumGenerator.py b/generators/java/JavaEnumGenerator.py index 569e9ea8..e5c7de20 100755 --- a/generators/java/JavaEnumGenerator.py +++ b/generators/java/JavaEnumGenerator.py @@ -1,108 +1,112 @@ -from .Helpers import get_generated_class_name, get_builtin_type, indent, get_attribute_size +from .Helpers import get_builtin_type, indent, get_attribute_size from .Helpers import get_read_method_name, get_reverse_method_name, get_write_method_name +from .Helpers import get_comments_if_present from .JavaMethodGenerator import JavaMethodGenerator +from .JavaGeneratorBase import JavaGeneratorBase def get_type(attribute): return get_builtin_type(attribute['size']) -class JavaEnumGenerator(): +def create_enum_name(name): + return name[0] + ''.join('_' + x if x.isupper() else x for x in name[1:]) + + +class JavaEnumGenerator(JavaGeneratorBase): """Java enum generator""" - def __init__(self, name, schema, attribute): - self.enum_name = get_generated_class_name(name) - self.enum_output = ['public enum {0} {{'.format(self.enum_name)] - self.schema = schema - self.privates = [] - self.attribute = attribute + def __init__(self, name, schema, class_schema): + super(JavaEnumGenerator, self).__init__(name, schema, class_schema) self.enum_values = {} + self.class_type = 'enum' - self._add_enum_values(self.attribute) + self._add_enum_values(self.class_schema) - def _add_private_declaration(self, attribute): - var_type = get_type(attribute) - self.enum_output += [ + def _add_private_declaration(self): + var_type = get_type(self.class_schema) + self.class_output += [ indent('private final {0} value;'.format(var_type))] + [''] def _add_enum_values(self, enum_attribute): enum_attribute_values = enum_attribute['values'] for current_attribute in enum_attribute_values: self.add_enum_value( - current_attribute['name'], current_attribute['value']) + current_attribute['name'], + current_attribute['value'], + current_attribute['comments']) def _write_enum_values(self): - enum_type = get_type(self.attribute) + enum_type = get_type(self.class_schema) enum_length = len(self.enum_values) count = 1 - for name, value in self.enum_values.items(): - line = '{0}(({1}){2})'.format(name.upper(), enum_type, value) + for name, value_comments in self.enum_values.items(): + value, comments = value_comments + comment_line = get_comments_if_present(comments) + if comment_line is not None: + self.class_output += [indent(comment_line)] + line = '{0}(({1}) {2})'.format(name.upper(), enum_type, value) line += ',' if count < enum_length else ';' - self.enum_output += [indent(line)] + self.class_output += [indent(line)] count += 1 - self.enum_output += [''] + self.class_output += [''] - def _add_constructor(self, attribute): - enum_type = get_type(attribute) - constructor_method = JavaMethodGenerator('private', '', self.enum_name, [ + def _add_constructor(self): + enum_type = get_type(self.class_schema) + constructor_method = JavaMethodGenerator('private', '', self.builder_class_name, [ '{0} value'.format(enum_type)]) constructor_method.add_instructions(['this.value = value']) - self.add_method(constructor_method) + self._add_method(constructor_method) - def _add_load_from_binary_method(self, attribute): - load_from_binary_method = JavaMethodGenerator( - 'public', self.enum_name, 'loadFromBinary', - ['DataInput stream'], 'throws Exception', True) - load_from_binary_method.add_instructions( - ['{0} val = stream.{1}()'.format( - get_type(attribute), get_read_method_name(attribute['size']))]) - size = get_attribute_size(self.schema, attribute) + def _add_load_from_binary_custom(self, load_from_binary_method): + read_data_line = 'stream.{0}()'.format( + get_read_method_name(self.class_schema['size'])) + size = get_attribute_size(self.schema, self.class_schema) reverse_byte_method = get_reverse_method_name( - size).format('val') + size).format(read_data_line) load_from_binary_method.add_instructions( - ['val = {0}'.format(reverse_byte_method)]) + ['{0} streamValue = {1}'.format(get_type(self.class_schema), reverse_byte_method)]) load_from_binary_method.add_instructions( - ['for ({0} current : {0}.values()) {{'.format(self.enum_name)], False) + ['for ({0} current : {0}.values()) {{'.format(self.builder_class_name)], False) load_from_binary_method.add_instructions( - [indent('if (val == current.value)')], False) + [indent('if (streamValue == current.value)')], False) load_from_binary_method.add_instructions( [indent('return current', 2)]) load_from_binary_method.add_instructions( ['}'], False) load_from_binary_method.add_instructions( - ['throw new RuntimeException(val + " was not a backing value for {0}.")' - .format(self.enum_name)]) - self.add_method(load_from_binary_method) - - def _add_serialize_method(self, attribute): - serialize_method = JavaMethodGenerator( - 'public', 'byte[]', 'serialize', [], 'throws Exception') - serialize_method.add_instructions( - ['ByteArrayOutputStream bos = new ByteArrayOutputStream()']) - serialize_method.add_instructions( - ['DataOutputStream stream = new DataOutputStream(bos)']) - size = get_attribute_size(self.schema, attribute) + ['throw new RuntimeException(streamValue + " was not a backing value for {0}.")' + .format(self.builder_class_name)]) + + def _add_serialize_custom(self, serialize_method): + size = get_attribute_size(self.schema, self.class_schema) reverse_byte_method = get_reverse_method_name( size).format('this.value') serialize_method.add_instructions([ - 'stream.{0}({1})'.format( + 'dataOutputStream.{0}({1})'.format( get_write_method_name(size), reverse_byte_method) ]) - serialize_method.add_instructions(['stream.close()']) - serialize_method.add_instructions(['return bos.toByteArray()']) - self.add_method(serialize_method) - def add_method(self, method): - self.enum_output += [indent(line) - for line in method.get_method()] + [''] + def add_enum_value(self, name, value, comments): + self.enum_values[create_enum_name(name)] = [value, comments] + + def _add_public_declarations(self): + pass + + def _add_private_declarations(self): + self._add_private_declaration() + self._add_constructor() - def add_enum_value(self, name, value): - self.enum_values[name] = value + def _calculate_size(self, new_getter): + new_getter.add_instructions( + ['return {0}'.format(self.class_schema['size'])]) def generate(self): + self._add_class_definition() self._write_enum_values() - self._add_private_declaration(self.attribute) - self._add_constructor(self.attribute) - self._add_load_from_binary_method(self.attribute) - self._add_serialize_method(self.attribute) - return self.enum_output + ['}'] + self._add_private_declarations() + self._add_public_declarations() + self._add_size_getter() + self._add_load_from_binary_method() + self._add_serialize_method() + return self.class_output + ['}'] diff --git a/generators/java/JavaFileGenerator.py b/generators/java/JavaFileGenerator.py index 78e082fe..1b347c9c 100755 --- a/generators/java/JavaFileGenerator.py +++ b/generators/java/JavaFileGenerator.py @@ -3,6 +3,7 @@ from .Helpers import is_byte_type, is_enum_type, is_struct_type, get_generated_class_name from .JavaEnumGenerator import JavaEnumGenerator from .JavaClassGenerator import JavaClassGenerator +from .JavaDefineTypeClassGenerator import JavaDefineTypeClassGenerator class JavaFileGenerator: @@ -27,16 +28,16 @@ def __next__(self): def prepend_copyright(self, copyright_file): if os.path.isfile(copyright_file): with open(copyright_file) as header: - self.code = [line.strip() for line in header] + [''] + self.code = [line.strip() for line in header] def set_import(self): self.code += ['import java.lang.*;'] self.code += ['import java.io.*;'] self.code += ['import java.nio.*;'] - self.code += ['import catapult.builders.*;'] + [''] + self.code += ['import io.nem.catapult.builders.*;'] + [''] def set_package(self): - self.code += ['package catapult.builders;'] + [''] + self.code += ['package io.nem.catapult.builders;'] + [''] def generate(self): for type_descriptor, value in self.schema.items(): @@ -47,22 +48,24 @@ def generate(self): attribute_type = value['type'] if is_byte_type(attribute_type): - # Typeless environment, values will be directly assigned - pass + new_class = JavaDefineTypeClassGenerator( + type_descriptor, self.schema, value, + JavaFileGenerator.enum_class_list) + self.code += new_class.generate() + yield self.code, get_generated_class_name(type_descriptor) elif is_enum_type(attribute_type): JavaFileGenerator.enum_class_list[type_descriptor] = JavaEnumGenerator( type_descriptor, self.schema, value) elif is_struct_type(attribute_type): # skip all the inline classes - if type_descriptor.endswith('Body'): - continue - new_class = JavaClassGenerator( - type_descriptor, self.schema, value['layout'], - JavaFileGenerator.enum_class_list) - self.code += new_class.generate(value) - yield self.code, get_generated_class_name(type_descriptor) + if JavaClassGenerator.should_generate_class(type_descriptor): + new_class = JavaClassGenerator( + type_descriptor, self.schema, value, + JavaFileGenerator.enum_class_list) + self.code += new_class.generate() + yield self.code, get_generated_class_name(type_descriptor) - # write all the enum last just in case there are 'dymanic values' + # write all the enum last just in case there are 'dynamic values' for type_descriptor, enum_class in JavaFileGenerator.enum_class_list.items(): self.code = [] self.prepend_copyright(self.options['copyright']) diff --git a/generators/java/JavaGeneratorBase.py b/generators/java/JavaGeneratorBase.py new file mode 100644 index 00000000..c795bc8b --- /dev/null +++ b/generators/java/JavaGeneratorBase.py @@ -0,0 +1,109 @@ +# pylint: disable=too-few-public-methods +from abc import ABC, abstractmethod +from .Helpers import get_generated_class_name, get_comments_if_present, indent, get_builtin_type +from .Helpers import get_attribute_property_equal +from .JavaMethodGenerator import JavaMethodGenerator + + +class JavaGeneratorBase(ABC): + + def __init__(self, name, schema, class_schema): + self.builder_class_name = get_generated_class_name(name) + self.base_class_name = None + self.class_output = [] + self.schema = schema + self.privates = [] + self.class_schema = class_schema + self.class_type = None + + @abstractmethod + def _add_class_definition(self): + raise NotImplementedError('need to override method') + + @abstractmethod + def _add_public_declarations(self): + raise NotImplementedError('need to override method') + + @abstractmethod + def _add_serialize_custom(self, serialize_method): + raise NotImplementedError('need to override method') + + @abstractmethod + def _add_load_from_binary_custom(self, load_from_binary_method): + raise NotImplementedError('need to override method') + + @abstractmethod + def _add_private_declarations(self): + raise NotImplementedError('need to override method') + + @abstractmethod + def _calculate_size(self, new_getter): + raise NotImplementedError('need to override method') + + @staticmethod + def _foreach_attributes(attributes, callback, context=None): + for attribute in attributes: + if context is None: + if callback(attribute): + break + else: + if callback(attribute, context): + break + + def _add_method(self, method, add_empty_line=True): + self.class_output += [indent(line) + for line in method.get_method()] + if add_empty_line: + self.class_output += [''] + + def _add_load_from_binary_method(self): + load_from_binary_method = JavaMethodGenerator( + 'public', self.builder_class_name, 'loadFromBinary', + ['DataInput stream'], 'throws Exception', True) + self._add_load_from_binary_custom(load_from_binary_method) + self._add_method(load_from_binary_method) + + def _add_serialize_method(self): + serialize_method = JavaMethodGenerator( + 'public', 'byte[]', 'serialize', [], 'throws Exception') + serialize_method.add_instructions( + ['ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()']) + serialize_method.add_instructions( + ['DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream)']) + self._add_serialize_custom(serialize_method) + serialize_method.add_instructions(['dataOutputStream.close()']) + serialize_method.add_instructions( + ['return byteArrayOutputStream.toByteArray()']) + self._add_method(serialize_method, False) + + def _add_class_definition(self): + line = get_comments_if_present(self.class_schema['comments']) + if line is not None: + self.class_output += [line] + line = 'public {0} {1} '.format( + self.class_type, self.builder_class_name) + if self.base_class_name is not None: + line += 'extends {0} '.format( + get_generated_class_name(self.base_class_name)) + line += '{' + self.class_output += [line] + + def _add_size_getter(self): + size_attribute = get_attribute_property_equal( + self.schema, self.schema['SizePrefixedEntity']['layout'], 'name', 'size') + return_type = get_builtin_type(size_attribute['size']) + new_getter = JavaMethodGenerator( + 'public', return_type, 'getSize', []) + if self.base_class_name is not None: + new_getter.add_annotation('@Override') + self._calculate_size(new_getter) + self._add_method(new_getter) + + def generate(self): + self._add_class_definition() + self._add_private_declarations() + self._add_public_declarations() + self._add_size_getter() + self._add_load_from_binary_method() + self._add_serialize_method() + return self.class_output + ['}'] diff --git a/generators/java/JavaMethodGenerator.py b/generators/java/JavaMethodGenerator.py index d7d8c4f8..3d5929cc 100755 --- a/generators/java/JavaMethodGenerator.py +++ b/generators/java/JavaMethodGenerator.py @@ -4,15 +4,18 @@ class JavaMethodGenerator: """Java method generator""" - def __init__(self, scope, return_type, name, params, exception_list='', static=False): + def __init__(self, scope, return_type, name, params, exception_list='', static_method=False): # pylint: disable-msg=too-many-arguments self.name = name - if static: - self.method_output = ['{0} static {1} {2}({3}) {4} {{'.format( - scope, return_type, self.name, ', '.join(params), exception_list)] - else: - self.method_output = ['{0} {1} {2}({3}) {4} {{'.format( - scope, return_type, self.name, ', '.join(params), exception_list)] + line = '{0} static'.format( + scope) if static_method else '{0}'.format(scope) + if return_type: + line += ' {0}'.format(return_type) + line += ' {0}({1})'.format(self.name, ', '.join(params)) + if exception_list: + line += ' {0}'.format(exception_list) + line += ' {' + self.method_output = [line] def add_instructions(self, instructions, add_semicolon=True): for instruction in instructions: @@ -22,3 +25,6 @@ def add_instructions(self, instructions, add_semicolon=True): def get_method(self): return self.method_output + ['}'] + + def add_annotation(self, annotation): + self.method_output.insert(0, annotation) From 60997a0778dde503e77468c2983043bdc4f23f04 Mon Sep 17 00:00:00 2001 From: WayonB Date: Tue, 23 Apr 2019 01:38:20 -0400 Subject: [PATCH 06/11] fix pylint issues --- generators/java/JavaDefineTypeClassGenerator.py | 12 ++++++------ generators/java/JavaEnumGenerator.py | 8 ++------ generators/java/JavaGeneratorBase.py | 14 +++++++------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/generators/java/JavaDefineTypeClassGenerator.py b/generators/java/JavaDefineTypeClassGenerator.py index 4fa4147e..6f3529d5 100644 --- a/generators/java/JavaDefineTypeClassGenerator.py +++ b/generators/java/JavaDefineTypeClassGenerator.py @@ -13,7 +13,7 @@ def __init__(self, name, schema, class_schema, enum_list): name, schema, class_schema, enum_list) def _add_public_declarations(self): - self._add_constructor(self.class_schema) + self._add_constructor() self._add_constructor_stream() self._add_getter_setter(self.class_schema) @@ -29,9 +29,9 @@ def _calculate_size(self, new_getter): new_getter.add_instructions( ['return {0}'.format(self.class_schema['size'])]) - def _add_constructor(self, attribute): - attribute_name = attribute['name'] - return_type = get_generated_type(self.schema, attribute) + def _add_constructor(self): + attribute_name = self.class_schema['name'] + return_type = get_generated_type(self.schema, self.class_schema) new_setter = JavaMethodGenerator('public', '', self.builder_class_name, [return_type + ' ' + attribute_name]) @@ -43,8 +43,8 @@ def _add_constructor(self, attribute): AttributeKind.CUSTOM: self._add_simple_setter } - attribute_kind = get_attribute_kind(attribute) - setters[attribute_kind](attribute, new_setter) + attribute_kind = get_attribute_kind(self.class_schema) + setters[attribute_kind](self.class_schema, new_setter) self._add_method(new_setter) def _add_constructor_stream(self): diff --git a/generators/java/JavaEnumGenerator.py b/generators/java/JavaEnumGenerator.py index e5c7de20..2058d086 100755 --- a/generators/java/JavaEnumGenerator.py +++ b/generators/java/JavaEnumGenerator.py @@ -104,9 +104,5 @@ def _calculate_size(self, new_getter): def generate(self): self._add_class_definition() self._write_enum_values() - self._add_private_declarations() - self._add_public_declarations() - self._add_size_getter() - self._add_load_from_binary_method() - self._add_serialize_method() - return self.class_output + ['}'] + self._generate_class_methods() + return self.class_output diff --git a/generators/java/JavaGeneratorBase.py b/generators/java/JavaGeneratorBase.py index c795bc8b..a0cce12d 100644 --- a/generators/java/JavaGeneratorBase.py +++ b/generators/java/JavaGeneratorBase.py @@ -16,10 +16,6 @@ def __init__(self, name, schema, class_schema): self.class_schema = class_schema self.class_type = None - @abstractmethod - def _add_class_definition(self): - raise NotImplementedError('need to override method') - @abstractmethod def _add_public_declarations(self): raise NotImplementedError('need to override method') @@ -99,11 +95,15 @@ def _add_size_getter(self): self._calculate_size(new_getter) self._add_method(new_getter) - def generate(self): - self._add_class_definition() + def _generate_class_methods(self): self._add_private_declarations() self._add_public_declarations() self._add_size_getter() self._add_load_from_binary_method() self._add_serialize_method() - return self.class_output + ['}'] + self.class_output += ['}'] + + def generate(self): + self._add_class_definition() + self._generate_class_methods() + return self.class_output From fa2cc5da05a793dfb7080c5d20759e659282fe4f Mon Sep 17 00:00:00 2001 From: WayonB Date: Tue, 23 Apr 2019 02:02:44 -0400 Subject: [PATCH 07/11] Fix variable name differ than in base --- generators/java/JavaClassGenerator.py | 8 ++++---- generators/java/JavaDefineTypeClassGenerator.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/generators/java/JavaClassGenerator.py b/generators/java/JavaClassGenerator.py index cc47cefc..d9a79243 100644 --- a/generators/java/JavaClassGenerator.py +++ b/generators/java/JavaClassGenerator.py @@ -373,15 +373,15 @@ def _add_load_from_binary_custom(self, load_from_binary_method): load_from_binary_method.add_instructions( ['return new {0}(stream)'.format(self.builder_class_name)]) - def _add_serialize_custom(self, add_serialize_method): + def _add_serialize_custom(self, serialize_method): if self.base_class_name is not None: - add_serialize_method.add_instructions( + serialize_method.add_instructions( ['byte[] superBytes = super.serialize()']) - add_serialize_method.add_instructions( + serialize_method.add_instructions( ['dataOutputStream.write(superBytes, 0, superBytes.length)']) self._recurse_foreach_attribute(self.class_schema['layout'], self._generate_serialize_attributes, - add_serialize_method) + serialize_method) def _add_constructor_stream(self): load_stream_constructor = JavaMethodGenerator( diff --git a/generators/java/JavaDefineTypeClassGenerator.py b/generators/java/JavaDefineTypeClassGenerator.py index 6f3529d5..64784f5f 100644 --- a/generators/java/JavaDefineTypeClassGenerator.py +++ b/generators/java/JavaDefineTypeClassGenerator.py @@ -21,9 +21,9 @@ def _add_private_declarations(self): self._add_private_declaration(self.class_schema) self.class_output += [''] - def _add_serialize_custom(self, add_serialize_method): + def _add_serialize_custom(self, serialize_method): self._generate_serialize_attributes( - self.class_schema, add_serialize_method) + self.class_schema, serialize_method) def _calculate_size(self, new_getter): new_getter.add_instructions( From 2801b41bd9cc6196c42ea648a1adf638fe9386b2 Mon Sep 17 00:00:00 2001 From: WayonB Date: Tue, 30 Apr 2019 10:13:12 -0400 Subject: [PATCH 08/11] Address some style issues by adding documentation and final where needed --- generators/java/JavaClassGenerator.py | 254 ++++++++++++------ .../java/JavaDefineTypeClassGenerator.py | 19 +- generators/java/JavaEnumGenerator.py | 5 +- generators/java/JavaGeneratorBase.py | 45 +++- generators/java/JavaMethodGenerator.py | 10 +- 5 files changed, 234 insertions(+), 99 deletions(-) diff --git a/generators/java/JavaClassGenerator.py b/generators/java/JavaClassGenerator.py index d9a79243..9399861d 100644 --- a/generators/java/JavaClassGenerator.py +++ b/generators/java/JavaClassGenerator.py @@ -1,10 +1,10 @@ -from .Helpers import get_generated_class_name, get_builtin_type, indent, get_attribute_size -from .Helpers import get_read_method_name, get_reverse_method_name, get_write_method_name -from .Helpers import get_generated_type, get_attribute_property_equal, AttributeKind, is_byte_type from .Helpers import get_attribute_kind, TypeDescriptorDisposition, get_attribute_if_size from .Helpers import get_comments_if_present, is_builtin_type -from .JavaMethodGenerator import JavaMethodGenerator +from .Helpers import get_generated_class_name, get_builtin_type, indent, get_attribute_size +from .Helpers import get_generated_type, get_attribute_property_equal, AttributeKind, is_byte_type +from .Helpers import get_read_method_name, get_reverse_method_name, get_write_method_name from .JavaGeneratorBase import JavaGeneratorBase +from .JavaMethodGenerator import JavaMethodGenerator def capitalize_first_character(string): @@ -24,41 +24,47 @@ def __init__(self, name, schema, class_schema, enum_list): self._foreach_attributes( self.class_schema['layout'], self._find_base_callback) + @staticmethod + def _is_inline_class(attribute): + return 'disposition' in attribute and attribute[ + 'disposition'] == TypeDescriptorDisposition.Inline.value + def _find_base_callback(self, attribute): - if ('disposition' in attribute and - attribute['disposition'] == TypeDescriptorDisposition.Inline.value and + if (self._is_inline_class(attribute) and self.should_generate_class(attribute['type'])): self.base_class_name = attribute['type'] + self.finalized_class = True return True return False def _should_declaration(self, attribute): return not self.is_count_size_field(attribute) and attribute['name'] != 'size' + def _get_body_class_name(self): + body_name = self.name if not self.name.startswith('Embedded') else self.name[8:] + return '{0}Body'.format(body_name) + def _add_private_declarations(self): self._recurse_foreach_attribute( - self.class_schema['layout'], self._add_private_declaration) + self.name, self._add_private_declaration, self.class_output, + [self.base_class_name, self._get_body_class_name()]) self.class_output += [''] - def _add_private_declaration(self, attribute): + def _add_private_declaration(self, attribute, private_output): if not self.is_count_size_field(attribute): line = get_comments_if_present(attribute['comments']) if line is not None: - self.class_output += [indent(line)] + private_output += [indent(line)] attribute_name = attribute['name'] var_type = get_generated_type(self.schema, attribute) - scope = 'private' if attribute_name != 'size' else 'protected' - self.class_output += [ + scope = 'private final' if attribute_name != 'size' else 'protected' + private_output += [ indent('{0} {1} {2};'.format(scope, var_type, attribute_name))] @staticmethod def _get_generated_getter_name(attribute_name): return 'get{0}'.format(capitalize_first_character(attribute_name)) - @staticmethod - def _get_generated_setter_name(attribute_name): - return 'set{0}'.format(capitalize_first_character(attribute_name)) - @staticmethod def _add_simple_getter(attribute, new_getter): new_getter.add_instructions( @@ -87,24 +93,37 @@ def _add_method_condition(self, attribute, method_writer): attribute['condition'], condition_type_prefix, attribute['condition_value'].upper())], False) method_writer.add_instructions( - [indent('throw new java.lang.IllegalStateException()')]) + [indent('{throw new java.lang.IllegalStateException();}')], False) method_writer.add_instructions([''], False) - def _add_getter(self, attribute): + def _add_getter(self, attribute, schema): attribute_name = attribute['name'] - return_type = get_generated_type(self.schema, attribute) + return_type = get_generated_type(schema, attribute) new_getter = JavaMethodGenerator( 'public', return_type, self._get_generated_getter_name(attribute_name), []) - self._add_method_condition(attribute, new_getter) - - getters = { - AttributeKind.SIMPLE: self._add_simple_getter, - AttributeKind.BUFFER: self._add_buffer_getter, - AttributeKind.ARRAY: self._add_array_getter, - AttributeKind.CUSTOM: self._add_simple_getter - } - attribute_kind = get_attribute_kind(attribute) - getters[attribute_kind](attribute, new_getter) + + if 'aggregate_class' in attribute: + # This is just a pass through + new_getter.add_instructions( + ['return this.{0}.{1}()'.format( + self._get_name_from_type(attribute['aggregate_class']), + self._get_generated_getter_name(attribute_name))]) + else: + self._add_method_condition(attribute, new_getter) + getters = { + AttributeKind.SIMPLE: self._add_simple_getter, + AttributeKind.BUFFER: self._add_buffer_getter, + AttributeKind.ARRAY: self._add_array_getter, + AttributeKind.CUSTOM: self._add_simple_getter + } + attribute_kind = get_attribute_kind(attribute) + getters[attribute_kind](attribute, new_getter) + + # If the comments is empty then just use name in the description + description = attribute['comments'] if attribute[ + 'comments'].strip() else attribute_name + '.' + self._add_method_documentation(new_getter, 'Get {0}.'.format(description), [], + description, None) self._add_method(new_getter) @staticmethod @@ -123,15 +142,14 @@ def _add_buffer_setter(self, attribute, new_setter): new_setter.add_instructions( ['if ({0} == null)'.format(attribute_name)], False) new_setter.add_instructions( - [indent('throw new NullPointerException("{0}")'.format(attribute_name))]) - new_setter.add_instructions([''], False) + [indent('{{throw new NullPointerException("{0}");}}'.format(attribute_name))], False) if not isinstance(attribute_size, str): new_setter.add_instructions( ['if ({0}.array().length != {1})'.format(attribute_name, attribute_size)], False) new_setter.add_instructions( - [indent('throw new IllegalArgumentException("{0} should be {1} bytes")' - .format(attribute_name, attribute_size))]) - new_setter.add_instructions([''], False) + [indent('{{throw new IllegalArgumentException("{0} should be {1} bytes");}}' + .format(attribute_name, attribute_size))], False) + new_setter.add_instructions([''], False) new_setter.add_instructions( ['this.{0} = {0}'.format(attribute_name)]) @@ -157,27 +175,46 @@ def _calculate_size(self, new_getter): return_type = get_builtin_type(size_attribute['size']) if self.base_class_name is not None: size_list.append('super.getSize();') - self._recurse_foreach_attribute(self.class_schema['layout'], - self._add_size_value, size_list) - new_getter.add_instructions( - ['{0} size = {1}'.format(return_type, size_list[0])], False) - for size in size_list[1:]: - new_getter.add_instructions(['size += {0}'.format(size)], False) + self._recurse_foreach_attribute(self.name, + self._add_size_value, size_list, + [self.base_class_name, self._get_body_class_name()]) + if size_list is not None: + new_getter.add_instructions( + ['{0} size = {1}'.format(return_type, size_list[0])], False) + for size in size_list[1:]: + new_getter.add_instructions(['size += {0}'.format(size)], False) new_getter.add_instructions(['return size']) - def _add_getter_setter(self, attribute): + def _add_getters(self, attribute, schema): if self._should_declaration(attribute): - self._add_getter(attribute) + self._add_getter(attribute, schema) + + @staticmethod + def _get_name_from_type(type_name): + return type_name[0].lower() + type_name[1:] + + def _recurse_foreach_attribute(self, class_name, callback, + context, ignore_inline_class): + class_generated = (class_name != self.name and self.should_generate_class(class_name)) + for attribute in self.schema[class_name]['layout']: + if class_generated: + attribute['aggregate_class'] = class_name - def _recurse_foreach_attribute(self, attributes, callback, - context=None, ignore_base_class=True): - for attribute in attributes: if 'disposition' in attribute: + inline_class = attribute['type'] if attribute['disposition'] == TypeDescriptorDisposition.Inline.value: - if ignore_base_class and self.base_class_name == attribute['type']: - continue - self._recurse_foreach_attribute( - self.schema[attribute['type']]['layout'], callback, context) + if self.should_generate_class(inline_class): + # Class was grenerated so it can be declare aggregate + attribute['name'] = self._get_name_from_type(inline_class) + if (self.base_class_name == inline_class and + self.base_class_name in ignore_inline_class): + continue # skip the base class + if inline_class in ignore_inline_class: + callback(attribute, context) + continue + + self._recurse_foreach_attribute(inline_class, + callback, context, ignore_inline_class) elif attribute['disposition'] == TypeDescriptorDisposition.Const.value: # add dynamic enum if present in this class enum_name = attribute['type'] @@ -186,11 +223,9 @@ def _recurse_foreach_attribute(self, attributes, callback, self.builder_class_name, attribute['value'], attribute['comments']) + continue else: - if context is None: - callback(attribute) - else: - callback(attribute, context) + callback(attribute, context) def _add_attribute_condition_if_needed(self, attribute, method_writer, obj_prefix): if 'condition' in attribute: @@ -303,7 +338,10 @@ def _serialize_attribute_buffer(self, attribute, serialize_method): ]) @staticmethod - def _serialize_attribute_array(attribute, serialize_method): + def _get_serialize_name(attribute_name): + return '{0}Bytes'.format(attribute_name) + + def _serialize_attribute_array(self, attribute, serialize_method): attribute_typename = attribute['type'] attribute_size = attribute['size'] attribute_name = attribute['name'] @@ -316,21 +354,25 @@ def _serialize_attribute_array(attribute, serialize_method): 'dataOutputStream.{0}(this.{1}.get(i))'.format(get_write_method_name(1), attribute_name))]) else: + attribute_bytes_name = self._get_serialize_name(attribute_name) serialize_method.add_instructions([indent( - 'byte[] ser = this.{0}.get(i).serialize()'.format(attribute_name))]) + 'final byte[] {0} = this.{1}.get(i).serialize()'.format(attribute_bytes_name, + attribute_name))]) serialize_method.add_instructions([indent( - 'dataOutputStream.{0}(ser, 0, ser.length)'.format( - get_write_method_name(attribute_size)))]) + 'dataOutputStream.{0}({1}, 0, {1}.length)'.format( + get_write_method_name(attribute_size), attribute_bytes_name))]) serialize_method.add_instructions(['}'], False) def _serialize_attribute_custom(self, attribute, serialize_method): + attribute_name = attribute['name'] + attribute_bytes_name = self._get_serialize_name(attribute_name) serialize_method.add_instructions([ - 'byte[] {0} = this.{1}().serialize()' - .format(attribute['name'], self._get_generated_getter_name(attribute['name'])) + 'final byte[] {0} = this.{1}.serialize()' + .format(attribute_bytes_name, attribute_name) ]) serialize_method.add_instructions([ 'dataOutputStream.write({0}, 0, {0}.length)'.format( - attribute['name']) + attribute_bytes_name) ]) def _generate_serialize_attributes(self, attribute, serialize_method): @@ -341,7 +383,8 @@ def _generate_serialize_attributes(self, attribute, serialize_method): 'Count') else '.array().length' full_property_name = '({0}){1}'.format( get_builtin_type(size), 'this.' + - get_attribute_if_size(attribute['name'], self.class_schema['layout'], + get_attribute_if_size(attribute['name'], + self.class_schema['layout'], self.schema) + size_extension) reverse_byte_method = get_reverse_method_name( size).format(full_property_name) @@ -359,15 +402,15 @@ def _generate_serialize_attributes(self, attribute, serialize_method): attribute_kind = get_attribute_kind(attribute) serialize_attribute[attribute_kind](attribute, serialize_method) - def _add_getter_setter_field(self): + def _add_getters_field(self): self._recurse_foreach_attribute( - self.class_schema['layout'], self._add_getter_setter) + self.name, self._add_getters, self.schema, [self.base_class_name]) def _add_public_declarations(self): self._add_constructor() self._add_constructor_stream() self._add_factory_method() - self._add_getter_setter_field() + self._add_getters_field() def _add_load_from_binary_custom(self, load_from_binary_method): load_from_binary_method.add_instructions( @@ -376,23 +419,28 @@ def _add_load_from_binary_custom(self, load_from_binary_method): def _add_serialize_custom(self, serialize_method): if self.base_class_name is not None: serialize_method.add_instructions( - ['byte[] superBytes = super.serialize()']) + ['final byte[] superBytes = super.serialize()']) serialize_method.add_instructions( ['dataOutputStream.write(superBytes, 0, superBytes.length)']) - self._recurse_foreach_attribute(self.class_schema['layout'], + self._recurse_foreach_attribute(self.name, self._generate_serialize_attributes, - serialize_method) + serialize_method, + [self.base_class_name, self._get_body_class_name()]) def _add_constructor_stream(self): load_stream_constructor = JavaMethodGenerator( 'protected', '', self.builder_class_name, - ['DataInput stream'], 'throws Exception') + ['final DataInput stream'], 'throws Exception') if self.base_class_name is not None: load_stream_constructor.add_instructions(['super(stream)']) - self._recurse_foreach_attribute(self.class_schema['layout'], + self._recurse_foreach_attribute(self.name, self._generate_load_from_binary_attributes, - load_stream_constructor) - + load_stream_constructor, + [self.base_class_name, self._get_body_class_name()]) + self._add_method_documentation(load_stream_constructor, + 'Constructor - Create object of a stream.', + [('stream', 'Byte stream to use to serialize the object.')], + None, 'Exception failed to deserialize from stream.') self._add_method(load_stream_constructor) def _add_to_variable(self, attribute, param_list): @@ -403,20 +451,31 @@ def _add_to_param(self, attribute, param_list): if self._should_declaration(attribute): attribute_name = attribute['name'] attribute_type = get_generated_type(self.schema, attribute) - param_list.append('{0} {1}'.format(attribute_type, attribute_name)) + param_list.append('final {0} {1}'.format(attribute_type, attribute_name)) - def _create_list(self, attributes, callback, ignore_base_class=False): + def _create_list(self, name, callback): param_list = [] - self._recurse_foreach_attribute(attributes, + self._recurse_foreach_attribute(name, callback, - param_list, ignore_base_class) + param_list, []) param_string = param_list[0] for param in param_list[1:]: param_string += ', {0}'.format(param) return param_string def _create_param_list(self): - return self._create_list(self.class_schema['layout'], self._add_to_param) + return self._create_list(self.name, self._add_to_param) + + def _add_name_comment(self, attribute, context): + if self._should_declaration(attribute): + context.append((attribute['name'], attribute['comments'])) + + def _create_name_comment_list(self, name): + name_comment_list = [] + self._recurse_foreach_attribute(name, + self._add_name_comment, + name_comment_list, []) + return name_comment_list @staticmethod def _add_attribute_to_list(attribute, attribute_list): @@ -429,27 +488,46 @@ def _add_constructor(self): if self.base_class_name is not None: constructor_method.add_instructions(['super({0})'.format( - self._create_list(self.schema[self.base_class_name]['layout'], + self._create_list(self.base_class_name, self._add_to_variable))]) - constructor_method.add_instructions([''], False) + constructor_method.add_instructions([''], False) object_attributes = [] - self._recurse_foreach_attribute(self.class_schema['layout'], + self._recurse_foreach_attribute(self.name, self._add_attribute_to_list, - object_attributes) + object_attributes, + [self.base_class_name, self._get_body_class_name()]) + add_space = False for attribute in object_attributes: + if self._is_inline_class(attribute): + continue if 'size' not in attribute or not is_builtin_type(attribute['type'], attribute['size']): attribute_name = attribute['name'] constructor_method.add_instructions( ['if ({0} == null)'.format(attribute_name)], False) constructor_method.add_instructions( - [indent('throw new NullPointerException("{0}")'.format(attribute_name))]) - constructor_method.add_instructions([''], False) + [indent('{{throw new NullPointerException("{0} cannot be null.");}}'.format( + attribute_name))], + False) + add_space = True + + if add_space: + constructor_method.add_instructions([''], False) for variable in object_attributes: if self._should_declaration(variable): - constructor_method.add_instructions( - ['this.{0} = {0}'.format(variable['name'])]) + if self._is_inline_class(variable): + constructor_method.add_instructions(['this.{0} = new {1}({2})'.format( + variable['name'], get_generated_class_name(variable['type']), + self._create_list(variable['type'], + self._add_to_variable))]) + else: + constructor_method.add_instructions( + ['this.{0} = {0}'.format(variable['name'])]) + + self._add_method_documentation(constructor_method, 'Constructor.', + self._create_name_comment_list(self.name), + None, 'Exception invalid parameters.') self._add_method(constructor_method) @@ -459,7 +537,12 @@ def _add_factory_method(self): [self._create_param_list()], 'throws Exception', True) factory.add_instructions(['return new {0}({1})'.format( self.builder_class_name, self._create_list( - self.class_schema['layout'], self._add_to_variable))]) + self.name, self._add_to_variable))]) + self._add_method_documentation(factory, + 'Create an instance of {0}.'.format(self.builder_class_name), + self._create_name_comment_list(self.name), + 'An instance of {0}.'.format(self.builder_class_name), + 'Exception Invalid parameters.') self._add_method(factory) @staticmethod @@ -468,4 +551,5 @@ def should_generate_class(name): or name.endswith('Transaction') or name.startswith('Mosaic') or name.endswith('Mosaic') - or name.endswith('Modification')) + or name.endswith('Modification') + or (name.endswith('Body') and name != 'EntityBody')) diff --git a/generators/java/JavaDefineTypeClassGenerator.py b/generators/java/JavaDefineTypeClassGenerator.py index 64784f5f..614ec9cc 100644 --- a/generators/java/JavaDefineTypeClassGenerator.py +++ b/generators/java/JavaDefineTypeClassGenerator.py @@ -11,14 +11,15 @@ def __init__(self, name, schema, class_schema, enum_list): class_schema['name'] = name[0].lower() + name[1:] super(JavaDefineTypeClassGenerator, self).__init__( name, schema, class_schema, enum_list) + self.finalized_class = True def _add_public_declarations(self): self._add_constructor() self._add_constructor_stream() - self._add_getter_setter(self.class_schema) + self._add_getters(self.class_schema, self.schema) def _add_private_declarations(self): - self._add_private_declaration(self.class_schema) + self._add_private_declaration(self.class_schema, self.class_output) self.class_output += [''] def _add_serialize_custom(self, serialize_method): @@ -31,10 +32,10 @@ def _calculate_size(self, new_getter): def _add_constructor(self): attribute_name = self.class_schema['name'] - return_type = get_generated_type(self.schema, self.class_schema) + param_type = get_generated_type(self.schema, self.class_schema) new_setter = JavaMethodGenerator('public', '', self.builder_class_name, - [return_type + ' ' + attribute_name]) + [param_type + ' ' + attribute_name]) setters = { AttributeKind.SIMPLE: self._add_simple_setter, @@ -45,12 +46,20 @@ def _add_constructor(self): attribute_kind = get_attribute_kind(self.class_schema) setters[attribute_kind](self.class_schema, new_setter) + self._add_method_documentation(new_setter, 'Constructor.', + [(attribute_name, self.class_schema['comments'])], + None, None) + self._add_method(new_setter) def _add_constructor_stream(self): load_stream_constructor = JavaMethodGenerator( 'public', '', self.builder_class_name, - ['DataInput stream'], 'throws Exception') + ['final DataInput stream'], 'throws Exception') self._generate_load_from_binary_attributes( self.class_schema, load_stream_constructor) + self._add_method_documentation(load_stream_constructor, + 'Constructor - Create object of a stream.', + [('stream', 'Byte stream to use to serialize.')], + None, 'Exception failed to deserialize.') self._add_method(load_stream_constructor) diff --git a/generators/java/JavaEnumGenerator.py b/generators/java/JavaEnumGenerator.py index 2058d086..04e0e85f 100755 --- a/generators/java/JavaEnumGenerator.py +++ b/generators/java/JavaEnumGenerator.py @@ -54,8 +54,11 @@ def _write_enum_values(self): def _add_constructor(self): enum_type = get_type(self.class_schema) constructor_method = JavaMethodGenerator('private', '', self.builder_class_name, [ - '{0} value'.format(enum_type)]) + 'final {0} value'.format(enum_type)]) constructor_method.add_instructions(['this.value = value']) + self._add_method_documentation(constructor_method, 'Constructor.', + [('value', self.class_schema['comments'])], None, None) + self._add_method(constructor_method) def _add_load_from_binary_custom(self, load_from_binary_method): diff --git a/generators/java/JavaGeneratorBase.py b/generators/java/JavaGeneratorBase.py index a0cce12d..c06f27d4 100644 --- a/generators/java/JavaGeneratorBase.py +++ b/generators/java/JavaGeneratorBase.py @@ -1,20 +1,24 @@ # pylint: disable=too-few-public-methods from abc import ABC, abstractmethod -from .Helpers import get_generated_class_name, get_comments_if_present, indent, get_builtin_type + from .Helpers import get_attribute_property_equal +from .Helpers import get_generated_class_name, get_comments_if_present, indent, get_builtin_type from .JavaMethodGenerator import JavaMethodGenerator class JavaGeneratorBase(ABC): def __init__(self, name, schema, class_schema): + # pylint: disable=too-many-instance-attributes self.builder_class_name = get_generated_class_name(name) + self.name = name self.base_class_name = None self.class_output = [] self.schema = schema self.privates = [] self.class_schema = class_schema self.class_type = None + self.finalized_class = False @abstractmethod def _add_public_declarations(self): @@ -55,28 +59,40 @@ def _add_method(self, method, add_empty_line=True): def _add_load_from_binary_method(self): load_from_binary_method = JavaMethodGenerator( 'public', self.builder_class_name, 'loadFromBinary', - ['DataInput stream'], 'throws Exception', True) + ['final DataInput stream'], 'throws Exception', True) self._add_load_from_binary_custom(load_from_binary_method) + self._add_method_documentation(load_from_binary_method, + 'loadFromBinary - Create an instance of {0} from a stream.' + .format(self.builder_class_name), + [('stream', 'Byte stream to use to serialize the object.')], + 'An instance of {0}.'.format( + self.builder_class_name), + 'Exception failed to deserialize from stream.') self._add_method(load_from_binary_method) def _add_serialize_method(self): serialize_method = JavaMethodGenerator( 'public', 'byte[]', 'serialize', [], 'throws Exception') serialize_method.add_instructions( - ['ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()']) + ['final ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream()']) serialize_method.add_instructions( - ['DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream)']) + ['final DataOutputStream dataOutputStream = new DataOutputStream(byteArrayStream)']) self._add_serialize_custom(serialize_method) serialize_method.add_instructions(['dataOutputStream.close()']) serialize_method.add_instructions( - ['return byteArrayOutputStream.toByteArray()']) + ['return byteArrayStream.toByteArray()']) + self._add_method_documentation(serialize_method, + 'Serialize the object to bytes.', + [], 'Serialized bytes.', 'Exception failed to serialize.') self._add_method(serialize_method, False) def _add_class_definition(self): line = get_comments_if_present(self.class_schema['comments']) if line is not None: self.class_output += [line] - line = 'public {0} {1} '.format( + + line = 'final ' if self.finalized_class or self.name.endswith('Body') else '' + line += 'public {0} {1} '.format( self.class_type, self.builder_class_name) if self.base_class_name is not None: line += 'extends {0} '.format( @@ -93,8 +109,25 @@ def _add_size_getter(self): if self.base_class_name is not None: new_getter.add_annotation('@Override') self._calculate_size(new_getter) + self._add_method_documentation(new_getter, + 'Get the size of the object.', + [], 'Size in bytes.', None) self._add_method(new_getter) + @staticmethod + def _add_method_documentation(method_writer, method_description, param_list, + return_description, exception): + method_writer.add_documentations(['/**']) + method_writer.add_documentations([' * {0}'.format(method_description)]) + if param_list is not None: + for name, description in param_list: + method_writer.add_documentations([' * @param {0} {1}'.format(name, description)]) + if return_description is not None: + method_writer.add_documentations([' * @return {0}'.format(return_description)]) + if exception is not None: + method_writer.add_documentations([' * @throws {0}'.format(exception)]) + method_writer.add_documentations([' */']) + def _generate_class_methods(self): self._add_private_declarations() self._add_public_declarations() diff --git a/generators/java/JavaMethodGenerator.py b/generators/java/JavaMethodGenerator.py index 3d5929cc..30cb6094 100755 --- a/generators/java/JavaMethodGenerator.py +++ b/generators/java/JavaMethodGenerator.py @@ -16,6 +16,8 @@ def __init__(self, scope, return_type, name, params, exception_list='', static_m line += ' {0}'.format(exception_list) line += ' {' self.method_output = [line] + self.annotation_output = [] + self.documentation_output = [] def add_instructions(self, instructions, add_semicolon=True): for instruction in instructions: @@ -24,7 +26,11 @@ def add_instructions(self, instructions, add_semicolon=True): self.method_output.append(indent(instruction)) def get_method(self): - return self.method_output + ['}'] + return self.documentation_output + self.annotation_output + self.method_output + ['}'] def add_annotation(self, annotation): - self.method_output.insert(0, annotation) + self.annotation_output.append(annotation) + + def add_documentations(self, documentations): + for documentation in documentations: + self.documentation_output.append(documentation) From afa70d4b698eacdb73ca59de83dfa9a5f0c3e56c Mon Sep 17 00:00:00 2001 From: WayonB Date: Tue, 30 Apr 2019 10:18:17 -0400 Subject: [PATCH 09/11] fix pylint issue --- generators/java/JavaGeneratorBase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generators/java/JavaGeneratorBase.py b/generators/java/JavaGeneratorBase.py index c06f27d4..cd0c08a8 100644 --- a/generators/java/JavaGeneratorBase.py +++ b/generators/java/JavaGeneratorBase.py @@ -8,8 +8,8 @@ class JavaGeneratorBase(ABC): + # pylint: disable=too-many-instance-attributes def __init__(self, name, schema, class_schema): - # pylint: disable=too-many-instance-attributes self.builder_class_name = get_generated_class_name(name) self.name = name self.base_class_name = None From b5c03d58966f3317fd63ced8358fd0b11315947d Mon Sep 17 00:00:00 2001 From: WayonB Date: Mon, 6 May 2019 07:07:39 -0400 Subject: [PATCH 10/11] Fix class with conditional variable gets generated. Plus some styles --- generators/java/Helpers.py | 50 ++- generators/java/JavaClassGenerator.py | 321 +++++++++++------- .../java/JavaDefineTypeClassGenerator.py | 4 +- generators/java/JavaEnumGenerator.py | 55 +-- generators/java/JavaFileGenerator.py | 24 +- generators/java/JavaGeneratorBase.py | 38 ++- 6 files changed, 310 insertions(+), 182 deletions(-) diff --git a/generators/java/Helpers.py b/generators/java/Helpers.py index 82653580..45d948ef 100755 --- a/generators/java/Helpers.py +++ b/generators/java/Helpers.py @@ -86,11 +86,11 @@ def get_attribute_if_size(attribute_name, attributes, schema): return value['name'] if value is not None else None -def get_attribute_property_equal(schema, attributes, attribute_name, attribute_value): +def get_attribute_property_equal(schema, attributes, attribute_name, attribute_value, recurse=True): for attribute in attributes: if attribute_name in attribute and attribute[attribute_name] == attribute_value: return attribute - if ('disposition' in attribute and + if (recurse and 'disposition' in attribute and attribute['disposition'] == TypeDescriptorDisposition.Inline.value): value = get_attribute_property_equal( schema, schema[attribute['type']]['layout'], attribute_name, attribute_value) @@ -110,9 +110,8 @@ def get_read_method_name(size): if isinstance(size, str) or size > 8: method_name = 'readFully' else: - typesize_methodname = {1: 'readByte', - 2: 'readShort', 4: 'readInt', 8: 'readLong'} - method_name = typesize_methodname[size] + type_size_method_name = {1: 'readByte', 2: 'readShort', 4: 'readInt', 8: 'readLong'} + method_name = type_size_method_name[size] return method_name @@ -150,12 +149,49 @@ def get_generated_type(schema, attribute): if attribute_kind == AttributeKind.BUFFER: return 'ByteBuffer' if attribute_kind == AttributeKind.ARRAY: - return 'java.util.ArrayList<{0}>'.format(typename) + return 'ArrayList<{0}>'.format(typename) return typename +def get_import_for_type(data_type): + actual_type = data_type.split('<')[0] if '<' in data_type else data_type + + type_import = { + 'ByteBuffer': 'java.nio.ByteBuffer', + 'ArrayList': 'java.util.ArrayList', + } + return type_import[actual_type] if actual_type in type_import.keys() else None + + +def append_period_if_needed(line): + return line if line.endswith('.') else line + '.' + + +def get_comment_from_name(name): + return name[0].upper() + ''.join(' ' + x.lower() if x.isupper() else x for x in name[1:]) + + def get_comments_if_present(comment): if comment: - return '/** {0} */'.format(comment) + return '/** {0} */'.format(append_period_if_needed(comment)) return None + + +def get_comments_from_attribute(attribute, formatted=True): + comment = attribute['comments'].strip() if 'comments' in attribute else '' + if not comment: + comment = get_comment_from_name(attribute['name']) + return get_comments_if_present(comment) if formatted else comment + + +def create_enum_name(name): + enum_name = name[0] + ''.join('_' + x if x.isupper() else x for x in name[1:]) + return enum_name.upper() + + +def get_default_value(attribute): + attribute_kind = get_attribute_kind(attribute) + if attribute_kind == AttributeKind.SIMPLE: + return '0' + return 'null' diff --git a/generators/java/JavaClassGenerator.py b/generators/java/JavaClassGenerator.py index 9399861d..e52758ee 100644 --- a/generators/java/JavaClassGenerator.py +++ b/generators/java/JavaClassGenerator.py @@ -1,8 +1,9 @@ +from .Helpers import create_enum_name, get_default_value from .Helpers import get_attribute_kind, TypeDescriptorDisposition, get_attribute_if_size -from .Helpers import get_comments_if_present, is_builtin_type from .Helpers import get_generated_class_name, get_builtin_type, indent, get_attribute_size from .Helpers import get_generated_type, get_attribute_property_equal, AttributeKind, is_byte_type from .Helpers import get_read_method_name, get_reverse_method_name, get_write_method_name +from .Helpers import is_builtin_type, get_comments_from_attribute, get_import_for_type from .JavaGeneratorBase import JavaGeneratorBase from .JavaMethodGenerator import JavaMethodGenerator @@ -19,10 +20,15 @@ def __init__(self, name, schema, class_schema, enum_list): self.enum_list = enum_list self.class_type = 'class' + self.condition_list = [] if 'layout' in self.class_schema: + # Find base class self._foreach_attributes( self.class_schema['layout'], self._find_base_callback) + # Find any condition variables + self._recurse_foreach_attribute( + self.name, self._create_condition_list, self.condition_list, []) @staticmethod def _is_inline_class(attribute): @@ -37,6 +43,14 @@ def _find_base_callback(self, attribute): return True return False + @staticmethod + def _is_conditional_attribute(attribute): + return 'condition' in attribute + + def _create_condition_list(self, attribute, condition_list): + if self._is_conditional_attribute(attribute): + condition_list.append(attribute) + def _should_declaration(self, attribute): return not self.is_count_size_field(attribute) and attribute['name'] != 'size' @@ -50,14 +64,21 @@ def _add_private_declarations(self): [self.base_class_name, self._get_body_class_name()]) self.class_output += [''] + def _add_required_import_if_needed(self, var_type): + import_string = get_import_for_type(var_type) + if import_string: + self._add_required_import(import_string) + def _add_private_declaration(self, attribute, private_output): if not self.is_count_size_field(attribute): - line = get_comments_if_present(attribute['comments']) + line = get_comments_from_attribute(attribute) if line is not None: private_output += [indent(line)] attribute_name = attribute['name'] var_type = get_generated_type(self.schema, attribute) - scope = 'private final' if attribute_name != 'size' else 'protected' + self._add_required_import_if_needed(var_type) + scope = 'private final' if (attribute_name != 'size' + and 'condition' not in attribute) else 'private' private_output += [ indent('{0} {1} {2};'.format(scope, var_type, attribute_name))] @@ -75,30 +96,33 @@ def _add_buffer_getter(attribute, new_getter): new_getter.add_instructions( ['return this.{0}'.format(attribute['name'])]) - def _add_array_getter(self, attribute, new_getter): - return_type = get_generated_type(self.schema, attribute) - new_getter.add_instructions( - ['return ({0})this.{1}'.format(return_type, attribute['name'])]) + # pylint: disable-msg=too-many-arguments + def _add_if_condition_for_variable_if_needed(self, attribute, writer, object_prefix, + if_condition, code_lines, add_semicolon=True): + condition_type_attribute = get_attribute_property_equal( + self.schema, self.class_schema['layout'], 'name', attribute['condition']) + condition_type = '{0}.{1}'.format( + get_generated_class_name(condition_type_attribute['type']), + create_enum_name(attribute['condition_value'])) + + writer.add_instructions(['if ({0}{1} {2} {3}) {{'.format( + object_prefix, attribute['condition'], if_condition, condition_type)], False) + for line in code_lines: + writer.add_instructions([indent(line)], add_semicolon) + writer.add_instructions(['}'], False) def _add_method_condition(self, attribute, method_writer): if 'condition' in attribute: - condition_type_attribute = get_attribute_property_equal( - self.schema, self.class_schema['layout'], 'name', attribute['condition']) - condition_type_prefix = '' - if condition_type_attribute is not None: - condition_type_prefix = '{0}.'.format( - get_generated_class_name(condition_type_attribute['type'])) - - method_writer.add_instructions(['if ({0} != {1}{2})'.format( - attribute['condition'], condition_type_prefix, - attribute['condition_value'].upper())], False) - method_writer.add_instructions( - [indent('{throw new java.lang.IllegalStateException();}')], False) - method_writer.add_instructions([''], False) + code_lines = [ + 'throw new java.lang.IllegalStateException("{0} is not set to {1}.")'.format( + attribute['condition'], create_enum_name(attribute['condition_value']))] + self._add_if_condition_for_variable_if_needed(attribute, method_writer, 'this.', '!=', + code_lines) def _add_getter(self, attribute, schema): attribute_name = attribute['name'] return_type = get_generated_type(schema, attribute) + self._add_required_import_if_needed(return_type) new_getter = JavaMethodGenerator( 'public', return_type, self._get_generated_getter_name(attribute_name), []) @@ -113,15 +137,14 @@ def _add_getter(self, attribute, schema): getters = { AttributeKind.SIMPLE: self._add_simple_getter, AttributeKind.BUFFER: self._add_buffer_getter, - AttributeKind.ARRAY: self._add_array_getter, + AttributeKind.ARRAY: self._add_simple_getter, AttributeKind.CUSTOM: self._add_simple_getter } attribute_kind = get_attribute_kind(attribute) getters[attribute_kind](attribute, new_getter) # If the comments is empty then just use name in the description - description = attribute['comments'] if attribute[ - 'comments'].strip() else attribute_name + '.' + description = get_comments_from_attribute(attribute, False) self._add_method_documentation(new_getter, 'Get {0}.'.format(description), [], description, None) self._add_method(new_getter) @@ -140,49 +163,48 @@ def _add_buffer_setter(self, attribute, new_setter): attribute_size = get_attribute_size(self.schema, attribute) attribute_name = attribute['name'] new_setter.add_instructions( - ['if ({0} == null)'.format(attribute_name)], False) + ['if ({0} == null) {{'.format(attribute_name)], False) new_setter.add_instructions( - [indent('{{throw new NullPointerException("{0}");}}'.format(attribute_name))], False) + [indent('throw new NullPointerException("{0}")'.format(attribute_name))]) + new_setter.add_instructions(['}'], False) + if not isinstance(attribute_size, str): new_setter.add_instructions( - ['if ({0}.array().length != {1})'.format(attribute_name, attribute_size)], False) + ['if ({0}.array().length != {1}) {{'.format(attribute_name, attribute_size)], False) new_setter.add_instructions( - [indent('{{throw new IllegalArgumentException("{0} should be {1} bytes");}}' - .format(attribute_name, attribute_size))], False) - new_setter.add_instructions([''], False) + [indent('throw new IllegalArgumentException("{0} should be {1} bytes")' + .format(attribute_name, attribute_size))]) + new_setter.add_instructions(['}'], False) new_setter.add_instructions( ['this.{0} = {0}'.format(attribute_name)]) - @staticmethod - def _add_size_value(attribute, size_list): + def _add_size_value(self, attribute, method_writer): kind = get_attribute_kind(attribute) + line = 'size += ' if kind == AttributeKind.SIMPLE: - size_list.append( - '{0}; // {1}'.format(attribute['size'], attribute['name'])) + line += '{0}; // {1}'.format(attribute['size'], attribute['name']) elif kind == AttributeKind.BUFFER: - size_list.append( - 'this.{0}.array().length;'.format(attribute['name'])) + line += 'this.{0}.array().length;'.format(attribute['name']) elif kind == AttributeKind.ARRAY: - size_list.append('this.{0}.stream().mapToInt(o -> o.getSize()).sum();'. - format(attribute['name'])) + line += 'this.{0}.stream().mapToInt(o -> o.getSize()).sum();'.format(attribute['name']) else: - size_list.append('this.{0}.getSize();'.format(attribute['name'])) + line += 'this.{0}.getSize();'.format(attribute['name']) + + self._add_attribute_condition_if_needed( + attribute, method_writer, 'this.', [line], False) def _calculate_size(self, new_getter): - size_list = [] size_attribute = get_attribute_property_equal( self.schema, self.schema['SizePrefixedEntity']['layout'], 'name', 'size') return_type = get_builtin_type(size_attribute['size']) if self.base_class_name is not None: - size_list.append('super.getSize();') + new_getter.add_instructions(['{0} size = super.getSize()'.format(return_type)]) + else: + new_getter.add_instructions(['{0} size = 0'.format(return_type)]) self._recurse_foreach_attribute(self.name, - self._add_size_value, size_list, + self._add_size_value, new_getter, [self.base_class_name, self._get_body_class_name()]) - if size_list is not None: - new_getter.add_instructions( - ['{0} size = {1}'.format(return_type, size_list[0])], False) - for size in size_list[1:]: - new_getter.add_instructions(['size += {0}'.format(size)], False) + new_getter.add_instructions(['return size']) def _add_getters(self, attribute, schema): @@ -227,32 +249,30 @@ def _recurse_foreach_attribute(self, class_name, callback, else: callback(attribute, context) - def _add_attribute_condition_if_needed(self, attribute, method_writer, obj_prefix): + def _init_other_attribute_in_condition(self, attribute, obj_prefix, code_lines): if 'condition' in attribute: - condition_type_attribute = get_attribute_property_equal( - self.schema, self.class_schema, 'name', attribute['condition']) - condition_type_prefix = '' - if condition_type_attribute is not None: - condition_type_prefix = '{0}.'.format( - get_generated_class_name(condition_type_attribute['type'])) - - method_writer.add_instructions(['if ({0}{1}() == {2}{3})'.format( - obj_prefix, self._get_generated_getter_name( - attribute['condition']), - condition_type_prefix, attribute['condition_value'].upper())], False) - return True - return False + for condition_attribute in self.condition_list: + if attribute['name'] != condition_attribute['name']: + code_lines.append('{0}{1} = {2}'.format(obj_prefix, condition_attribute['name'], + get_default_value(attribute))) + + def _add_attribute_condition_if_needed(self, attribute, method_writer, obj_prefix, code_lines, + add_semicolon=True): + if 'condition' in attribute: + self._add_if_condition_for_variable_if_needed(attribute, method_writer, obj_prefix, + '==', code_lines, add_semicolon) + else: + method_writer.add_instructions(code_lines, add_semicolon) def _load_from_binary_simple(self, attribute, load_from_binary_method): - indent_required = self._add_attribute_condition_if_needed( - attribute, load_from_binary_method, 'this.') size = get_attribute_size(self.schema, attribute) read_method_name = 'stream.{0}()'.format(get_read_method_name(size)) reverse_byte_method = get_reverse_method_name( size).format(read_method_name) - line = 'this.{0} = {1}'.format(attribute['name'], reverse_byte_method) - load_from_binary_method.add_instructions( - [indent(line) if indent_required else line]) + lines = ['this.{0} = {1}'.format(attribute['name'], reverse_byte_method)] + self._init_other_attribute_in_condition(attribute, 'this.', lines) + self._add_attribute_condition_if_needed( + attribute, load_from_binary_method, 'this.', lines) def _load_from_binary_buffer(self, attribute, load_from_binary_method): attribute_name = attribute['name'] @@ -284,12 +304,12 @@ def _load_from_binary_array(attribute, load_from_binary_method): attribute_name, get_generated_class_name(attribute_typename)))]) load_from_binary_method.add_instructions(['}'], False) - @staticmethod - def _load_from_binary_custom(attribute, load_from_binary_method): - load_from_binary_method.add_instructions([ - 'this.{0} = {1}.loadFromBinary(stream)' - .format(attribute['name'], get_generated_class_name(attribute['type'])) - ]) + def _load_from_binary_custom(self, attribute, load_from_binary_method): + lines = ['this.{0} = {1}.loadFromBinary(stream)'.format( + attribute['name'], get_generated_class_name(attribute['type']))] + self._init_other_attribute_in_condition(attribute, 'this.', lines) + self._add_attribute_condition_if_needed( + attribute, load_from_binary_method, 'this.', lines) @staticmethod def is_count_size_field(field): @@ -319,15 +339,12 @@ def _generate_load_from_binary_attributes(self, attribute, load_from_binary_meth load_attribute[attribute_kind](attribute, load_from_binary_method) def _serialize_attribute_simple(self, attribute, serialize_method): - indent_required = self._add_attribute_condition_if_needed(attribute, - serialize_method, 'this.') size = get_attribute_size(self.schema, attribute) reverse_byte_method = get_reverse_method_name(size).format( 'this.' + self._get_generated_getter_name(attribute['name'] + '()')) line = 'dataOutputStream.{0}({1})'.format( get_write_method_name(size), reverse_byte_method) - serialize_method.add_instructions( - [indent(line) if indent_required else line]) + self._add_attribute_condition_if_needed(attribute, serialize_method, 'this.', [line]) def _serialize_attribute_buffer(self, attribute, serialize_method): attribute_name = attribute['name'] @@ -366,14 +383,11 @@ def _serialize_attribute_array(self, attribute, serialize_method): def _serialize_attribute_custom(self, attribute, serialize_method): attribute_name = attribute['name'] attribute_bytes_name = self._get_serialize_name(attribute_name) - serialize_method.add_instructions([ - 'final byte[] {0} = this.{1}.serialize()' - .format(attribute_bytes_name, attribute_name) - ]) - serialize_method.add_instructions([ - 'dataOutputStream.write({0}, 0, {0}.length)'.format( - attribute_bytes_name) - ]) + lines = [ + 'final byte[] {0} = this.{1}.serialize()'.format(attribute_bytes_name, attribute_name)] + lines += ['dataOutputStream.write({0}, 0, {0}.length)'.format(attribute_bytes_name)] + self._add_attribute_condition_if_needed(attribute, + serialize_method, 'this.', lines) def _generate_serialize_attributes(self, attribute, serialize_method): attribute_name = attribute['name'] @@ -381,11 +395,11 @@ def _generate_serialize_attributes(self, attribute, serialize_method): size = get_attribute_size(self.schema, attribute) size_extension = '.size()' if attribute_name.endswith( 'Count') else '.array().length' - full_property_name = '({0}){1}'.format( - get_builtin_type(size), 'this.' + - get_attribute_if_size(attribute['name'], - self.class_schema['layout'], - self.schema) + size_extension) + full_property_name = '({0}) {1}'.format( + get_builtin_type(size), + 'this.' + get_attribute_if_size(attribute['name'], + self.class_schema['layout'], + self.schema) + size_extension) reverse_byte_method = get_reverse_method_name( size).format(full_property_name) line = 'dataOutputStream.{0}({1})'.format( @@ -407,9 +421,13 @@ def _add_getters_field(self): self.name, self._add_getters, self.schema, [self.base_class_name]) def _add_public_declarations(self): - self._add_constructor() self._add_constructor_stream() - self._add_factory_method() + if self.condition_list: + self._add_constructors() + self._add_factory_methods() + else: + self._add_constructor() + self._add_factory_method() self._add_getters_field() def _add_load_from_binary_custom(self, load_from_binary_method): @@ -438,113 +456,156 @@ def _add_constructor_stream(self): load_stream_constructor, [self.base_class_name, self._get_body_class_name()]) self._add_method_documentation(load_stream_constructor, - 'Constructor - Create object of a stream.', + 'Constructor - Create object from stream.', [('stream', 'Byte stream to use to serialize the object.')], None, 'Exception failed to deserialize from stream.') self._add_method(load_stream_constructor) - def _add_to_variable(self, attribute, param_list): - if self._should_declaration(attribute): - param_list.append('{0}'.format(attribute['name'])) + def _add_to_variable(self, attribute, context): + param_list, condition_attribute = context + attribute_name = attribute['name'] + if (self._should_declaration(attribute) and + self._should_add_base_on_condition(attribute, condition_attribute)): + param_list.append(attribute_name) - def _add_to_param(self, attribute, param_list): - if self._should_declaration(attribute): + @staticmethod + def _should_add_base_on_condition(attribute, condition_attribute): + attribute_name = attribute['name'] + if condition_attribute is not None: + if 'condition' in attribute: + if (condition_attribute['condition'] == attribute['condition'] and + condition_attribute['name'] != attribute_name): + return False # Skip all other conditions attribute + elif condition_attribute['condition'] == attribute_name: + return False + return True + + def _add_to_param(self, attribute, context): + param_list, condition_attribute = context + if (self._should_declaration(attribute) and + self._should_add_base_on_condition(attribute, condition_attribute)): attribute_name = attribute['name'] attribute_type = get_generated_type(self.schema, attribute) param_list.append('final {0} {1}'.format(attribute_type, attribute_name)) - def _create_list(self, name, callback): + def _create_list(self, name, callback, condition_attribute): param_list = [] self._recurse_foreach_attribute(name, callback, - param_list, []) + (param_list, condition_attribute), []) param_string = param_list[0] for param in param_list[1:]: param_string += ', {0}'.format(param) return param_string - def _create_param_list(self): - return self._create_list(self.name, self._add_to_param) + def _create_param_list(self, condition_attribute): + return self._create_list(self.name, self._add_to_param, condition_attribute) def _add_name_comment(self, attribute, context): - if self._should_declaration(attribute): - context.append((attribute['name'], attribute['comments'])) + comment_list, condition_attribute = context + if (self._should_declaration(attribute) and + self._should_add_base_on_condition(attribute, condition_attribute)): + comment_list.append((attribute['name'], attribute['comments'])) - def _create_name_comment_list(self, name): + def _create_name_comment_list(self, name, condition_variable): name_comment_list = [] self._recurse_foreach_attribute(name, self._add_name_comment, - name_comment_list, []) + (name_comment_list, condition_variable), []) return name_comment_list - @staticmethod - def _add_attribute_to_list(attribute, attribute_list): - attribute_list.append(attribute) + def _add_attribute_to_list(self, attribute, context): + attribute_list, condition_attribute = context + if self._should_add_base_on_condition(attribute, condition_attribute): + attribute_list.append(attribute) def _add_constructor(self): + self._add_constructor_internal(None) + + def _add_constructor_internal(self, condition_attribute): constructor_method = JavaMethodGenerator( 'protected', '', self.builder_class_name, - [self._create_param_list()], 'throws Exception') - + [self._create_param_list(condition_attribute)], None) if self.base_class_name is not None: constructor_method.add_instructions(['super({0})'.format( self._create_list(self.base_class_name, - self._add_to_variable))]) - constructor_method.add_instructions([''], False) + self._add_to_variable, condition_attribute))]) object_attributes = [] self._recurse_foreach_attribute(self.name, self._add_attribute_to_list, - object_attributes, + (object_attributes, condition_attribute), [self.base_class_name, self._get_body_class_name()]) - add_space = False for attribute in object_attributes: if self._is_inline_class(attribute): continue if 'size' not in attribute or not is_builtin_type(attribute['type'], attribute['size']): attribute_name = attribute['name'] constructor_method.add_instructions( - ['if ({0} == null)'.format(attribute_name)], False) + ['if ({0} == null) {{'.format(attribute_name)], False) constructor_method.add_instructions( - [indent('{{throw new NullPointerException("{0} cannot be null.");}}'.format( - attribute_name))], - False) - add_space = True - - if add_space: - constructor_method.add_instructions([''], False) + [indent('throw new NullPointerException("{0} cannot be null.")'.format( + attribute_name))]) + constructor_method.add_instructions(['}'], False) for variable in object_attributes: if self._should_declaration(variable): if self._is_inline_class(variable): - constructor_method.add_instructions(['this.{0} = new {1}({2})'.format( + constructor_method.add_instructions(['this.{0} = {1}.create({2})'.format( variable['name'], get_generated_class_name(variable['type']), self._create_list(variable['type'], - self._add_to_variable))]) + self._add_to_variable, condition_attribute))]) else: constructor_method.add_instructions( ['this.{0} = {0}'.format(variable['name'])]) + if condition_attribute: + condition_type_attribute = get_attribute_property_equal( + self.schema, self.class_schema['layout'], 'name', condition_attribute[ + 'condition'], False) + if condition_type_attribute: + condition_type_value = '{0}.{1}'.format( + get_generated_class_name(condition_type_attribute['type']), + create_enum_name(condition_attribute['condition_value'])) + constructor_method.add_instructions( + ['this.{0} = {1}'.format(condition_attribute['condition'], + condition_type_value)]) + code_lines = [] + self._init_other_attribute_in_condition(condition_attribute, 'this.', code_lines) + constructor_method.add_instructions(code_lines) + self._add_method_documentation(constructor_method, 'Constructor.', - self._create_name_comment_list(self.name), - None, 'Exception invalid parameters.') + self._create_name_comment_list(self.name, + condition_attribute), + None, None) self._add_method(constructor_method) def _add_factory_method(self): + self._add_factory_method_internal(None) + + def _add_factory_method_internal(self, condition_attribute): factory = JavaMethodGenerator( 'public', self.builder_class_name, 'create', - [self._create_param_list()], 'throws Exception', True) + [self._create_param_list(condition_attribute)], '', True) factory.add_instructions(['return new {0}({1})'.format( self.builder_class_name, self._create_list( - self.name, self._add_to_variable))]) + self.name, self._add_to_variable, condition_attribute))]) self._add_method_documentation(factory, 'Create an instance of {0}.'.format(self.builder_class_name), - self._create_name_comment_list(self.name), - 'An instance of {0}.'.format(self.builder_class_name), - 'Exception Invalid parameters.') + self._create_name_comment_list(self.name, + condition_attribute), + 'An instance of {0}.'.format(self.builder_class_name), '') self._add_method(factory) + def _add_constructors(self): + for attribute in self.condition_list: + self._add_constructor_internal(attribute) + + def _add_factory_methods(self): + for attribute in self.condition_list: + self._add_factory_method_internal(attribute) + @staticmethod def should_generate_class(name): return (name.startswith('Embedded') diff --git a/generators/java/JavaDefineTypeClassGenerator.py b/generators/java/JavaDefineTypeClassGenerator.py index 614ec9cc..46eececb 100644 --- a/generators/java/JavaDefineTypeClassGenerator.py +++ b/generators/java/JavaDefineTypeClassGenerator.py @@ -1,7 +1,7 @@ # pylint: disable=too-few-public-methods from .Helpers import get_generated_type, AttributeKind, get_attribute_kind -from .JavaMethodGenerator import JavaMethodGenerator from .JavaClassGenerator import JavaClassGenerator +from .JavaMethodGenerator import JavaMethodGenerator class JavaDefineTypeClassGenerator(JavaClassGenerator): @@ -35,7 +35,7 @@ def _add_constructor(self): param_type = get_generated_type(self.schema, self.class_schema) new_setter = JavaMethodGenerator('public', '', self.builder_class_name, - [param_type + ' ' + attribute_name]) + ['final ' + param_type + ' ' + attribute_name]) setters = { AttributeKind.SIMPLE: self._add_simple_setter, diff --git a/generators/java/JavaEnumGenerator.py b/generators/java/JavaEnumGenerator.py index 04e0e85f..347360ad 100755 --- a/generators/java/JavaEnumGenerator.py +++ b/generators/java/JavaEnumGenerator.py @@ -1,18 +1,14 @@ from .Helpers import get_builtin_type, indent, get_attribute_size +from .Helpers import get_comments_if_present, create_enum_name from .Helpers import get_read_method_name, get_reverse_method_name, get_write_method_name -from .Helpers import get_comments_if_present -from .JavaMethodGenerator import JavaMethodGenerator from .JavaGeneratorBase import JavaGeneratorBase +from .JavaMethodGenerator import JavaMethodGenerator def get_type(attribute): return get_builtin_type(attribute['size']) -def create_enum_name(name): - return name[0] + ''.join('_' + x if x.isupper() else x for x in name[1:]) - - class JavaEnumGenerator(JavaGeneratorBase): """Java enum generator""" @@ -25,8 +21,8 @@ def __init__(self, name, schema, class_schema): def _add_private_declaration(self): var_type = get_type(self.class_schema) - self.class_output += [ - indent('private final {0} value;'.format(var_type))] + [''] + self.class_output += [indent(get_comments_if_present('Enum value.'))] + self.class_output += [indent('private final {0} value;'.format(var_type))] + [''] def _add_enum_values(self, enum_attribute): enum_attribute_values = enum_attribute['values'] @@ -53,7 +49,7 @@ def _write_enum_values(self): def _add_constructor(self): enum_type = get_type(self.class_schema) - constructor_method = JavaMethodGenerator('private', '', self.builder_class_name, [ + constructor_method = JavaMethodGenerator('', '', self.builder_class_name, [ 'final {0} value'.format(enum_type)]) constructor_method.add_instructions(['this.value = value']) self._add_method_documentation(constructor_method, 'Constructor.', @@ -68,18 +64,9 @@ def _add_load_from_binary_custom(self, load_from_binary_method): reverse_byte_method = get_reverse_method_name( size).format(read_data_line) load_from_binary_method.add_instructions( - ['{0} streamValue = {1}'.format(get_type(self.class_schema), reverse_byte_method)]) - load_from_binary_method.add_instructions( - ['for ({0} current : {0}.values()) {{'.format(self.builder_class_name)], False) - load_from_binary_method.add_instructions( - [indent('if (streamValue == current.value)')], False) - load_from_binary_method.add_instructions( - [indent('return current', 2)]) - load_from_binary_method.add_instructions( - ['}'], False) - load_from_binary_method.add_instructions( - ['throw new RuntimeException(streamValue + " was not a backing value for {0}.")' - .format(self.builder_class_name)]) + ['final {0} streamValue = {1}'.format( + get_type(self.class_schema), reverse_byte_method)]) + load_from_binary_method.add_instructions(['return rawValueOf(streamValue)']) def _add_serialize_custom(self, serialize_method): size = get_attribute_size(self.schema, self.class_schema) @@ -94,7 +81,7 @@ def add_enum_value(self, name, value, comments): self.enum_values[create_enum_name(name)] = [value, comments] def _add_public_declarations(self): - pass + self._add_raw_value_of_method() def _add_private_declarations(self): self._add_private_declaration() @@ -104,6 +91,30 @@ def _calculate_size(self, new_getter): new_getter.add_instructions( ['return {0}'.format(self.class_schema['size'])]) + def _add_raw_value_of_method(self): + enum_type = get_type(self.class_schema) + new_method = JavaMethodGenerator( + 'public', self.builder_class_name, 'rawValueOf', + ['final {0} value'.format(enum_type)], '', True) + new_method.add_instructions( + ['for ({0} current : {0}.values()) {{'.format(self.builder_class_name)], False) + new_method.add_instructions( + [indent('if (value == current.value) {')], False) + new_method.add_instructions( + [indent('return current', 2)]) + new_method.add_instructions( + [indent('}')], False) + new_method.add_instructions( + ['}'], False) + new_method.add_instructions( + ['throw new IllegalArgumentException(value + " was not a backing value for {0}.")' + .format(self.builder_class_name)]) + self._add_method_documentation(new_method, 'Get enum value.', + [('value', 'The raw value of the enum')], + 'enum value', None) + + self._add_method(new_method) + def generate(self): self._add_class_definition() self._write_enum_values() diff --git a/generators/java/JavaFileGenerator.py b/generators/java/JavaFileGenerator.py index 1b347c9c..215e02d4 100755 --- a/generators/java/JavaFileGenerator.py +++ b/generators/java/JavaFileGenerator.py @@ -1,9 +1,11 @@ import os + from generators.Descriptor import Descriptor + from .Helpers import is_byte_type, is_enum_type, is_struct_type, get_generated_class_name -from .JavaEnumGenerator import JavaEnumGenerator from .JavaClassGenerator import JavaClassGenerator from .JavaDefineTypeClassGenerator import JavaDefineTypeClassGenerator +from .JavaEnumGenerator import JavaEnumGenerator class JavaFileGenerator: @@ -31,14 +33,19 @@ def prepend_copyright(self, copyright_file): self.code = [line.strip() for line in header] def set_import(self): - self.code += ['import java.lang.*;'] - self.code += ['import java.io.*;'] - self.code += ['import java.nio.*;'] - self.code += ['import io.nem.catapult.builders.*;'] + [''] + self.code += ['import java.io.ByteArrayOutputStream;'] + self.code += ['import java.io.DataInput;'] + self.code += ['import java.io.DataOutputStream;'] def set_package(self): self.code += ['package io.nem.catapult.builders;'] + [''] + def update_code(self, generator): + generated_class = generator.generate() + for import_type in generator.get_required_import(): + self.code += ['import {0};'.format(import_type)] + self.code += [''] + generated_class + def generate(self): for type_descriptor, value in self.schema.items(): self.code = [] @@ -51,18 +58,17 @@ def generate(self): new_class = JavaDefineTypeClassGenerator( type_descriptor, self.schema, value, JavaFileGenerator.enum_class_list) - self.code += new_class.generate() + self.update_code(new_class) yield self.code, get_generated_class_name(type_descriptor) elif is_enum_type(attribute_type): JavaFileGenerator.enum_class_list[type_descriptor] = JavaEnumGenerator( type_descriptor, self.schema, value) elif is_struct_type(attribute_type): - # skip all the inline classes if JavaClassGenerator.should_generate_class(type_descriptor): new_class = JavaClassGenerator( type_descriptor, self.schema, value, JavaFileGenerator.enum_class_list) - self.code += new_class.generate() + self.update_code(new_class) yield self.code, get_generated_class_name(type_descriptor) # write all the enum last just in case there are 'dynamic values' @@ -71,5 +77,5 @@ def generate(self): self.prepend_copyright(self.options['copyright']) self.set_package() self.set_import() - self.code += enum_class.generate() + self.code += [''] + enum_class.generate() yield self.code, get_generated_class_name(type_descriptor) diff --git a/generators/java/JavaGeneratorBase.py b/generators/java/JavaGeneratorBase.py index cd0c08a8..6b4cd16d 100644 --- a/generators/java/JavaGeneratorBase.py +++ b/generators/java/JavaGeneratorBase.py @@ -1,8 +1,8 @@ # pylint: disable=too-few-public-methods from abc import ABC, abstractmethod -from .Helpers import get_attribute_property_equal -from .Helpers import get_generated_class_name, get_comments_if_present, indent, get_builtin_type +from .Helpers import get_attribute_property_equal, append_period_if_needed +from .Helpers import get_generated_class_name, get_comments_from_attribute, indent, get_builtin_type from .JavaMethodGenerator import JavaMethodGenerator @@ -19,6 +19,7 @@ def __init__(self, name, schema, class_schema): self.class_schema = class_schema self.class_type = None self.finalized_class = False + self.required_import = set() @abstractmethod def _add_public_declarations(self): @@ -87,12 +88,13 @@ def _add_serialize_method(self): self._add_method(serialize_method, False) def _add_class_definition(self): - line = get_comments_if_present(self.class_schema['comments']) + line = get_comments_from_attribute(self.class_schema) if line is not None: self.class_output += [line] - line = 'final ' if self.finalized_class or self.name.endswith('Body') else '' - line += 'public {0} {1} '.format( + line = 'public ' + line += 'final ' if self.finalized_class or self.name.endswith('Body') else '' + line += '{0} {1} '.format( self.class_type, self.builder_class_name) if self.base_class_name is not None: line += 'extends {0} '.format( @@ -118,16 +120,28 @@ def _add_size_getter(self): def _add_method_documentation(method_writer, method_description, param_list, return_description, exception): method_writer.add_documentations(['/**']) - method_writer.add_documentations([' * {0}'.format(method_description)]) - if param_list is not None: + method_writer.add_documentations([' * {0}'.format( + append_period_if_needed(method_description))]) + method_writer.add_documentations([' *']) + if param_list: for name, description in param_list: - method_writer.add_documentations([' * @param {0} {1}'.format(name, description)]) - if return_description is not None: - method_writer.add_documentations([' * @return {0}'.format(return_description)]) - if exception is not None: - method_writer.add_documentations([' * @throws {0}'.format(exception)]) + method_writer.add_documentations([' * @param {0} {1}'.format( + name, append_period_if_needed(description))]) + if return_description: + method_writer.add_documentations([' * @return {0}'.format( + append_period_if_needed(return_description))]) + if exception: + method_writer.add_documentations([' * @throws {0}'.format( + append_period_if_needed(exception))]) method_writer.add_documentations([' */']) + def _add_required_import(self, full_class): + if full_class not in self.required_import: + self.required_import.add(full_class) + + def get_required_import(self): + return self.required_import + def _generate_class_methods(self): self._add_private_declarations() self._add_public_declarations() From 3723551f68616eae7e33e41d98da9a692dfe9a7a Mon Sep 17 00:00:00 2001 From: Anton Wiedermann Date: Mon, 6 May 2019 21:07:16 +0200 Subject: [PATCH 11/11] customized package for proxima and minor project setup tweaks --- .gitignore | 3 +++ generators/java/JavaFileGenerator.py | 2 +- scripts/generate_all.sh | 0 3 files changed, 4 insertions(+), 1 deletion(-) mode change 100644 => 100755 scripts/generate_all.sh diff --git a/.gitignore b/.gitignore index 6e9c13fc..1909cce7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ __pycache__/ .idea .vscode/ _generated/ +/.project +/.pydevproject +/generate_all.sh diff --git a/generators/java/JavaFileGenerator.py b/generators/java/JavaFileGenerator.py index 215e02d4..36683900 100755 --- a/generators/java/JavaFileGenerator.py +++ b/generators/java/JavaFileGenerator.py @@ -38,7 +38,7 @@ def set_import(self): self.code += ['import java.io.DataOutputStream;'] def set_package(self): - self.code += ['package io.nem.catapult.builders;'] + [''] + self.code += ['package io.proximax.transport.builders;'] + [''] def update_code(self, generator): generated_class = generator.generate() diff --git a/scripts/generate_all.sh b/scripts/generate_all.sh old mode 100644 new mode 100755