You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2022/01/20 10:25:26 UTC
[plc4x] branch develop updated: refactor(plc4j/codegen): move parser to model classes
This is an automated email from the ASF dual-hosted git repository.
sruehl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git
The following commit(s) were added to refs/heads/develop by this push:
new b08eb50 refactor(plc4j/codegen): move parser to model classes
b08eb50 is described below
commit b08eb50f458304448191bd7ad4a5a0c0392e191e
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Thu Jan 20 11:25:15 2022 +0100
refactor(plc4j/codegen): move parser to model classes
---
.../language/java/JavaLanguageTemplateHelper.java | 2 +-
.../templates/java/model-template.java.ftlh | 279 ++++++++++++++++++++-
2 files changed, 270 insertions(+), 11 deletions(-)
diff --git a/code-generation/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java b/code-generation/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
index 8699b4d..578b463 100644
--- a/code-generation/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
+++ b/code-generation/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
@@ -382,7 +382,7 @@ public class JavaLanguageTemplateHelper extends BaseFreemarkerLanguageTemplateHe
.append(toParseExpression(null, argumentType, paramTerm, null))
.append(")");
}
- return "new DataReaderComplexDefault<>(() -> " + parserCallString + "IO.staticParse(readBuffer" + paramsString + "), readBuffer)";
+ return "new DataReaderComplexDefault<>(() -> " + parserCallString + ".staticParse(readBuffer" + paramsString + "), readBuffer)";
} else {
throw new IllegalStateException("What is this type? " + typeReference);
}
diff --git a/code-generation/language-java/src/main/resources/templates/java/model-template.java.ftlh b/code-generation/language-java/src/main/resources/templates/java/model-template.java.ftlh
index 9be5806..8e16917 100644
--- a/code-generation/language-java/src/main/resources/templates/java/model-template.java.ftlh
+++ b/code-generation/language-java/src/main/resources/templates/java/model-template.java.ftlh
@@ -48,18 +48,17 @@ ${helper.packageName(protocolName, languageName, outputFlavor)?replace(".", "/")
package ${helper.packageName(protocolName, languageName, outputFlavor)};
import static org.apache.plc4x.java.spi.generation.StaticHelper.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*;
import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*;
+import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*;
import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
+import org.apache.plc4x.java.spi.codegen.*;
+import org.apache.plc4x.java.spi.codegen.io.*;
+import org.apache.plc4x.java.spi.codegen.fields.*;
+import org.apache.plc4x.java.api.exceptions.*;
+import org.apache.plc4x.java.spi.generation.*;
import org.apache.plc4x.java.api.value.*;
-import org.apache.plc4x.java.spi.codegen.io.DataWriterComplexDefault;
-import org.apache.plc4x.java.spi.codegen.io.DataWriterEnumDefault;
-import org.apache.plc4x.java.spi.generation.Message;
-import org.apache.plc4x.java.spi.generation.MessageIO;
-import org.apache.plc4x.java.spi.generation.SerializationException;
-import org.apache.plc4x.java.spi.generation.WriteBuffer;
-import org.apache.plc4x.java.spi.generation.WriteBufferBoxBased;
-import org.apache.plc4x.java.spi.generation.WithReaderWriterArgs;
import java.time.*;
import java.util.*;
@@ -68,8 +67,8 @@ import java.math.BigInteger;
// Code generated by code-generation. DO NOT EDIT.
<#-- TODO: the code below implies that parserArguments will be null if not present... not pretty -->
-<#if type.parserArguments.isPresent()><#assign parserArguments=type.parserArguments.orElseThrow()></#if>
-public<#if type.isDiscriminatedParentTypeDefinition()> abstract</#if> class ${type.name}<#if type.parentType??> extends ${type.parentType.name}</#if> implements Message {
+<#if type.parserArguments.isPresent()><#assign parserArguments=type.allParserArguments.orElseThrow()></#if>
+public<#if type.isDiscriminatedParentTypeDefinition()> abstract</#if> class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()><T extends ${type.name}></#if><#if type.parentType??> extends ${type.parentType.name}<${type.name}></#if> implements Message, MessageInput<<#if type.isDiscriminatedParentTypeDefinition()>T<#else>${type.name}</#if>> {
<#--
If this is a discriminated child type, we need to generate methods for accessing it's discriminator
@@ -480,6 +479,266 @@ public<#if type.isDiscriminatedParentTypeDefinition()> abstract</#if> class ${ty
return lengthInBits;
}
+<#-- The parse and serialize methods here are just proxies for forwardning the requests to static counterparts -->
+ <#if !type.isDiscriminatedChildTypeDefinition()>
+ @Override
+ public <#if type.isDiscriminatedParentTypeDefinition()>T<#else>${type.name}</#if> parse(ReadBuffer readBuffer, Object... args) throws ParseException {
+ <#if parserArguments?has_content>
+ if((args == null) || (args.length != ${parserArguments?size})) {
+ throw new PlcRuntimeException("Wrong number of arguments, expected ${parserArguments?size}, but got " + args.length);
+ }
+ <#list parserArguments as parserArgument>
+ ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)} ${parserArgument.name};
+ if(args[${parserArgument?index}] instanceof ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)}) {
+ ${parserArgument.name} = (${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)}) args[${parserArgument?index}];
+ <#if helper.isSimpleTypeReference(parserArgument.type)>
+ } else if (args[${parserArgument?index}] instanceof String) {
+ ${parserArgument.name} = ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)}.valueOf((String) args[${parserArgument?index}]);
+ </#if>
+ } else {
+ throw new PlcRuntimeException("Argument ${parserArgument?index} expected to be of type ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)} or a string which is parseable but was " + args[${parserArgument?index}].getClass().getName());
+ }
+ </#list>
+ </#if>
+ return <#if type.isDiscriminatedParentTypeDefinition()>(T)</#if>${type.name}.staticParse(readBuffer<#if parserArguments?has_content>, <#list parserArguments as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if>);
+ }
+
+ <#else>
+ @Override
+ public ${type.name} parse(ReadBuffer readBuffer, Object... args) throws ParseException {
+ return (${type.name}) super.parse(readBuffer, args);
+ }
+
+ </#if>
+<#-- Here come the actual parse and serialize methods that actually do the parsing and serlaizing -->
+ <#assign hasParserArguments=parserArguments?has_content/>
+ <#assign parserArgumentList><#if hasParserArguments><#list parserArguments as parserArgument>${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)} ${parserArgument.name}<#sep>, </#sep></#list></#if></#assign>
+ public static ${type.name}<#if type.isDiscriminatedChildTypeDefinition()>Builder staticParseBuilder<#else> staticParse</#if>(ReadBuffer readBuffer<#if hasParserArguments>, ${parserArgumentList}</#if>) throws ParseException {
+ readBuffer.pullContext("${type.name}");
+ int startPos = readBuffer.getPos();
+ int curPos;
+ <#list type.fields as field>
+ <#switch field.typeName>
+ <#case "array">
+ <#assign arrayField = field.asArrayField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+ <#assign namedField = field.asNamedField().orElseThrow()>
+
+ <#if typedField.type.isByteBased()>
+ <#if !field.isCountArrayField() && !field.isLengthArrayField()>
+ throw new ParseException("array fields of type byte only support 'count' and 'length' loop-types.");
+ </#if>
+ byte[] ${namedField.name} = readBuffer.readByteArray("${namedField.name}", (int) ${helper.toParseExpression(arrayField, helper.intTypeReference, arrayField.loopExpression, parserArguments)}${helper.getFieldOptions(typedField, parserArguments)});
+ <#else>
+ <#-- If this is a count array, we can directly initialize an array with the given size -->
+ <#if field.isCountArrayField()>
+ List<${helper.getNonPrimitiveLanguageTypeNameForField(arrayField)}> ${arrayField.name} = readCountArrayField("${arrayField.name}", ${helper.getDataReaderCall(arrayField.type)}, ${helper.toParseExpression(arrayField, helper.intTypeReference, arrayField.loopExpression, parserArguments)}${helper.getFieldOptions(typedField, parserArguments)});
+ <#-- In all other cases do we have to work with a list, that is later converted to an array -->
+ <#else>
+ <#-- For a length array, we read data till the read position of the buffer reaches a given position -->
+ <#if field.isLengthArrayField()>
+ List<${helper.getNonPrimitiveLanguageTypeNameForField(arrayField)}> ${arrayField.name} = readLengthArrayField("${arrayField.name}", ${helper.getDataReaderCall(arrayField.type)}, ${helper.toParseExpression(arrayField, helper.intTypeReference, arrayField.loopExpression, parserArguments)}${helper.getFieldOptions(typedField, parserArguments)});
+ <#-- A terminated array keeps on reading data as long as the termination expression evaluates to false -->
+ <#elseif field.isTerminatedArrayField()>
+ List<${helper.getNonPrimitiveLanguageTypeNameForField(arrayField)}> ${arrayField.name} = readTerminatedArrayField("${arrayField.name}", ${helper.getDataReaderCall(arrayField.type)}, () -> ((boolean) (${helper.toParseExpression(arrayField, helper.intTypeReference, arrayField.loopExpression, parserArguments)}))${helper.getFieldOptions(typedField, parserArguments)});
+ </#if>
+ </#if>
+ </#if>
+ <#break>
+ <#case "assert">
+ <#assign assertField = field.asAssertField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+ <#assign namedField = field.asNamedField().orElseThrow()>
+
+ ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}, ${helper.toParseExpression(assertField, assertField.type, assertField.conditionExpression, parserArguments)}${helper.getFieldOptions(typedField, parserArguments)});
+ <#break>
+ <#case "checksum">
+ <#assign checksumField = field.asChecksumField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+ <#assign namedField = field.asNamedField().orElseThrow()>
+
+ ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}, ${helper.toParseExpression(checksumField, checksumField.type, checksumField.checksumExpression, parserArguments)}${helper.getFieldOptions(typedField, parserArguments)});
+ <#break>
+ <#case "const">
+ <#assign constField = field.asConstField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+ <#assign namedField = field.asNamedField().orElseThrow()>
+
+ ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}, ${type.name}.${namedField.name?upper_case}${helper.getFieldOptions(typedField, parserArguments)});
+ <#break>
+ <#case "discriminator">
+ <#assign discriminatorField = field.asDiscriminatorField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+ <#assign namedField = field.asNamedField().orElseThrow()>
+
+ ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}${helper.getFieldOptions(typedField, parserArguments)});
+ <#break>
+ <#case "enum">
+ <#assign enumField = field.asEnumField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+ <#assign namedField = field.asNamedField().orElseThrow()>
+
+ ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", "${helper.getTypeDefinitionForTypeReference(enumField.type).name}", readEnum(${helper.getTypeDefinitionForTypeReference(enumField.type).name}::firstEnumForField${enumField.fieldName?cap_first}, ${helper.getDataReaderCall(helper.getEnumFieldTypeReference(enumField.type, enumField.fieldName))})${helper.getFieldOptions(typedField, parserArguments)});
+ <#break>
+ <#case "implicit">
+ <#assign implicitField = field.asImplicitField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+ <#assign namedField = field.asNamedField().orElseThrow()>
+
+ ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}${helper.getFieldOptions(typedField, parserArguments)});
+ <#break>
+ <#case "manualArray">
+ <#assign manualArrayField = field.asManualArrayField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+ <#assign namedField = field.asNamedField().orElseThrow()>
+
+ <#if manualArrayField.type.isByteBased()>
+ byte[] ${namedField.name} = readManualByteArrayField("${namedField.name}", readBuffer, (byte[] _values) -> (boolean) (${helper.toParseExpression(manualArrayField, helper.boolTypeReference, manualArrayField.loopExpression, parserArguments)}), () -> (${helper.getLanguageTypeNameForField(manualArrayField)}) (${helper.toParseExpression(manualArrayField, manualArrayField.type, manualArrayField.parseExpression, parserArguments)})${helper.getFieldOptions(typedField, parserAr [...]
+ <#else>
+ List<${helper.getNonPrimitiveLanguageTypeNameForField(manualArrayField)}> ${namedField.name} = readManualArrayField("${namedField.name}", readBuffer, (List<${helper.getNonPrimitiveLanguageTypeNameForField(manualArrayField)}> _values) -> (boolean) (${helper.toParseExpression(manualArrayField, helper.boolTypeReference, manualArrayField.loopExpression, parserArguments)}), () -> (${helper.getLanguageTypeNameForField(manualArrayField)}) (${helper.toParseExpression(manualAr [...]
+ </#if>
+ <#break>
+ <#case "manual">
+ <#assign manualField = field.asManualField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+ <#assign namedField = field.asNamedField().orElseThrow()>
+
+ ${helper.getLanguageTypeNameForField(field)} ${manualField.name} = readManualField("${namedField.name}", readBuffer, () -> (${helper.getLanguageTypeNameForField(manualField)}) (${helper.toParseExpression(manualField, manualField.type, manualField.parseExpression, parserArguments)})${helper.getFieldOptions(typedField, parserArguments)});
+ <#break>
+ <#case "optional">
+ <#assign optionalField = field.asOptionalField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+ <#assign namedField = field.asNamedField().orElseThrow()>
+
+ ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}<#if optionalField.conditionExpression.present>, ${helper.toParseExpression(optionalField, helper.boolTypeReference, optionalField.conditionExpression.get(), parserArguments)}</#if>${helper.getFieldOptions(typedField, parserArguments)});
+ <#break>
+ <#case "padding">
+ <#assign paddingField = field.asPaddingField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+ <#assign simpleTypeReference = paddingField.type.asSimpleTypeReference().orElseThrow()>
+
+ read${field.typeName?cap_first}Field(${helper.getDataReaderCall(typedField.type)}, (int) (${helper.toParseExpression(paddingField, paddingField.type, paddingField.paddingCondition, parserArguments)})${helper.getFieldOptions(typedField, parserArguments)});
+ <#break>
+ <#case "reserved">
+ <#assign reservedField = field.asReservedField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+
+ read${field.typeName?cap_first}Field("reserved", ${helper.getDataReaderCall(typedField.type)}, ${helper.getReservedValue(reservedField)}${helper.getFieldOptions(typedField, parserArguments)});
+ <#break>
+ <#case "simple">
+ <#assign simpleField = field.asSimpleField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+ <#assign namedField = field.asNamedField().orElseThrow()>
+
+ ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = <#if helper.isEnumTypeReference(typedField.type)>readEnumField("${namedField.name}", "${helper.getLanguageTypeNameForField(field)}", ${helper.getDataReaderCall(typedField.type)}${helper.getFieldOptions(typedField, parserArguments)});<#else>read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}${helper.getFieldOptions(typedField, parserArguments)});</#if>
+ <#break>
+ <#case "switch">
+ <#assign switchField = field.asSwitchField().orElseThrow()>
+
+ // Switch Field (Depending on the discriminator values, passes the instantiation to a sub-type)
+ ${type.name}Builder builder = null;
+ <#list switchField.cases as case>
+ <@compress single_line=true>
+ <#if case.discriminatorValueTerms?has_content>
+ if(
+ <#list case.discriminatorValueTerms as discriminatorValueTerm>
+ <#if helper.isWildcard(discriminatorValueTerm)>
+ true
+ <#else>
+ <#assign discriminatorExpression=switchField.discriminatorExpressions[discriminatorValueTerm?index].asLiteral().orElseThrow().asVariableLiteral().orElseThrow()>
+ <#assign discriminatorType=helper.getDiscriminatorTypes()[discriminatorExpression.discriminatorName]>
+ EvaluationHelper.equals(
+ ${helper.toParseExpression(switchField, discriminatorType, discriminatorExpression, parserArguments)},
+ <#if helper.isEnumTypeReference(discriminatorType)>
+ ${helper.getLanguageTypeNameForTypeReference(discriminatorType)}.${helper.toParseExpression(switchField, discriminatorType, discriminatorValueTerm, parserArguments)}
+ <#else>
+ ${helper.toParseExpression(switchField, discriminatorType, discriminatorValueTerm, parserArguments)}
+ </#if>
+ )
+ </#if>
+ <#sep> && </#sep>
+ </#list>
+ )
+ </#if>{
+ </...@compress>
+ <@compress single_line=true>
+ <#assign hasCaseParseArguments=case.allParserArguments.isPresent() && case.allParserArguments.orElseThrow()?has_content>
+ <#assign caseParseArguments><#if hasCaseParseArguments><#list case.allParserArguments.orElseThrow() as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if></#assign>
+ builder = ${case.name}.staticParseBuilder(readBuffer<#if hasCaseParseArguments>, ${tracer.dive("case parse arguments")} ${caseParseArguments}</#if>);
+ </...@compress>
+ }<#sep> else </#sep>
+ </#list>
+ if (builder == null) {
+ throw new ParseException("Unsupported case for discriminated type");
+ }
+ <#break>
+ <#case "unknown">
+ <#assign unknownField = field.asUnknownField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+
+ read${field.typeName?cap_first}Field("unknown", ${helper.getDataReaderCall(typedField.type)}${helper.getFieldOptions(typedField, parserArguments)});
+ <#break>
+ <#case "virtual">
+ <#assign virtualField = field.asVirtualField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+ <#assign namedField = field.asNamedField().orElseThrow()>
+ ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getLanguageTypeNameForField(field)}.class, ${helper.toParseExpression(virtualField, virtualField.type, virtualField.valueExpression, parserArguments)}${helper.getFieldOptions(typedField, parserArguments)});
+ <#break>
+ <#case "validation">
+ <#assign validationField = field.asValidationField().orElseThrow()>
+ // Validation
+ if (!(${helper.toParseExpression(validationField, helper.boolTypeReference, validationField.getValidationExpression(), null)})) {
+ throw new ParseAssertException(${validationField.getDescription().orElse("Validation failed")});
+ }
+ <#break>
+ <#case "peek">
+ <#assign peekField = field.asPeekField().orElseThrow()>
+ <#assign typedField = field.asTypedField().orElseThrow()>
+ <#assign namedField = field.asNamedField().orElseThrow()>
+
+ ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}<#if peekField.offsetExpression.present>, ${helper.toParseExpression(peekField, helper.boolTypeReference, peekField.offsetExpression.get(), parserArguments)}</#if>${helper.getFieldOptions(typedField, parserArguments)});
+ <#break>
+ </#switch>
+ </#list>
+
+ readBuffer.closeContext("${type.name}");
+ // Create the instance
+ <#if type.isDiscriminatedChildTypeDefinition()>
+ return new ${type.name}Builder(<#list type.propertyFields as field>${field.name}<#sep>, </#sep></#list>);
+ <#elseif type.isDiscriminatedParentTypeDefinition()>
+ return builder.build(<#list type.propertyFields as field>${field.name}<#sep>, </#sep></#list>);
+ <#else>
+ return new ${type.name}(<#list type.propertyFields as field>${field.name}<#sep>, </#sep></#list>);
+ </#if>
+ }
+
+ <#if type.isDiscriminatedParentTypeDefinition()>
+ public static interface ${type.name}Builder {
+ ${type.name} build(<#list type.propertyFields as field><#if field.loopType??>List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}><#else>${helper.getLanguageTypeNameForField(field)}</#if> ${field.name}<#sep>, </#sep></#list>);
+ }
+
+ </#if>
+ <#if type.isDiscriminatedChildTypeDefinition()>
+ public static class ${type.name}Builder implements ${type.parentType.name}.${type.parentType.name}Builder {
+ <#if type.propertyFields?has_content>
+ <#list type.propertyFields as field>
+ private final <#if field.loopType?? && field.type.isByteBased()>byte[]<#elseif field.loopType??>List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}><#else>${helper.getLanguageTypeNameForField(field)}</#if> ${field.name};
+ </#list>
+ </#if>
+
+ public ${type.name}Builder(<#list type.propertyFields as field><#if field.loopType?? && field.type.isByteBased()>byte[]<#elseif field.loopType??>List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}><#else>${helper.getLanguageTypeNameForField(field)}</#if> ${field.name}<#sep>, </#sep></#list>) {
+ <#list type.propertyFields as field>
+ this.${field.name} = ${field.name};
+ </#list>
+ }
+
+ public ${type.name} build(<#list type.parentType.asComplexTypeDefinition().orElseThrow().propertyFields as field><#if field.loopType?? && field.type.isByteBased()>byte[]<#elseif field.loopType??>List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}><#else>${helper.getLanguageTypeNameForField(field)}</#if> ${field.name}<#sep>, </#sep></#list>) {
+ return new ${type.name}(<#list type.allPropertyFields as field>${field.name}<#sep>, </#sep></#list>);
+ }
+ }
+
+ </#if>
+
@Override
public boolean equals(Object o) {
if (this == o) {