<#--
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you 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

      https://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.
-->
<#-- Prevent freemarker from escaping stuff -->
<#outputformat "undefined">
<#-- Declare the name and type of variables passed in to the template -->
<#-- @ftlvariable name="languageName" type="java.lang.String" -->
<#-- @ftlvariable name="protocolName" type="java.lang.String" -->
<#-- @ftlvariable name="outputFlavor" type="java.lang.String" -->
<#-- @ftlvariable name="helper" type="org.apache.plc4x.language.cs.CsLanguageTemplateHelper" -->
<#-- @ftlvariable name="tracer" type="org.apache.plc4x.plugins.codegenerator.protocol.freemarker.Tracer" -->
<#-- @ftlvariable name="type" type="org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition" -->
<#-- Declare the name and type of variables declared locally inside the template -->
${helper.fileName(protocolName, languageName, outputFlavor)?replace(".", "/")}/model/${type.name}.cs
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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
 *
 *   https://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.
 */

using System;
using System.Collections.Generic;
using System.Text;
using org.apache.plc4net.api.value;
using org.apache.plc4net.spi.generation;
using org.apache.plc4net.spi.model.values;

// 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>
namespace org.apache.plc4net.drivers.${protocolName?replace("-", "")}.${outputFlavor?replace("-", "")}.model
{

