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) {