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/.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/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..45d948ef --- /dev/null +++ b/generators/java/Helpers.py @@ -0,0 +1,197 @@ +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' + + +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 + CUSTOM = 4 + UNKNOWN = 100 + + +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'])): + attr = schema[attribute['type']] + if 'size' in attr: + return attr['size'] + return 1 + + return attribute['size'] + + +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: + return AttributeKind.CUSTOM + + 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, 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, recurse=True): + for attribute in attributes: + if attribute_name in attribute and attribute[attribute_name] == attribute_value: + return attribute + 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) + 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: + type_size_method_name = {1: 'readByte', 2: 'readShort', 4: 'readInt', 8: 'readLong'} + method_name = type_size_method_name[size] + return method_name + + +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})', + 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'} + method_name = typesize_methodname[size] + return method_name + + +def get_generated_type(schema, attribute): + typename = attribute['type'] + attribute_kind = get_attribute_kind(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)) + if attribute_kind == AttributeKind.BUFFER: + return 'ByteBuffer' + if attribute_kind == AttributeKind.ARRAY: + 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(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 new file mode 100644 index 00000000..e52758ee --- /dev/null +++ b/generators/java/JavaClassGenerator.py @@ -0,0 +1,616 @@ +from .Helpers import create_enum_name, get_default_value +from .Helpers import get_attribute_kind, TypeDescriptorDisposition, get_attribute_if_size +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 + + +def capitalize_first_character(string): + return string[0].upper() + string[1:] + + +class JavaClassGenerator(JavaGeneratorBase): + """Java class generator""" + + def __init__(self, name, schema, class_schema, enum_list): + super(JavaClassGenerator, self).__init__(name, schema, class_schema) + + 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): + return 'disposition' in attribute and attribute[ + 'disposition'] == TypeDescriptorDisposition.Inline.value + + def _find_base_callback(self, attribute): + 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 + + @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' + + 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.name, self._add_private_declaration, self.class_output, + [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_from_attribute(attribute) + if line is not None: + private_output += [indent(line)] + attribute_name = attribute['name'] + var_type = get_generated_type(self.schema, attribute) + 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))] + + @staticmethod + def _get_generated_getter_name(attribute_name): + return 'get{0}'.format(capitalize_first_character(attribute_name)) + + @staticmethod + def _add_simple_getter(attribute, new_getter): + new_getter.add_instructions( + ['return this.{0}'.format(attribute['name'])]) + + @staticmethod + def _add_buffer_getter(attribute, new_getter): + new_getter.add_instructions( + ['return this.{0}'.format(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: + 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), []) + + 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_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 = get_comments_from_attribute(attribute, False) + self._add_method_documentation(new_getter, 'Get {0}.'.format(description), [], + description, None) + 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'] + 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_size_value(self, attribute, method_writer): + kind = get_attribute_kind(attribute) + line = 'size += ' + if kind == AttributeKind.SIMPLE: + line += '{0}; // {1}'.format(attribute['size'], attribute['name']) + elif kind == AttributeKind.BUFFER: + line += 'this.{0}.array().length;'.format(attribute['name']) + elif kind == AttributeKind.ARRAY: + line += 'this.{0}.stream().mapToInt(o -> o.getSize()).sum();'.format(attribute['name']) + else: + 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_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: + 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, new_getter, + [self.base_class_name, self._get_body_class_name()]) + + new_getter.add_instructions(['return size']) + + def _add_getters(self, attribute, schema): + if self._should_declaration(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 + + if 'disposition' in attribute: + inline_class = attribute['type'] + if attribute['disposition'] == TypeDescriptorDisposition.Inline.value: + 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'] + if enum_name in self.enum_list: + self.enum_list[enum_name].add_enum_value( + self.builder_class_name, + attribute['value'], + attribute['comments']) + continue + else: + callback(attribute, context) + + def _init_other_attribute_in_condition(self, attribute, obj_prefix, code_lines): + if 'condition' in attribute: + 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): + 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) + 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'] + attribute_size = get_attribute_size(self.schema, attribute) + 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) + ]) + + @staticmethod + def _load_from_binary_array(attribute, load_from_binary_method): + attribute_typename = attribute['type'] + attribute_sizename = attribute['size'] + attribute_name = attribute['name'] + 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): + load_from_binary_method.add_instructions([indent( + '{0}.add(stream.{1}())'.format(attribute_name, get_read_method_name(1)))]) + else: + load_from_binary_method.add_instructions([indent( + '{0}.add({1}.loadFromBinary(stream))'.format( + attribute_name, get_generated_class_name(attribute_typename)))]) + load_from_binary_method.add_instructions(['}'], False) + + 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): + 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 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) + load_from_binary_method.add_instructions([ + '{0} {1} = {2}'.format(get_generated_type(self.schema, attribute), + attribute_name, reverse_byte_method) + ]) + 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(attribute) + load_attribute[attribute_kind](attribute, load_from_binary_method) + + def _serialize_attribute_simple(self, attribute, serialize_method): + 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) + self._add_attribute_condition_if_needed(attribute, serialize_method, 'this.', [line]) + + def _serialize_attribute_buffer(self, attribute, serialize_method): + attribute_name = attribute['name'] + attribute_size = get_attribute_size(self.schema, attribute) + serialize_method.add_instructions([ + 'dataOutputStream.{0}(this.{1}.array(), 0, this.{1}.array().length)'.format( + get_write_method_name(attribute_size), attribute_name) + ]) + + @staticmethod + 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'] + serialize_method.add_instructions([ + 'for (int i = 0; i < this.{0}.size(); i++) {{'.format(attribute_name) + ], False) + + if is_byte_type(attribute_typename): + serialize_method.add_instructions([indent( + '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( + 'final byte[] {0} = this.{1}.get(i).serialize()'.format(attribute_bytes_name, + attribute_name))]) + serialize_method.add_instructions([indent( + '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) + 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'] + 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.' + 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( + get_write_method_name(size), reverse_byte_method) + 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(attribute) + serialize_attribute[attribute_kind](attribute, serialize_method) + + def _add_getters_field(self): + self._recurse_foreach_attribute( + self.name, self._add_getters, self.schema, [self.base_class_name]) + + def _add_public_declarations(self): + self._add_constructor_stream() + 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): + load_from_binary_method.add_instructions( + ['return new {0}(stream)'.format(self.builder_class_name)]) + + def _add_serialize_custom(self, serialize_method): + if self.base_class_name is not None: + serialize_method.add_instructions( + ['final byte[] superBytes = super.serialize()']) + serialize_method.add_instructions( + ['dataOutputStream.write(superBytes, 0, superBytes.length)']) + self._recurse_foreach_attribute(self.name, + self._generate_serialize_attributes, + 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, + ['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.name, + self._generate_load_from_binary_attributes, + load_stream_constructor, + [self.base_class_name, self._get_body_class_name()]) + self._add_method_documentation(load_stream_constructor, + '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, 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) + + @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, condition_attribute): + param_list = [] + self._recurse_foreach_attribute(name, + callback, + (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, condition_attribute): + return self._create_list(self.name, self._add_to_param, condition_attribute) + + def _add_name_comment(self, attribute, context): + 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, condition_variable): + name_comment_list = [] + self._recurse_foreach_attribute(name, + self._add_name_comment, + (name_comment_list, condition_variable), []) + return name_comment_list + + 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(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, condition_attribute))]) + + object_attributes = [] + self._recurse_foreach_attribute(self.name, + self._add_attribute_to_list, + (object_attributes, condition_attribute), + [self.base_class_name, self._get_body_class_name()]) + 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} 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} = {1}.create({2})'.format( + variable['name'], get_generated_class_name(variable['type']), + self._create_list(variable['type'], + 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, + 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(condition_attribute)], '', True) + factory.add_instructions(['return new {0}({1})'.format( + self.builder_class_name, self._create_list( + 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, + 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') + or name.endswith('Transaction') + or name.startswith('Mosaic') + or name.endswith('Mosaic') + or name.endswith('Modification') + or (name.endswith('Body') and name != 'EntityBody')) diff --git a/generators/java/JavaDefineTypeClassGenerator.py b/generators/java/JavaDefineTypeClassGenerator.py new file mode 100644 index 00000000..46eececb --- /dev/null +++ b/generators/java/JavaDefineTypeClassGenerator.py @@ -0,0 +1,65 @@ +# pylint: disable=too-few-public-methods +from .Helpers import get_generated_type, AttributeKind, get_attribute_kind +from .JavaClassGenerator import JavaClassGenerator +from .JavaMethodGenerator import JavaMethodGenerator + + +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) + self.finalized_class = True + + def _add_public_declarations(self): + self._add_constructor() + self._add_constructor_stream() + self._add_getters(self.class_schema, self.schema) + + def _add_private_declarations(self): + self._add_private_declaration(self.class_schema, self.class_output) + self.class_output += [''] + + def _add_serialize_custom(self, serialize_method): + self._generate_serialize_attributes( + self.class_schema, serialize_method) + + def _calculate_size(self, new_getter): + new_getter.add_instructions( + ['return {0}'.format(self.class_schema['size'])]) + + def _add_constructor(self): + attribute_name = self.class_schema['name'] + param_type = get_generated_type(self.schema, self.class_schema) + new_setter = JavaMethodGenerator('public', '', + self.builder_class_name, + ['final ' + param_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(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, + ['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 new file mode 100755 index 00000000..347360ad --- /dev/null +++ b/generators/java/JavaEnumGenerator.py @@ -0,0 +1,122 @@ +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 .JavaGeneratorBase import JavaGeneratorBase +from .JavaMethodGenerator import JavaMethodGenerator + + +def get_type(attribute): + return get_builtin_type(attribute['size']) + + +class JavaEnumGenerator(JavaGeneratorBase): + """Java enum generator""" + + 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.class_schema) + + def _add_private_declaration(self): + var_type = get_type(self.class_schema) + 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'] + for current_attribute in enum_attribute_values: + self.add_enum_value( + current_attribute['name'], + current_attribute['value'], + current_attribute['comments']) + + def _write_enum_values(self): + enum_type = get_type(self.class_schema) + enum_length = len(self.enum_values) + count = 1 + 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.class_output += [indent(line)] + count += 1 + self.class_output += [''] + + def _add_constructor(self): + enum_type = get_type(self.class_schema) + 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.', + [('value', self.class_schema['comments'])], None, None) + + self._add_method(constructor_method) + + 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(read_data_line) + load_from_binary_method.add_instructions( + ['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) + reverse_byte_method = get_reverse_method_name( + size).format('this.value') + serialize_method.add_instructions([ + 'dataOutputStream.{0}({1})'.format( + get_write_method_name(size), reverse_byte_method) + ]) + + def add_enum_value(self, name, value, comments): + self.enum_values[create_enum_name(name)] = [value, comments] + + def _add_public_declarations(self): + self._add_raw_value_of_method() + + def _add_private_declarations(self): + self._add_private_declaration() + self._add_constructor() + + 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() + self._generate_class_methods() + return self.class_output diff --git a/generators/java/JavaFileGenerator.py b/generators/java/JavaFileGenerator.py new file mode 100755 index 00000000..36683900 --- /dev/null +++ b/generators/java/JavaFileGenerator.py @@ -0,0 +1,81 @@ +import os + +from generators.Descriptor import Descriptor + +from .Helpers import is_byte_type, is_enum_type, is_struct_type, get_generated_class_name +from .JavaClassGenerator import JavaClassGenerator +from .JavaDefineTypeClassGenerator import JavaDefineTypeClassGenerator +from .JavaEnumGenerator import JavaEnumGenerator + + +class JavaFileGenerator: + """Java file generator""" + enum_class_list = {} + + 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.io.ByteArrayOutputStream;'] + self.code += ['import java.io.DataInput;'] + self.code += ['import java.io.DataOutputStream;'] + + def set_package(self): + self.code += ['package io.proximax.transport.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 = [] + self.prepend_copyright(self.options['copyright']) + self.set_package() + self.set_import() + attribute_type = value['type'] + + if is_byte_type(attribute_type): + new_class = JavaDefineTypeClassGenerator( + type_descriptor, self.schema, value, + JavaFileGenerator.enum_class_list) + 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): + if JavaClassGenerator.should_generate_class(type_descriptor): + new_class = JavaClassGenerator( + type_descriptor, self.schema, value, + JavaFileGenerator.enum_class_list) + 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' + 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) diff --git a/generators/java/JavaGeneratorBase.py b/generators/java/JavaGeneratorBase.py new file mode 100644 index 00000000..6b4cd16d --- /dev/null +++ b/generators/java/JavaGeneratorBase.py @@ -0,0 +1,156 @@ +# pylint: disable=too-few-public-methods +from abc import ABC, abstractmethod + +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 + + +class JavaGeneratorBase(ABC): + + # pylint: disable=too-many-instance-attributes + def __init__(self, name, schema, class_schema): + 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 + self.required_import = set() + + @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', + ['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( + ['final ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream()']) + serialize_method.add_instructions( + ['final DataOutputStream dataOutputStream = new DataOutputStream(byteArrayStream)']) + self._add_serialize_custom(serialize_method) + serialize_method.add_instructions(['dataOutputStream.close()']) + serialize_method.add_instructions( + ['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_from_attribute(self.class_schema) + if line is not None: + self.class_output += [line] + + 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( + 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_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( + 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, 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() + self._add_size_getter() + self._add_load_from_binary_method() + self._add_serialize_method() + self.class_output += ['}'] + + def generate(self): + self._add_class_definition() + self._generate_class_methods() + return self.class_output diff --git a/generators/java/JavaMethodGenerator.py b/generators/java/JavaMethodGenerator.py new file mode 100755 index 00000000..30cb6094 --- /dev/null +++ b/generators/java/JavaMethodGenerator.py @@ -0,0 +1,36 @@ +from .Helpers import indent + + +class JavaMethodGenerator: + """Java method generator""" + + def __init__(self, scope, return_type, name, params, exception_list='', static_method=False): + # pylint: disable-msg=too-many-arguments + self.name = name + 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] + self.annotation_output = [] + self.documentation_output = [] + + 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.documentation_output + self.annotation_output + self.method_output + ['}'] + + def add_annotation(self, annotation): + self.annotation_output.append(annotation) + + def add_documentations(self, documentations): + for documentation in documentations: + self.documentation_output.append(documentation) 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()); + } +} diff --git a/scripts/generate_all.sh b/scripts/generate_all.sh old mode 100644 new mode 100755