    public<#if type.isDiscriminatedParentTypeDefinition()> abstract</#if> class ${type.name}<#if type.parentType.isPresent()> : ${type.parentType.orElseThrow().name}</#if>
    {

<#--
    If this is a discriminated child type, we need to generate methods for accessing it's discriminator
    values, as if they were normal java properties.
-->
<#if type.isDiscriminatedChildTypeDefinition()>
    <#assign discriminatedChildType = type.asDiscriminatedComplexTypeDefinition().orElseThrow()>
        // Accessors for discriminator values.
    <#list discriminatedChildType.getDiscriminatorMap() as discriminatorName, discriminatorValue>
        <#-- If the discriminator name matches that of another field, suppress the methods generation -->
        <#if !discriminatedChildType.isNonDiscriminatorField(discriminatorName)><#--&& !discriminatedChildType.isParserArgument(discriminatorName)-->
            <#assign discriminatorType = helper.getDiscriminatorTypes()[discriminatorName]>
        public override ${helper.getLanguageTypeNameForTypeReference(discriminatorType)} ${discriminatorName?cap_first} => <#if discriminatorValue??><#if discriminatorType.isEnumTypeReference()>${helper.getLanguageTypeNameForTypeReference(discriminatorType)}.${helper.toParseExpression(null, discriminatorType, discriminatorValue, parserArguments)}<#else>${helper.toParseExpression(null, discriminatorType, discriminatorValue, parserArguments)}</#if><#else>${helper.getNullValueForTypeReference(discriminatorType)}</#if>;
        </#if>
    </#list>
</#if>
<#--
    If this is a discriminated parent type, we need to generate the abstract methods for accessing it's
    discriminator values instead.
-->
<#if type.isDiscriminatedParentTypeDefinition()>
    <#assign discriminatedParentType = type>
    <#-- @ftlvariable name="discriminatedParentType" type="org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition" -->
        // Abstract accessors for discriminator values.
    <#list helper.discriminatorTypes as discriminatorName, discriminatorType>
        <#-- If the discriminator name matches that of another field, suppress the methods generation -->
        <#if !type.isNonDiscriminatorField(discriminatorName)><#-- && !type.isParserArgument(discriminatorName)-->
        public abstract ${helper.getLanguageTypeNameForTypeReference(discriminatorType)} ${discriminatorName?cap_first} { get; }
        </#if>
    </#list>
</#if>
<#-- If the current type contains "const" fields, generate some java constants for holing their values -->
<#if type.constFields?has_content>

        // Constant values.
<#list type.constFields as field>
        public const ${helper.getLanguageTypeNameForField(field)} ${field.name?upper_case} = ${helper.toParseExpression(field, field.type, field.referenceValue, parserArguments)};
</#list>
</#if>
<#-- Property fields are fields that require a property in the pojo -->
<#if type.propertyFields?has_content>

        // Properties.
<#list type.propertyFields as field>
        public ${helper.getLanguageTypeNameForTypeReference(field.type)} ${field.name?cap_first} { get; }
</#list>
</#if>

    <#-- getAllPropertyFields() returns not only the property fields of this type but also of it's parents -->
        public ${type.name}(<#list type.getAllPropertyFields() as field>${helper.getLanguageTypeNameForField(field)} ${field.name}<#sep>, </#sep></#list>)
<#if type.getParentPropertyFields()?has_content>
            : base(<#list type.getParentPropertyFields() as field>${field.name}<#sep>, </#sep></#list>)
</#if>
        {
<#list type.propertyFields as field>
            ${field.name?cap_first} = ${field.name};
</#list>
        }

<#--list type.abstractFields as field>
    public abstract <#if field.loopType?? && field.type.isByteBased()>byte[]<#elseif field.loopType??>List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}><#else>${helper.getLanguageTypeNameForField(field)}</#if> get${field.asNamedField().orElseThrow().name?cap_first}();

</#list>
<#list type.propertyFields as field>
    public <#if field.loopType?? && field.type.isByteBased()>byte[]<#elseif field.loopType??>List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}><#else>${helper.getLanguageTypeNameForField(field)}</#if> get${field.name?cap_first}() {
        return ${field.name};
    }

</#list>
<#list type.virtualFields as field>
    <#if !type.isDiscriminatorField(field.name)>
    public <#if field.loopType?? && field.type.isByteBased()>byte[]<#elseif field.loopType??>List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}><#else>${helper.getLanguageTypeNameForField(field)}</#if> get${field.name?cap_first}() {
        <#if helper.getLanguageTypeNameForField(field) = 'String'>
        return ${helper.getLanguageTypeNameForField(field)}.valueOf(${helper.toSerializationExpression(field, field.type, field.valueExpression, parserArguments)});
        <#elseif helper.getLanguageTypeNameForField(field) = 'BigInteger'>
        return BigInteger.valueOf(${helper.toSerializationExpression(field, field.type, field.valueExpression, parserArguments)});
        <#else>
        return (${helper.getLanguageTypeNameForField(field)}) (${helper.toSerializationExpression(field, field.type, field.valueExpression, parserArguments)});
        </#if>
    }
    </#if>

</#list>

    <#if outputFlavor != "passive">
<#if type.isDiscriminatedChildTypeDefinition()>
    @Override
    protected void serialize${type.parentType.orElseThrow().name}Child(WriteBuffer writeBuffer) throws SerializationException {
<#else>
    <#if type.isDiscriminatedParentTypeDefinition()>
    abstract protected void serialize${type.name?cap_first}Child(WriteBuffer writeBuffer) throws SerializationException;

    </#if>
    public void serialize(WriteBuffer writeBuffer) throws SerializationException {
</#if>
        <#if helper.hasFieldOfType("unknown")>
            throw new SerializationException("Unknown field not serializable");
        <#else>
            int startPos = writeBuffer.getPos();
            writeBuffer.pushContext("${type.name}");
            <#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()>

                        // Array Field (${arrayField.name})
                        <#if arrayField.type.isByteBased()>
                        writeByteArrayField("${namedField.name}", ${namedField.name}, writeByteArray(writeBuffer, 8));
                        <#elseif arrayField.type.isSimpleTypeReference()>
                        writeSimpleTypeArrayField("${namedField.name}", ${namedField.name}, ${helper.getDataWriterCall(typedField.type, namedField.name)});
                        <#else>
                        writeComplexTypeArrayField("${namedField.name}", ${namedField.name}, writeBuffer);
                        </#if>
                        <#break>
                    <#case "checksum">
                        <#assign checksumField = field.asChecksumField().orElseThrow()>
                        <#assign typedField = field.asTypedField().orElseThrow()>
                        <#assign namedField = field.asNamedField().orElseThrow()>

                        // Checksum Field (checksum) (Calculated)
                        writeChecksumField("${namedField.name}", (${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(checksumField, checksumField.type, checksumField.checksumExpression, parserArguments)}), ${helper.getDataWriterCall(typedField.type, namedField.name)});
                        <#break>
                    <#case "const">
                        <#assign constField = field.asConstField().orElseThrow()>
                        <#assign typedField = field.asTypedField().orElseThrow()>
                        <#assign namedField = field.asNamedField().orElseThrow()>

                        // Const Field (${constField.name})
                        <#if typedField.type.isEnumTypeReference()>writeConstField("${constField.name}", ${namedField.name?upper_case}.getValue(), ${helper.getDataWriterCall(helper.getEnumBaseTypeReference(typedField.type), namedField.name)});<#else>writeConstField("${constField.name}", ${namedField.name?upper_case}, ${helper.getDataWriterCall(typedField.type, namedField.name)});</#if>
                        <#break>
                    <#case "discriminator">
                        <#assign discriminatorField = field.asDiscriminatorField().orElseThrow()>
                        <#assign typedField = field.asTypedField().orElseThrow()>
                        <#assign namedField = field.asNamedField().orElseThrow()>

                        // Discriminator Field (${discriminatorField.name}) (Used as input to a switch field)
                        <#if typedField.type.isEnumTypeReference()>writeDiscriminatorEnumField("${namedField.name}", "${helper.getLanguageTypeNameForField(field)}", get${discriminatorField.name?cap_first}(), ${helper.getEnumDataWriterCall(typedField.type, namedField.name, "value")});<#else>writeDiscriminatorField("${namedField.name}", get${discriminatorField.name?cap_first}(), ${helper.getDataWriterCall(typedField.type, namedField.name)});</#if>
                        <#break>
                    <#case "enum">
                        <#assign enumField = field.asEnumField().orElseThrow()>
                        <#assign typedField = field.asTypedField().orElseThrow()>
                        <#assign namedField = field.asNamedField().orElseThrow()>

                        // Enum field (${namedField.name})
                        writeEnumField("${namedField.name}", "${helper.getLanguageTypeNameForField(field)}", ${namedField.name}, ${helper.getEnumDataWriterCall(typedField.type, namedField.name, enumField.fieldName)});
                        <#break>
                    <#case "implicit">
                        <#assign implicitField = field.asImplicitField().orElseThrow()>
                        <#assign typedField = field.asTypedField().orElseThrow()>
                        <#assign namedField = field.asNamedField().orElseThrow()>

                        // Implicit Field (${implicitField.name}) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
                        <#- Implicit field values might be used in expressions, in order to avoid problems, we generate a temporary variable with the given name. ->
                        ${helper.getLanguageTypeNameForField(field)} ${implicitField.name} = (${helper.getLanguageTypeNameForField(field)}) (${helper.toSerializationExpression(implicitField, implicitField.type, implicitField.serializeExpression, parserArguments)});
                        writeImplicitField("${namedField.name}", ${implicitField.name}, ${helper.getDataWriterCall(typedField.type, namedField.name)});
                        <#break>
                    <#case "manualArray">
                        <#assign manualArrayField = field.asManualArrayField().orElseThrow()>
                        <#assign typedField = field.asTypedField().orElseThrow()>
                        <#assign namedField = field.asNamedField().orElseThrow()>

                        // Manual Array Field (${manualArrayField.name})
                        writeManualArrayField("${namedField.name}", ${namedField.name}, (${helper.getNonPrimitiveLanguageTypeNameForField(typedField)} _value) -> ${helper.toParseExpression(manualArrayField, manualArrayField.type, manualArrayField.serializeExpression, parserArguments)}, writeBuffer);
                        <#break>
                    <#case "manual">
                        <#assign manualField = field.asManualField().orElseThrow()>
                        <#assign typedField = field.asTypedField().orElseThrow()>
                        <#assign namedField = field.asNamedField().orElseThrow()>

                        // Manual Field (${manualField.name})
                        writeManualField("${namedField.name}", () -> ${helper.toParseExpression(manualField, manualField.type, manualField.serializeExpression, parserArguments)}, writeBuffer);
                        <#break>
                    <#case "optional">
                        <#assign optionalField = field.asOptionalField().orElseThrow()>
                        <#assign typedField = field.asTypedField().orElseThrow()>
                        <#assign namedField = field.asNamedField().orElseThrow()>

                        // Optional Field (${optionalField.name}) (Can be skipped, if the value is null)
                        <#if typedField.type.isEnumTypeReference()>writeOptionalEnumField("${namedField.name}", "${helper.getLanguageTypeNameForField(field)}", ${namedField.name}, ${helper.getEnumDataWriterCall(typedField.type, namedField.name, "value")});<#else>writeOptionalField("${namedField.name}", ${namedField.name}, ${helper.getDataWriterCall(typedField.type, namedField.name)});</#if>
                        <#break>
                    <#case "padding">
                        <#assign paddingField = field.asPaddingField().orElseThrow()>
                        <#assign typedField = field.asTypedField().orElseThrow()>

                        // Padding Field (padding)
                        writePaddingField("padding", (int) (${helper.toParseExpression(paddingField, helper.intTypeReference, paddingField.paddingCondition, parserArguments)}), (${helper.getLanguageTypeNameForField(field)}) ${helper.toSerializationExpression(paddingField, paddingField.type, paddingField.paddingValue, parserArguments)}, ${helper.getDataWriterCall(typedField.type, "padding")});
                        <#break>
                    <#case "reserved">
                        <#assign reservedField = field.asReservedField().orElseThrow()>
                        <#assign typedField = field.asTypedField().orElseThrow()>

                        // Reserved Field (reserved)
                        writeReservedField("reserved", ${helper.getReservedValue(reservedField)}, ${helper.getDataWriterCall(typedField.type, "reserved")});
                        <#break>
                    <#case "simple">
                        <#assign simpleField = field.asSimpleField().orElseThrow()>
                        <#assign typedField = field.asTypedField().orElseThrow()>
                        <#assign namedField = field.asNamedField().orElseThrow()>

                        // Simple Field (${namedField.name})
                        <#if typedField.type.isEnumTypeReference()>writeSimpleEnumField("${namedField.name}", "${helper.getLanguageTypeNameForField(field)}", ${namedField.name}, ${helper.getEnumDataWriterCall(typedField.type, namedField.name, "value")});<#else>writeSimpleField("${namedField.name}", ${simpleField.name}, ${helper.getDataWriterCall(typedField.type, namedField.name)});</#if>
                        <#break>
                    <#case "switch">
                        <#assign switchField = field.asSwitchField().orElseThrow()>

                        // Switch field (Serialize the sub-type)
                        serialize${type.name?cap_first}Child(writeBuffer);
                        <#break>
                    <#case "virtual">
                        <#assign virtualField = field.asVirtualField().orElseThrow()>
                        <#assign typedField = field.asTypedField().orElseThrow()>
                        <#assign namedField = field.asNamedField().orElseThrow()>

                        // Virtual field (doesn't actually serialize anything, just makes the value available)
                        ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = get${namedField.name?cap_first}();
                        writeBuffer.writeVirtual("${namedField.name}", ${namedField.name});
                        <#break>
                </#switch>
            </#list>

            writeBuffer.popContext("${type.name}");
        </#if>
        }
    </#if>

    @Override
    public int getLengthInBytes() {
        return getLengthInBits() / 8;
    }

        @Override
        public int getLengthInBits() {
            int lengthInBits = <#if type.parentType.isPresent()>super.getLengthInBits()<#else>0</#if>;
            ${type.name} _value  = this;
    <#list type.fields as field>
    <#switch field.typeName>
        <#case "array">
            <#assign arrayField = field.asArrayField().orElseThrow()>

            // Array field
            if(${arrayField.name} != null) {
            <#if helper.isSimpleTypeReference(arrayField.type)>
                <#assign simpleTypeReference = arrayField.type.asSimpleTypeReference().orElseThrow()>
                lengthInBits += ${simpleTypeReference.sizeInBits} * ${arrayField.name}.<#if arrayField.type.isByteBased()>length<#else>size()</#if>;
            <#elseif arrayField.isCountArrayField()>
                int i=0;
                <#assign complexTypeReference = arrayField.type.asComplexTypeReference().orElseThrow()>
                for(${complexTypeReference.name} element : ${arrayField.name}) {
                    boolean last = ++i >= ${arrayField.name}.size();
                    lengthInBits += element.getLengthInBits();
                }
            <#else>
                for(Message element : ${arrayField.name}) {
                    lengthInBits += element.getLengthInBits();
                }
            </#if>
            }
            <#break>
        <#case "checksum">
            <#assign checksumField = field.asChecksumField().orElseThrow()>
            <#assign typedField = field.asTypedField().orElseThrow()>
            <#assign simpleTypeReference = typedField.type.asSimpleTypeReference().orElseThrow()>

            // Checksum Field (checksum)
            lengthInBits += ${simpleTypeReference.sizeInBits};
            <#break>
        <#case "const">
            <#assign constField = field.asConstField().orElseThrow()>
            <#assign typedField = field.asTypedField().orElseThrow()>

            // Const Field (${constField.name})
            <#if helper.isSimpleTypeReference(typedField.type)>
            <#assign simpleTypeReference = typedField.type.asSimpleTypeReference().orElseThrow()>
            lengthInBits += ${simpleTypeReference.sizeInBits};
            <#else>
            lengthInBits += ${helper.getEnumBaseTypeReference(typedField.type).sizeInBits};
            </#if>
            <#break>
        <#case "discriminator">
            <#assign discriminatorField = field.asDiscriminatorField().orElseThrow()>

            // Discriminator Field (${discriminatorField.name})
            <#if helper.isSimpleTypeReference(discriminatorField.type)>
                <#assign simpleTypeReference = discriminatorField.type.asSimpleTypeReference().orElseThrow()>
                <#if simpleTypeReference.isVstringTypeReference()>
                    <#assign vstringTypeReference = simpleTypeReference.asVstringTypeReference().orElseThrow()>
            lengthInBits += ${helper.toSerializationExpression(discriminatorField, helper.intTypeReference, vstringTypeReference.getLengthExpression(), parserArguments)};
                <#else>
            lengthInBits += ${simpleTypeReference.sizeInBits};
                </#if>
            <#elseif helper.isEnumField(field)>
                lengthInBits += ${helper.getEnumBaseTypeReference(discriminatorField.type).sizeInBits};
            <#else>
                lengthInBits += ${discriminatorField.name}.getLengthInBits();
            </#if>
            <#break>
        <#case "enum">
            <#assign enumField = field.asEnumField().orElseThrow()>

            // Enum Field (${enumField.name})
            lengthInBits += ${helper.getEnumBaseTypeReference(enumField.type).sizeInBits};
            <#break>
        <#case "implicit">
            <#assign implicitField = field.asImplicitField().orElseThrow()>
            <#assign simpleTypeReference = implicitField.type.asSimpleTypeReference().orElseThrow()>

            // Implicit Field (${implicitField.name})
            lengthInBits += ${simpleTypeReference.sizeInBits};
            <#break>
        <#case "manualArray">
            <#assign manualArrayField = field.asManualArrayField().orElseThrow()>

            // Manual Array Field (${manualArrayField.name})
            lengthInBits += ${helper.toParseExpression(manualArrayField, helper.intTypeReference, manualArrayField.lengthExpression, parserArguments)} * 8;
            <#break>
        <#case "manual">
            <#assign manualField = field.asManualField().orElseThrow()>

            // Manual Field (${manualField.name})
            lengthInBits += ${helper.toParseExpression(manualField, helper.intTypeReference, manualField.lengthExpression, parserArguments)} * 8;
            <#break>
        <#case "optional">
            <#assign optionalField = field.asOptionalField().orElseThrow()>

            // Optional Field (${optionalField.name})
            if(${optionalField.name} != null) {
            <#if helper.isSimpleTypeReference(optionalField.type)>
                <#assign simpleTypeReference = optionalField.type.asSimpleTypeReference().orElseThrow()>
                <#if simpleTypeReference.isVstringTypeReference()>
                    <#assign vstringTypeReference = simpleTypeReference.asVstringTypeReference().orElseThrow()>
                lengthInBits += ${helper.toSerializationExpression(optionalField, helper.intTypeReference, vstringTypeReference.getLengthExpression(), parserArguments)};
                <#else>
                lengthInBits += ${simpleTypeReference.sizeInBits};
                </#if>
            <#elseif helper.isEnumField(field)>
                lengthInBits += ${helper.getEnumBaseTypeReference(optionalField.type).sizeInBits};
            <#else>
                lengthInBits += ${optionalField.name}.getLengthInBits();
            </#if>
            }
            <#break>
        <#case "padding">
            <#assign paddingField = field.asPaddingField().orElseThrow()>
            <#assign simpleTypeReference = paddingField.type.asSimpleTypeReference().orElseThrow()>

            // Padding Field (padding)
            <#- We're replacing the "lastItem" with 'false' here as the item itself can't know if it is the last ->
            int _timesPadding = (int) (${helper.toParseExpression(paddingField, helper.intTypeReference, paddingField.paddingCondition, parserArguments)});
            while (_timesPadding-- > 0) {
                lengthInBits += ${simpleTypeReference.sizeInBits};
            }
            <#break>
        <#case "reserved">
            <#assign reservedField = field.asReservedField().orElseThrow()>
            <#assign simpleTypeReference = reservedField.type.asSimpleTypeReference().orElseThrow()>

            // Reserved Field (reserved)
            lengthInBits += ${simpleTypeReference.sizeInBits};
            <#break>
        <#case "simple">
            <#assign simpleField = field.asSimpleField().orElseThrow()>

            // Simple field (${simpleField.name})
            <#if simpleField.type.isSimpleTypeReference()>
                <#assign simpleTypeReference = simpleField.type.asSimpleTypeReference().orElseThrow()>
                <#if simpleTypeReference.isVstringTypeReference()>
                    <#assign vstringTypeReference = simpleTypeReference.asVstringTypeReference().orElseThrow()>
            lengthInBits += ${helper.toSerializationExpression(simpleField, helper.intTypeReference, vstringTypeReference.getLengthExpression(), parserArguments)};
                <#else>
            lengthInBits += ${simpleTypeReference.sizeInBits};
                </#if>
            <#elseif helper.isEnumField(field)>
            lengthInBits += ${helper.getEnumBaseTypeReference(simpleField.type).sizeInBits};
            <#else>
            lengthInBits += ${simpleField.name}.getLengthInBits();
            </#if>
            <#break>
        <#case "switch">
            <#assign switchField = field.asSwitchField().orElseThrow()>

            // Length of sub-type elements will be added by sub-type...
            <#break>
        <#case "unknown">
            <#assign unknownField = field.asUnknownField().orElseThrow()>
            <#assign simpleTypeReference = unknownField.type.asSimpleTypeReference().orElseThrow()>

            // Unknown field
            lengthInBits += ${simpleTypeReference.sizeInBits};
        <#case "virtual">
            <#assign virtualField = field>

            // A virtual field doesn't have any in- or output.
            <#break>
    </#switch>
    </#list>

            return lengthInBits;
        }-->
    }
}
</#outputformat>