You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2021/10/08 12:41:16 UTC

[plc4x] branch feature/mspec-ng updated: - Replaced float syntax with only one bitLength - Started implementing the DataReader, DataWriter and FieldReaders - Added a new java-ng template module

This is an automated email from the ASF dual-hosted git repository.

cdutz pushed a commit to branch feature/mspec-ng
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/feature/mspec-ng by this push:
     new a740d81  - Replaced float syntax with only one bitLength - Started implementing the DataReader, DataWriter and FieldReaders - Added a new java-ng template module
a740d81 is described below

commit a740d813b1d41dc6c6ea91621fce28455f53d269
Author: cdutz <ch...@c-ware.de>
AuthorDate: Fri Oct 8 14:41:04 2021 +0200

    - Replaced float syntax with only one bitLength
    - Started implementing the DataReader, DataWriter and FieldReaders
    - Added a new java-ng template module
---
 code-generation/language-java-ng/pom.xml           | 155 ++++
 .../plc4x/language/java/JavaLanguageOutput.java    | 100 +++
 .../language/java/JavaLanguageTemplateHelper.java  | 992 +++++++++++++++++++++
 ...x.plugins.codegenerator.language.LanguageOutput |  19 +
 .../templates/java/data-io-template.java.ftlh      | 425 +++++++++
 .../java/enum-package-info-template.java.ftlh      |  50 ++
 .../templates/java/enum-template.java.ftlh         | 144 +++
 .../resources/templates/java/io-template.java.ftlh | 936 +++++++++++++++++++
 .../templates/java/pojo-template.java.ftlh         | 394 ++++++++
 .../src/test/resources/integration-test/pom.xml    | 207 +++++
 .../src/test/resources/settings.xml                |  53 ++
 code-generation/pom.xml                            |   1 +
 .../plugins/codegenerator/language/mspec/MSpec.g4  |   4 +-
 .../src/main/resources/protocols/test/test.mspec   |  54 +-
 .../plc4x/java/spi/codegen/FieldCommons.java       |  67 ++
 .../apache/plc4x/java/spi/codegen/WithOption.java  |  50 ++
 .../plc4x/java/spi/codegen/fields/FieldReader.java |  31 +
 .../spi/codegen/fields/FieldReaderAbstract.java    |  37 +
 .../java/spi/codegen/fields/FieldReaderArray.java  |  37 +
 .../java/spi/codegen/fields/FieldReaderAssert.java |  37 +
 .../spi/codegen/fields/FieldReaderChecksum.java    |  37 +
 .../java/spi/codegen/fields/FieldReaderConst.java  |  37 +
 .../codegen/fields/FieldReaderDiscriminator.java   |  37 +
 .../java/spi/codegen/fields/FieldReaderEnum.java   |  37 +
 .../spi/codegen/fields/FieldReaderImplicit.java    |  37 +
 .../java/spi/codegen/fields/FieldReaderManual.java |  37 +
 .../spi/codegen/fields/FieldReaderManualArray.java |  37 +
 .../spi/codegen/fields/FieldReaderOptional.java    |  53 ++
 .../spi/codegen/fields/FieldReaderPadding.java     |  37 +
 .../spi/codegen/fields/FieldReaderReserved.java    |  37 +
 .../java/spi/codegen/fields/FieldReaderSimple.java |  32 +
 .../spi/codegen/fields/FieldReaderTypeSwitch.java  |  37 +
 .../spi/codegen/fields/FieldReaderUnknown.java     |  37 +
 .../spi/codegen/fields/FieldReaderVirtual.java     |  37 +
 .../plc4x/java/spi/codegen/fields/FieldWriter.java |  28 +
 .../plc4x/java/spi/codegen/io/DataReader.java      |  35 +
 .../java/spi/codegen/io/DataReaderComplex.java     |  30 +
 .../spi/codegen/io/DataReaderComplexDefault.java   |  60 ++
 .../plc4x/java/spi/codegen/io/DataReaderEnum.java  |  28 +
 .../java/spi/codegen/io/DataReaderEnumDefault.java |  57 ++
 .../java/spi/codegen/io/DataReaderSimple.java      |  31 +
 .../spi/codegen/io/DataReaderSimpleBigDecimal.java |  46 +
 .../spi/codegen/io/DataReaderSimpleBoolean.java    |  48 +
 .../java/spi/codegen/io/DataReaderSimpleByte.java  |  46 +
 .../spi/codegen/io/DataReaderSimpleByteArray.java  |  46 +
 .../spi/codegen/io/DataReaderSimpleDouble.java     |  43 +
 .../java/spi/codegen/io/DataReaderSimpleFloat.java |  43 +
 .../io/DataReaderSimpleSignedBigInteger.java       |  45 +
 .../spi/codegen/io/DataReaderSimpleSignedByte.java |  43 +
 .../spi/codegen/io/DataReaderSimpleSignedInt.java  |  43 +
 .../spi/codegen/io/DataReaderSimpleSignedLong.java |  43 +
 .../codegen/io/DataReaderSimpleSignedShort.java    |  43 +
 .../io/DataReaderSimpleUnsignedBigInteger.java     |  45 +
 .../codegen/io/DataReaderSimpleUnsignedByte.java   |  43 +
 .../codegen/io/DataReaderSimpleUnsignedInt.java    |  43 +
 .../codegen/io/DataReaderSimpleUnsignedLong.java   |  43 +
 .../codegen/io/DataReaderSimpleUnsignedShort.java  |  43 +
 .../plc4x/java/spi/codegen/io/DataWriter.java      |  27 +
 .../java/spi/codegen/io/DataWriterBigDecimal.java  |  39 +
 .../java/spi/codegen/io/DataWriterBoolean.java     |  41 +
 .../plc4x/java/spi/codegen/io/DataWriterByte.java  |  41 +
 .../java/spi/codegen/io/DataWriterByteArray.java   |  42 +
 .../java/spi/codegen/io/DataWriterDouble.java      |  38 +
 .../plc4x/java/spi/codegen/io/DataWriterFloat.java |  40 +
 .../spi/codegen/io/DataWriterSignedBigInteger.java |  39 +
 .../java/spi/codegen/io/DataWriterSignedByte.java  |  38 +
 .../java/spi/codegen/io/DataWriterSignedInt.java   |  38 +
 .../java/spi/codegen/io/DataWriterSignedLong.java  |  38 +
 .../java/spi/codegen/io/DataWriterSignedShort.java |  38 +
 .../codegen/io/DataWriterUnsignedBigInteger.java   |  39 +
 .../spi/codegen/io/DataWriterUnsignedByte.java     |  38 +
 .../java/spi/codegen/io/DataWriterUnsignedInt.java |  38 +
 .../spi/codegen/io/DataWriterUnsignedLong.java     |  38 +
 .../java/spi/generation/WriteBufferByteBased.java  |  12 +-
 .../java/spi/generation/WriteBufferJsonBased.java  |   6 +-
 .../java/spi/generation/WriteBufferXmlBased.java   |   7 +-
 .../org/apache/plc4x/java/spi/values/PlcLREAL.java |   3 +-
 .../org/apache/plc4x/java/spi/values/PlcREAL.java  |   3 +-
 .../ads/src/main/resources/protocols/ads/ads.mspec |   4 +-
 .../resources/protocols/bacnetip/bacnetip.mspec    |   8 +-
 .../src/main/resources/protocols/can/canopen.mspec |   4 +-
 .../main/resources/protocols/can/genericcan.mspec  |   4 +-
 .../resources/protocols/knxnetip/knxnetip.mspec    |   6 +-
 .../main/resources/protocols/modbus/modbus.mspec   |   8 +-
 .../s7/src/main/resources/protocols/s7/s7.mspec    |   4 +-
 .../resources/protocols/simulated/simulated.mspec  |   8 +-
 86 files changed, 5915 insertions(+), 71 deletions(-)

diff --git a/code-generation/language-java-ng/pom.xml b/code-generation/language-java-ng/pom.xml
new file mode 100644
index 0000000..7d51745
--- /dev/null
+++ b/code-generation/language-java-ng/pom.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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
+
+      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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.plc4x</groupId>
+    <artifactId>plc4x-code-generation</artifactId>
+    <version>0.10.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>plc4x-code-generation-language-java-ng</artifactId>
+
+  <name>Code-Generation: Language: Java</name>
+  <description>Code generation template for generating Java code</description>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-invoker-plugin</artifactId>
+        <version>3.2.2</version>
+        <executions>
+          <execution>
+            <id>integration-test</id>
+            <goals>
+              <!-- Install the current project artifacts into the maven local repo, so they can be used in the test -->
+              <goal>install</goal>
+              <!-- Execute the maven builds defines in src/test/resources -->
+              <goal>integration-test</goal>
+              <!-- Checks the results of the maven builds -->
+              <goal>verify</goal>
+            </goals>
+            <configuration>
+              <!-- These option allow that openjdk >=16 can run the google-code-format -->
+              <!-- TODO: Add this to a self-activating maven profile -->
+              <!--mavenOpts>
+                - -add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
+                - -add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
+                - -add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
+                - -add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
+                - -add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+              </mavenOpts-->
+              <debug>true</debug>
+              <localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath>
+              <projectsDirectory>src/test/resources</projectsDirectory>
+              <cloneProjectsTo>${project.build.directory}/integration-tests</cloneProjectsTo>
+              <settingsFile>src/test/resources/settings.xml</settingsFile>
+              <pomIncludes>
+                <pomInclude>*/pom.xml</pomInclude>
+              </pomIncludes>
+              <!-- The goals we will be executing in the test-projects -->
+              <goals>
+                <goal>test</goal>
+              </goals>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <!-- We are using the Freemarker module to generate Java code -->
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4x-code-generation-language-base-freemarker</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x.plugins</groupId>
+      <artifactId>plc4x-code-generation-types-base</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.freemarker</groupId>
+      <artifactId>freemarker</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-text</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4x-code-generation-protocol-base-mspec</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.googlejavaformat</groupId>
+      <artifactId>google-java-format</artifactId>
+      <version>1.11.0</version>
+    </dependency>
+
+    <!-- Make sure the dependencies of the module are built first -->
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-api</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-spi</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-transport-tcp</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-utils-test-utils</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4x-code-generation-protocol-test</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4x-code-generation-protocol-test</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+      <classifier>tests</classifier>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git a/code-generation/language-java-ng/src/main/java/org/apache/plc4x/language/java/JavaLanguageOutput.java b/code-generation/language-java-ng/src/main/java/org/apache/plc4x/language/java/JavaLanguageOutput.java
new file mode 100644
index 0000000..f935fcc
--- /dev/null
+++ b/code-generation/language-java-ng/src/main/java/org/apache/plc4x/language/java/JavaLanguageOutput.java
@@ -0,0 +1,100 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.language.java;
+
+import com.google.googlejavaformat.java.FormatterException;
+import freemarker.template.*;
+import org.apache.commons.io.FileUtils;
+import org.apache.plc4x.plugins.codegenerator.protocol.freemarker.FreemarkerException;
+import org.apache.plc4x.plugins.codegenerator.protocol.freemarker.FreemarkerLanguageOutput;
+import org.apache.plc4x.plugins.codegenerator.protocol.freemarker.FreemarkerLanguageTemplateHelper;
+import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.*;
+
+import com.google.googlejavaformat.java.Formatter;
+
+public class JavaLanguageOutput extends FreemarkerLanguageOutput {
+
+    private Formatter formatter = new Formatter();
+
+    @Override
+    public String getName() {
+        return "Java";
+    }
+
+    @Override
+    public Set<String> supportedOptions() {
+        return Collections.singleton("package");
+    }
+
+    @Override
+    public List<String> supportedOutputFlavors() {
+        return Arrays.asList("read-write", "read-only", "passive");
+    }
+
+    @Override
+    protected List<Template> getSpecTemplates(Configuration freemarkerConfiguration) throws IOException {
+        return Collections.singletonList(
+            freemarkerConfiguration.getTemplate("templates/java/enum-package-info-template.java.ftlh"));
+    }
+
+    @Override
+    protected List<Template> getComplexTypeTemplates(Configuration freemarkerConfiguration) throws IOException {
+        return Arrays.asList(
+            freemarkerConfiguration.getTemplate("templates/java/pojo-template.java.ftlh"),
+            freemarkerConfiguration.getTemplate("templates/java/io-template.java.ftlh"));
+    }
+
+    @Override
+    protected List<Template> getEnumTypeTemplates(Configuration freemarkerConfiguration) throws IOException {
+        return Collections.singletonList(
+            freemarkerConfiguration.getTemplate("templates/java/enum-template.java.ftlh"));
+    }
+
+    @Override
+    protected List<Template> getDataIoTemplates(Configuration freemarkerConfiguration) throws IOException {
+        return Collections.singletonList(
+            freemarkerConfiguration.getTemplate("templates/java/data-io-template.java.ftlh"));
+    }
+
+    @Override
+    protected FreemarkerLanguageTemplateHelper getHelper(TypeDefinition thisType, String protocolName, String flavorName, Map<String, TypeDefinition> types,
+                                                         Map<String, String> options) {
+        return new JavaLanguageTemplateHelper(thisType, protocolName, flavorName, types, options);
+    }
+
+    @Override
+    protected void postProcessTemplateOutput(File outputFile) {
+        try {
+            FileUtils.writeStringToFile(
+                outputFile,
+                formatter.formatSourceAndFixImports(
+                    FileUtils.readFileToString(outputFile, StandardCharsets.UTF_8)
+                ),
+                StandardCharsets.UTF_8
+            );
+        } catch (IOException | FormatterException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/code-generation/language-java-ng/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java b/code-generation/language-java-ng/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
new file mode 100644
index 0000000..686b3f7
--- /dev/null
+++ b/code-generation/language-java-ng/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
@@ -0,0 +1,992 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.language.java;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.commons.text.WordUtils;
+import org.apache.plc4x.plugins.codegenerator.protocol.freemarker.BaseFreemarkerLanguageTemplateHelper;
+import org.apache.plc4x.plugins.codegenerator.protocol.freemarker.FreemarkerException;
+import org.apache.plc4x.plugins.codegenerator.protocol.freemarker.Tracer;
+import org.apache.plc4x.plugins.codegenerator.types.definitions.*;
+import org.apache.plc4x.plugins.codegenerator.types.fields.*;
+import org.apache.plc4x.plugins.codegenerator.types.references.*;
+import org.apache.plc4x.plugins.codegenerator.types.terms.*;
+
+import java.util.Map;
+import java.util.function.Function;
+
+@SuppressWarnings({"unused", "WeakerAccess"})
+public class JavaLanguageTemplateHelper extends BaseFreemarkerLanguageTemplateHelper {
+
+    private final Map<String, String> options;
+
+    public JavaLanguageTemplateHelper(TypeDefinition thisType, String protocolName, String flavorName, Map<String, TypeDefinition> types,
+                                      Map<String, String> options) {
+        super(thisType, protocolName, flavorName, types);
+        this.options = options;
+    }
+
+    public String packageName(String protocolName, String languageName, String languageFlavorName) {
+        return Optional.ofNullable(options.get("package")).orElseGet(() ->
+            "org.apache.plc4x." + String.join("", languageName.split("\\-")) + "." +
+                String.join("", protocolName.split("\\-")) + "." +
+                String.join("", languageFlavorName.split("\\-")));
+    }
+
+    @Override
+    public String getLanguageTypeNameForField(Field field) {
+        boolean optional = field instanceof OptionalField;
+        // If the referenced type is a DataIo type, the value is of type PlcValue.
+        if (field instanceof PropertyField) {
+            PropertyField propertyField = (PropertyField) field;
+            if (propertyField.getType() instanceof ComplexTypeReference) {
+                ComplexTypeReference complexTypeReference = (ComplexTypeReference) propertyField.getType();
+                final TypeDefinition typeDefinition = getTypeDefinitions().get(complexTypeReference.getName());
+                if (typeDefinition instanceof DataIoTypeDefinition) {
+                    return "PlcValue";
+                }
+            }
+        }
+        return getLanguageTypeNameForTypeReference(((TypedField) field).getType(), !optional);
+    }
+
+    public String getNonPrimitiveLanguageTypeNameForField(TypedField field) {
+        return getLanguageTypeNameForTypeReference(field.getType(), false);
+    }
+
+    public String getLanguageTypeNameForSpecType(TypeReference typeReference) {
+        return getLanguageTypeNameForTypeReference(typeReference, true);
+    }
+
+    @Override
+    public String getLanguageTypeNameForTypeReference(TypeReference typeReference) {
+        return getLanguageTypeNameForTypeReference(typeReference, false);
+    }
+
+    public String adjustLiterals(String javaType, String value) {
+        switch (javaType) {
+            case "long":
+            case "Long":
+                return value + "L";
+            default:
+                return value;
+        }
+    }
+
+    public String getLanguageTypeNameForTypeReference(TypeReference typeReference, boolean allowPrimitive) {
+        if (typeReference instanceof SimpleTypeReference) {
+            SimpleTypeReference simpleTypeReference = (SimpleTypeReference) typeReference;
+            switch (simpleTypeReference.getBaseType()) {
+                case BIT:
+                    return allowPrimitive ? boolean.class.getSimpleName() : Boolean.class.getSimpleName();
+                case BYTE:
+                    return allowPrimitive ? byte.class.getSimpleName() : Byte.class.getSimpleName();
+                case UINT:
+                    IntegerTypeReference unsignedIntegerTypeReference = (IntegerTypeReference) simpleTypeReference;
+                    if (unsignedIntegerTypeReference.getSizeInBits() <= 4) {
+                        return allowPrimitive ? byte.class.getSimpleName() : Byte.class.getSimpleName();
+                    }
+                    if (unsignedIntegerTypeReference.getSizeInBits() <= 8) {
+                        return allowPrimitive ? short.class.getSimpleName() : Short.class.getSimpleName();
+                    }
+                    if (unsignedIntegerTypeReference.getSizeInBits() <= 16) {
+                        return allowPrimitive ? int.class.getSimpleName() : Integer.class.getSimpleName();
+                    }
+                    if (unsignedIntegerTypeReference.getSizeInBits() <= 32) {
+                        return allowPrimitive ? long.class.getSimpleName() : Long.class.getSimpleName();
+                    }
+                    return BigInteger.class.getSimpleName();
+                case INT:
+                    IntegerTypeReference integerTypeReference = (IntegerTypeReference) simpleTypeReference;
+                    if (integerTypeReference.getSizeInBits() <= 8) {
+                        return allowPrimitive ? byte.class.getSimpleName() : Byte.class.getSimpleName();
+                    }
+                    if (integerTypeReference.getSizeInBits() <= 16) {
+                        return allowPrimitive ? short.class.getSimpleName() : Short.class.getSimpleName();
+                    }
+                    if (integerTypeReference.getSizeInBits() <= 32) {
+                        return allowPrimitive ? int.class.getSimpleName() : Integer.class.getSimpleName();
+                    }
+                    if (integerTypeReference.getSizeInBits() <= 64) {
+                        return allowPrimitive ? long.class.getSimpleName() : Long.class.getSimpleName();
+                    }
+                    return BigInteger.class.getSimpleName();
+                case FLOAT:
+                case UFLOAT:
+                    FloatTypeReference floatTypeReference = (FloatTypeReference) simpleTypeReference;
+                    int sizeInBits = ((floatTypeReference.getBaseType() == SimpleTypeReference.SimpleBaseType.FLOAT) ? 1 : 0) +
+                        floatTypeReference.getExponent() + floatTypeReference.getMantissa();
+                    if (sizeInBits <= 32) {
+                        return allowPrimitive ? float.class.getSimpleName() : Float.class.getSimpleName();
+                    }
+                    if (sizeInBits <= 64) {
+                        return allowPrimitive ? double.class.getSimpleName() : Double.class.getSimpleName();
+                    }
+                    return BigDecimal.class.getSimpleName();
+                case STRING:
+                case VSTRING:
+                    return String.class.getSimpleName();
+                case TIME:
+                    return LocalTime.class.getSimpleName();
+                case DATE:
+                    return LocalDate.class.getSimpleName();
+                case DATETIME:
+                    return LocalDateTime.class.getSimpleName();
+
+            }
+            throw new RuntimeException("Unsupported simple type");
+        } else {
+            return ((ComplexTypeReference) typeReference).getName();
+        }
+    }
+
+    public String getPlcValueTypeForTypeReference(TypeReference typeReference) {
+        if (typeReference instanceof SimpleTypeReference) {
+            SimpleTypeReference simpleTypeReference = (SimpleTypeReference) typeReference;
+            switch (simpleTypeReference.getBaseType()) {
+                case BIT:
+                    return "PlcBOOL";
+                case BYTE:
+                    return "PlcSINT";
+                case UINT:
+                    IntegerTypeReference unsignedIntegerTypeReference = (IntegerTypeReference) simpleTypeReference;
+                    if (unsignedIntegerTypeReference.getSizeInBits() <= 4) {
+                        return "PlcUSINT";
+                    }
+                    if (unsignedIntegerTypeReference.getSizeInBits() <= 8) {
+                        return "PlcUINT";
+                    }
+                    if (unsignedIntegerTypeReference.getSizeInBits() <= 16) {
+                        return "PlcUDINT";
+                    }
+                    if (unsignedIntegerTypeReference.getSizeInBits() <= 32) {
+                        return "PlcULINT";
+                    }
+                case INT:
+                    IntegerTypeReference integerTypeReference = (IntegerTypeReference) simpleTypeReference;
+                    if (integerTypeReference.getSizeInBits() <= 8) {
+                        return "PlcSINT";
+                    }
+                    if (integerTypeReference.getSizeInBits() <= 16) {
+                        return "PlcINT";
+                    }
+                    if (integerTypeReference.getSizeInBits() <= 32) {
+                        return "PlcDINT";
+                    }
+                    if (integerTypeReference.getSizeInBits() <= 64) {
+                        return "PlcLINT";
+                    }
+
+                case FLOAT:
+                case UFLOAT:
+                    FloatTypeReference floatTypeReference = (FloatTypeReference) simpleTypeReference;
+                    int sizeInBits = ((floatTypeReference.getBaseType() == SimpleTypeReference.SimpleBaseType.FLOAT) ? 1 : 0) +
+                        floatTypeReference.getExponent() + floatTypeReference.getMantissa();
+                    if (sizeInBits <= 32) {
+                        return "PlcREAL";
+                    }
+                    if (sizeInBits <= 64) {
+                        return "PlcLREAL";
+                    }
+                case STRING:
+                case VSTRING:
+                    return "PlcSTRING";
+                case TIME:
+                case DATE:
+                case DATETIME:
+                    return "PlcTIME";
+            }
+            throw new RuntimeException("Unsupported simple type");
+        } else {
+            return "PlcStruct";
+        }
+    }
+
+    @Override
+    public String getNullValueForTypeReference(TypeReference typeReference) {
+        if (typeReference instanceof SimpleTypeReference) {
+            SimpleTypeReference simpleTypeReference = (SimpleTypeReference) typeReference;
+            switch (simpleTypeReference.getBaseType()) {
+                case BIT:
+                    return "false";
+                case BYTE:
+                    return "0";
+                case UINT:
+                    IntegerTypeReference unsignedIntegerTypeReference = (IntegerTypeReference) simpleTypeReference;
+                    if (unsignedIntegerTypeReference.getSizeInBits() <= 16) {
+                        return "0";
+                    }
+                    if (unsignedIntegerTypeReference.getSizeInBits() <= 32) {
+                        return "0l";
+                    }
+                    return "null";
+                case INT:
+                    IntegerTypeReference integerTypeReference = (IntegerTypeReference) simpleTypeReference;
+                    if (integerTypeReference.getSizeInBits() <= 32) {
+                        return "0";
+                    }
+                    if (integerTypeReference.getSizeInBits() <= 64) {
+                        return "0l";
+                    }
+                    return "null";
+                case FLOAT:
+                    FloatTypeReference floatTypeReference = (FloatTypeReference) simpleTypeReference;
+                    int sizeInBits = ((floatTypeReference.getBaseType() == SimpleTypeReference.SimpleBaseType.FLOAT) ? 1 : 0) +
+                        floatTypeReference.getExponent() + floatTypeReference.getMantissa();
+                    if (sizeInBits <= 32) {
+                        return "0.0f";
+                    }
+                    if (sizeInBits <= 64) {
+                        return "0.0";
+                    }
+                    return "null";
+                case STRING:
+                case VSTRING:
+                    return "null";
+            }
+            throw new FreemarkerException("Unmapped basetype" + simpleTypeReference.getBaseType());
+        } else {
+            return "null";
+        }
+    }
+
+    /*public String getArgumentType(TypeReference typeReference, int index) {
+        if(typeReference instanceof ComplexTypeReference) {
+            ComplexTypeReference complexTypeReference = (ComplexTypeReference) typeReference;
+            if(!getTypeDefinitions().containsKey(complexTypeReference.getName())) {
+                throw new RuntimeException("Could not find definition of complex type " + complexTypeReference.getName());
+            }
+            TypeDefinition complexTypeDefinition = getTypeDefinitions().get(complexTypeReference.getName());
+            if(complexTypeDefinition.getParserArguments().length <= index) {
+                throw new RuntimeException("Type " + complexTypeReference.getName() + " specifies too few parser arguments");
+            }
+            return getLanguageTypeNameForSpecType(complexTypeDefinition.getParserArguments()[index].getType());
+        }
+        return "Hurz";
+    }*/
+
+    public int getNumBits(SimpleTypeReference simpleTypeReference) {
+        switch (simpleTypeReference.getBaseType()) {
+            case BIT:
+                return 1;
+            case BYTE:
+                return 8;
+            case UINT:
+            case INT:
+                IntegerTypeReference integerTypeReference = (IntegerTypeReference) simpleTypeReference;
+                return integerTypeReference.getSizeInBits();
+            case FLOAT:
+                FloatTypeReference floatTypeReference = (FloatTypeReference) simpleTypeReference;
+                return floatTypeReference.getSizeInBits();
+            case STRING:
+            case VSTRING:
+                StringTypeReference stringTypeReference = (StringTypeReference) simpleTypeReference;
+                return stringTypeReference.getSizeInBits();
+            default:
+                return 0;
+        }
+    }
+
+    @Override
+    public String getReadBufferReadMethodCall(SimpleTypeReference simpleTypeReference, String valueString, TypedField field) {
+        return getReadBufferReadMethodCall("", simpleTypeReference, valueString, field);
+    }
+
+    public String getReadBufferReadMethodCall(String logicalName, SimpleTypeReference simpleTypeReference, String valueString, TypedField field) {
+        switch (simpleTypeReference.getBaseType()) {
+            case BIT:
+                return "readBuffer.readBit(\"" + logicalName + "\")";
+            case BYTE:
+                ByteTypeReference byteTypeReference = (ByteTypeReference) simpleTypeReference;
+                return "readBuffer.readByte(\"" + logicalName + "\")";
+            case UINT:
+                IntegerTypeReference unsignedIntegerTypeReference = (IntegerTypeReference) simpleTypeReference;
+                if (unsignedIntegerTypeReference.getSizeInBits() <= 4) {
+                    return "readBuffer.readUnsignedByte(\"" + logicalName + "\", " + unsignedIntegerTypeReference.getSizeInBits() + ")";
+                }
+                if (unsignedIntegerTypeReference.getSizeInBits() <= 8) {
+                    return "readBuffer.readUnsignedShort(\"" + logicalName + "\", " + unsignedIntegerTypeReference.getSizeInBits() + ")";
+                }
+                if (unsignedIntegerTypeReference.getSizeInBits() <= 16) {
+                    return "readBuffer.readUnsignedInt(\"" + logicalName + "\", " + unsignedIntegerTypeReference.getSizeInBits() + ")";
+                }
+                if (unsignedIntegerTypeReference.getSizeInBits() <= 32) {
+                    return "readBuffer.readUnsignedLong(\"" + logicalName + "\", " + unsignedIntegerTypeReference.getSizeInBits() + ")";
+                }
+                return "readBuffer.readUnsignedBigInteger(\"" + logicalName + "\", " + unsignedIntegerTypeReference.getSizeInBits() + ")";
+            case INT:
+                IntegerTypeReference integerTypeReference = (IntegerTypeReference) simpleTypeReference;
+                if (integerTypeReference.getSizeInBits() <= 8) {
+                    return "readBuffer.readSignedByte(\"" + logicalName + "\", " + integerTypeReference.getSizeInBits() + ")";
+                }
+                if (integerTypeReference.getSizeInBits() <= 16) {
+                    return "readBuffer.readShort(\"" + logicalName + "\", " + integerTypeReference.getSizeInBits() + ")";
+                }
+                if (integerTypeReference.getSizeInBits() <= 32) {
+                    return "readBuffer.readInt(\"" + logicalName + "\", " + integerTypeReference.getSizeInBits() + ")";
+                }
+                if (integerTypeReference.getSizeInBits() <= 64) {
+                    return "readBuffer.readLong(\"" + logicalName + "\", " + integerTypeReference.getSizeInBits() + ")";
+                }
+                return "readBuffer.readBigInteger(" + integerTypeReference.getSizeInBits() + ")";
+            case FLOAT:
+                FloatTypeReference floatTypeReference = (FloatTypeReference) simpleTypeReference;
+                String type = (floatTypeReference.getSizeInBits() <= 32) ? "Float" : "Double";
+                String typeCast = (floatTypeReference.getSizeInBits() <= 32) ? "float" : "double";
+                String defaultNull = (floatTypeReference.getSizeInBits() <= 32) ? "0.0f" : "0.0";
+                return "((Supplier<" + type + ">) (() -> {" +
+                    "\n            return (" + typeCast + ") toFloat(readBuffer, \"" + logicalName + "\", " +
+                    ((floatTypeReference.getBaseType() == SimpleTypeReference.SimpleBaseType.FLOAT) ? "true" : "false") +
+                    ", " + floatTypeReference.getExponent() + ", " +
+                    floatTypeReference.getMantissa() + ");" +
+                    "\n        })).get()";
+            case STRING:
+            case VSTRING:
+                StringTypeReference stringTypeReference = (StringTypeReference) simpleTypeReference;
+                return "readBuffer.readString(\"" + logicalName + "\", " + toParseExpression(field, stringTypeReference.getLengthExpression(), null) + ", \"" +
+                    stringTypeReference.getEncoding() + "\")";
+        }
+        return "";
+    }
+
+    @Override
+    public String getWriteBufferWriteMethodCall(SimpleTypeReference simpleTypeReference, String fieldName, TypedField field) {
+        return getWriteBufferWriteMethodCall("", simpleTypeReference, fieldName, field);
+    }
+
+    public String getWriteBufferWriteMethodCall(String logicalName, SimpleTypeReference simpleTypeReference, String fieldName, TypedField field, String... writerArgs) {
+        String writerArgsString = "";
+        if (writerArgs.length > 0) {
+            writerArgsString += ", " + StringUtils.join(writerArgs, ", ");
+        }
+        switch (simpleTypeReference.getBaseType()) {
+            case BIT:
+                return "writeBuffer.writeBit(\"" + logicalName + "\", (boolean) " + fieldName + "" + writerArgsString + ")";
+            case BYTE:
+                ByteTypeReference byteTypeReference = (ByteTypeReference) simpleTypeReference;
+                return "writeBuffer.writeByte(\"" + logicalName + "\", ((Number) " + fieldName + ").byteValue()" + writerArgsString + ")";
+            case UINT:
+                IntegerTypeReference unsignedIntegerTypeReference = (IntegerTypeReference) simpleTypeReference;
+                if (unsignedIntegerTypeReference.getSizeInBits() <= 4) {
+                    return "writeBuffer.writeUnsignedByte(\"" + logicalName + "\", " + unsignedIntegerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").byteValue()" + writerArgsString + ")";
+                }
+                if (unsignedIntegerTypeReference.getSizeInBits() <= 8) {
+                    return "writeBuffer.writeUnsignedShort(\"" + logicalName + "\", " + unsignedIntegerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").shortValue()" + writerArgsString + ")";
+                }
+                if (unsignedIntegerTypeReference.getSizeInBits() <= 16) {
+                    return "writeBuffer.writeUnsignedInt(\"" + logicalName + "\", " + unsignedIntegerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").intValue()" + writerArgsString + ")";
+                }
+                if (unsignedIntegerTypeReference.getSizeInBits() <= 32) {
+                    return "writeBuffer.writeUnsignedLong(\"" + logicalName + "\", " + unsignedIntegerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").longValue()" + writerArgsString + ")";
+                }
+                return "writeBuffer.writeUnsignedBigInteger(\"" + logicalName + "\", " + unsignedIntegerTypeReference.getSizeInBits() + ", (BigInteger) " + fieldName + "" + writerArgsString + ")";
+            case INT:
+                IntegerTypeReference integerTypeReference = (IntegerTypeReference) simpleTypeReference;
+                if (integerTypeReference.getSizeInBits() <= 8) {
+                    return "writeBuffer.writeSignedByte(\"" + logicalName + "\", " + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").byteValue()" + writerArgsString + ")";
+                }
+                if (integerTypeReference.getSizeInBits() <= 16) {
+                    return "writeBuffer.writeShort(\"" + logicalName + "\", " + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").shortValue()" + writerArgsString + ")";
+                }
+                if (integerTypeReference.getSizeInBits() <= 32) {
+                    return "writeBuffer.writeInt(\"" + logicalName + "\", " + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").intValue()" + writerArgsString + ")";
+                }
+                if (integerTypeReference.getSizeInBits() <= 64) {
+                    return "writeBuffer.writeLong(\"" + logicalName + "\", " + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").longValue()" + writerArgsString + ")";
+                }
+                return "writeBuffer.writeBigInteger(\"" + logicalName + "\", " + integerTypeReference.getSizeInBits() + ", BigInteger.valueOf( " + fieldName + ")" + writerArgsString + ")";
+            case FLOAT:
+            case UFLOAT:
+                FloatTypeReference floatTypeReference = (FloatTypeReference) simpleTypeReference;
+                if (floatTypeReference.getSizeInBits() <= 32) {
+                    return "writeBuffer.writeFloat(\"" + logicalName + "\", " + fieldName + "," + floatTypeReference.getExponent() + "," + floatTypeReference.getMantissa() + "" + writerArgsString + ")";
+                } else if (floatTypeReference.getSizeInBits() <= 64) {
+                    return "writeBuffer.writeDouble(\"" + logicalName + "\", " + fieldName + "," + floatTypeReference.getExponent() + "," + floatTypeReference.getMantissa() + "" + writerArgsString + ")";
+                } else {
+                    throw new RuntimeException("Unsupported float type");
+                }
+            case STRING:
+            case VSTRING:
+                StringTypeReference stringTypeReference = (StringTypeReference) simpleTypeReference;
+                return "writeBuffer.writeString(\"" + logicalName + "\", " + toSerializationExpression(field, stringTypeReference.getLengthExpression(), thisType.getParserArguments().orElse(Collections.emptyList())) + ", \"" +
+                    stringTypeReference.getEncoding() + "\", (String) " + fieldName + "" + writerArgsString + ")";
+        }
+        throw new FreemarkerException("Unmapped basetype" + simpleTypeReference.getBaseType());
+    }
+
+    /*public String getReadMethodName(SimpleTypeReference simpleTypeReference) {
+        String languageTypeName = getLanguageTypeNameForSpecType(simpleTypeReference);
+        languageTypeName = languageTypeName.substring(0, 1).toUpperCase() + languageTypeName.substring(1);
+        if(simpleTypeReference.getBaseType().equals(SimpleTypeReference.SimpleBaseType.UINT)) {
+            return "readUnsigned" + languageTypeName;
+        } else {
+            return "read" + languageTypeName;
+        }
+    }*/
+
+    public String getReservedValue(ReservedField reservedField) {
+        final String languageTypeName = getLanguageTypeNameForTypeReference(reservedField.getType(), true);
+        if ("BigInteger".equals(languageTypeName)) {
+            return "BigInteger.valueOf(" + reservedField.getReferenceValue() + ")";
+        } else {
+            return "(" + languageTypeName + ") " + reservedField.getReferenceValue();
+        }
+    }
+
+    /*public Collection<ComplexTypeReference> getComplexTypes(ComplexTypeDefinition complexTypeDefinition) {
+        Map<String, ComplexTypeReference> types = new HashMap<>();
+        for (Field field : complexTypeDefinition.getFields()) {
+            if(field instanceof TypedField) {
+                TypedField typedField = (TypedField) field;
+                if(typedField.getType() instanceof ComplexTypeReference) {
+                    ComplexTypeReference complexTypeReference = (ComplexTypeReference) typedField.getType();
+                    types.put(complexTypeReference.getName(),  complexTypeReference);
+                }
+            } else if(field instanceof SwitchField) {
+                SwitchField switchField = (SwitchField) field;
+                for (DiscriminatedComplexTypeDefinition cas : switchField.getCases()) {
+                    types.put(cas.getName(), new ComplexTypeReference() {
+                        @Override
+                        public String getName() {
+                            return cas.getName();
+                        }
+                    });
+                }
+            }
+        }
+        return types.values();
+    }*/
+
+    /*public Collection<ComplexTypeReference> getEnumTypes(ComplexTypeDefinition complexTypeDefinition) {
+        Map<String, ComplexTypeReference> types = new HashMap<>();
+        for (Field field : complexTypeDefinition.getFields()) {
+            if(field instanceof EnumField) {
+                EnumField enumField = (EnumField) field;
+                if(enumField.getType() instanceof ComplexTypeReference) {
+                    ComplexTypeReference complexTypeReference = (ComplexTypeReference) enumField.getType();
+                    types.put(complexTypeReference.getName(),  complexTypeReference);
+                }
+            }
+        }
+        for (Field field : complexTypeDefinition.getParentPropertyFields()) {
+            if(field instanceof EnumField) {
+                EnumField enumField = (EnumField) field;
+                if(enumField.getType() instanceof ComplexTypeReference) {
+                    ComplexTypeReference complexTypeReference = (ComplexTypeReference) enumField.getType();
+                    types.put(complexTypeReference.getName(),  complexTypeReference);
+                }
+            }
+        }
+        return types.values();
+    }*/
+
+    public String toAccessExpression(TypedField field, Term term, List<Argument> parserArguments) {
+        return toExpression(field, term, variableLiteral -> {
+            if (isVariableLiteralVirtualField(variableLiteral) || isVariableLiteralDiscriminatorField(variableLiteral)) { // If we are accessing virtual|discriminator fields, we need to call the getter.
+                return "get" + StringUtils.capitalize(variableLiteral.getName()) + "()";
+            }
+            return toVariableParseExpression(field, variableLiteral, parserArguments);
+        });
+    }
+
+    public String toParseExpression(TypedField field, Term term, List<Argument> parserArguments) {
+        Tracer tracer = Tracer.start("toParseExpression");
+        return tracer + toExpression(field, term, variableLiteral -> tracer.dive("variableExpressionGenerator") + toVariableParseExpression(field, variableLiteral, parserArguments));
+    }
+
+    public String toSerializationExpression(TypedField field, Term term, List<Argument> serializerArguments) {
+        Tracer tracer = Tracer.start("toSerializationExpression");
+        return tracer + toExpression(field, term, variableLiteral -> tracer.dive("variableExpressionGenerator") + toVariableSerializationExpression(field, variableLiteral, serializerArguments));
+    }
+
+    private String toExpression(TypedField field, Term term, Function<VariableLiteral, String> variableExpressionGenerator) {
+        Tracer tracer = Tracer.start("toExpression");
+        if (term == null) {
+            return tracer + "";
+        }
+        if (term instanceof Literal) {
+            return toLiteralTermExpression((Literal) term, variableExpressionGenerator, tracer);
+        } else if (term instanceof UnaryTerm) {
+            return toUnaryTermExpression(field, (UnaryTerm) term, variableExpressionGenerator, tracer);
+        } else if (term instanceof BinaryTerm) {
+            return toBinaryTermExpression(field, (BinaryTerm) term, variableExpressionGenerator, tracer);
+        } else if (term instanceof TernaryTerm) {
+            return toTernaryTermExpression(field, (TernaryTerm) term, variableExpressionGenerator, tracer);
+        } else {
+            throw new RuntimeException("Unsupported Term type " + term.getClass().getName());
+        }
+    }
+
+    private String toLiteralTermExpression(Literal literal, Function<VariableLiteral, String> variableExpressionGenerator, Tracer tracer) {
+        tracer = tracer.dive("literal term instanceOf");
+        if (literal instanceof NullLiteral) {
+            tracer = tracer.dive("null literal instanceOf");
+            return tracer + "null";
+        } else if (literal instanceof BooleanLiteral) {
+            tracer = tracer.dive("boolean literal instanceOf");
+            return tracer + Boolean.toString(((BooleanLiteral) literal).getValue());
+        } else if (literal instanceof NumericLiteral) {
+            tracer = tracer.dive("numeric literal instanceOf");
+            return tracer + ((NumericLiteral) literal).getNumber().toString();
+        } else if (literal instanceof StringLiteral) {
+            tracer = tracer.dive("string literal instanceOf");
+            return tracer + "\"" + ((StringLiteral) literal).getValue() + "\"";
+        } else if (literal instanceof VariableLiteral) {
+            tracer = tracer.dive("variable literal instanceOf");
+            VariableLiteral variableLiteral = (VariableLiteral) literal;
+            // If this literal references an Enum type, then we have to output it differently.
+            if (getTypeDefinitions().get(variableLiteral.getName()) instanceof EnumTypeDefinition) {
+                tracer = tracer.dive("enum definition instanceOf");
+                VariableLiteral enumDefinitionChild = variableLiteral.getChild()
+                    .orElseThrow(() -> new RuntimeException("enum definitions should have childs"));
+                return tracer + variableLiteral.getName() + "." + enumDefinitionChild.getName() +
+                    enumDefinitionChild.getChild().map(child -> "." + toVariableExpressionRest(child)).orElse("");
+            } else {
+                return tracer + variableExpressionGenerator.apply(variableLiteral);
+            }
+        } else {
+            throw new RuntimeException("Unsupported Literal type " + literal.getClass().getName());
+        }
+    }
+
+    private String toUnaryTermExpression(TypedField field, UnaryTerm unaryTerm, Function<VariableLiteral, String> variableExpressionGenerator, Tracer tracer) {
+        tracer = tracer.dive("unary term instanceOf");
+        Term a = unaryTerm.getA();
+        switch (unaryTerm.getOperation()) {
+            case "!":
+                tracer = tracer.dive("case !");
+                return tracer + "!(" + toExpression(field, a, variableExpressionGenerator) + ")";
+            case "-":
+                tracer = tracer.dive("case -");
+                return tracer + "-(" + toExpression(field, a, variableExpressionGenerator) + ")";
+            case "()":
+                tracer = tracer.dive("case ()");
+                return tracer + "(" + toExpression(field, a, variableExpressionGenerator) + ")";
+            default:
+                throw new RuntimeException("Unsupported unary operation type " + unaryTerm.getOperation());
+        }
+    }
+
+    private String toBinaryTermExpression(TypedField field, BinaryTerm binaryTerm, Function<VariableLiteral, String> variableExpressionGenerator, Tracer tracer) {
+        tracer = tracer.dive("binary term instanceOf");
+        Term a = binaryTerm.getA();
+        Term b = binaryTerm.getB();
+        String operation = binaryTerm.getOperation();
+        switch (operation) {
+            case "^":
+                tracer = tracer.dive("^");
+                return tracer + "Math.pow((" + toExpression(field, a, variableExpressionGenerator) + "), (" + toExpression(field, b, variableExpressionGenerator) + "))";
+            default:
+                return tracer + "(" + toExpression(field, a, variableExpressionGenerator) + ") " + operation + " (" + toExpression(field, b, variableExpressionGenerator) + ")";
+        }
+    }
+
+    private String toTernaryTermExpression(TypedField field, TernaryTerm ternaryTerm, Function<VariableLiteral, String> variableExpressionGenerator, Tracer tracer) {
+        tracer = tracer.dive("ternary term instanceOf");
+        if ("if".equals(ternaryTerm.getOperation())) {
+            Term a = ternaryTerm.getA();
+            Term b = ternaryTerm.getB();
+            Term c = ternaryTerm.getC();
+            return tracer + "((" + toExpression(field, a, variableExpressionGenerator) + ") ? " + toExpression(field, b, variableExpressionGenerator) + " : " + toExpression(field, c, variableExpressionGenerator) + ")";
+        } else {
+            throw new RuntimeException("Unsupported ternary operation type " + ternaryTerm.getOperation());
+        }
+    }
+
+    public String toVariableEnumAccessExpression(VariableLiteral variableLiteral) {
+        return variableLiteral.getName();
+    }
+
+    private String toVariableParseExpression(TypedField field, VariableLiteral variableLiteral, List<Argument> parserArguments) {
+        Tracer tracer = Tracer.start("toVariableParseExpression");
+        // CAST expressions are special as we need to add a ".class" to the second parameter in Java.
+        if ("CAST".equals(variableLiteral.getName())) {
+            return toCastVariableParseExpression(field, variableLiteral, parserArguments, tracer);
+        } else if ("STATIC_CALL".equals(variableLiteral.getName())) {
+            return toStaticCallVariableParseExpression(field, variableLiteral, parserArguments, tracer);
+        } else if (isVariableLiteralImplicitField(variableLiteral)) { // If we are accessing implicit fields, we need to rely on a local variable instead.
+            return toImplictVariableParseExpression(variableLiteral, tracer);
+        } else if (variableLiteral.getName().equals(variableLiteral.getName().toUpperCase())) { // All uppercase names are not fields, but utility methods.
+            return toUpperCaseVariableParseExpression(field, variableLiteral, parserArguments, tracer);
+        }
+        return tracer + variableLiteral.getName() + variableLiteral.getChild().map(child -> "." + toVariableExpressionRest(child)).orElse("");
+    }
+
+    private String toUpperCaseVariableParseExpression(TypedField field, VariableLiteral variableLiteral, List<Argument> parserArguments, Tracer tracer) {
+        tracer = tracer.dive("UPPERCASE");
+        StringBuilder sb = new StringBuilder(variableLiteral.getName());
+        if (variableLiteral.getArgs().isPresent()) {
+            sb.append("(");
+            boolean firstArg = true;
+            for (Term arg : variableLiteral.getArgs().get()) {
+                if (!firstArg) {
+                    sb.append(", ");
+                }
+                sb.append(toParseExpression(field, arg, parserArguments));
+                firstArg = false;
+            }
+            sb.append(")");
+        }
+        if (variableLiteral.getIndex() != VariableLiteral.NO_INDEX) {
+            sb.append("[").append(variableLiteral.getIndex()).append("]");
+        }
+        return tracer + sb.toString() + variableLiteral.getChild().map(child -> "." + toVariableExpressionRest(child)).orElse("");
+    }
+
+    private String toImplictVariableParseExpression(VariableLiteral variableLiteral, Tracer tracer) {
+        tracer = tracer.dive("implicit");
+        return tracer + variableLiteral.getName();
+    }
+
+    private String toStaticCallVariableParseExpression(TypedField field, VariableLiteral variableLiteral, List<Argument> parserArguments, Tracer tracer) {
+        tracer = tracer.dive("STATIC_CALL");
+        StringBuilder sb = new StringBuilder();
+        List<Term> arguments = variableLiteral.getArgs().orElseThrow(() -> new RuntimeException("A STATIC_CALL expression needs arguments"));
+        if (arguments.size() < 1) {
+            throw new RuntimeException("A STATIC_CALL expression expects at least one argument.");
+        }
+        // Get the class and method name
+        String methodName = arguments.get(0).asLiteral()
+            .orElseThrow(() -> new RuntimeException("First argument should be a literal"))
+            .asStringLiteral()
+            .orElseThrow(() -> new RuntimeException("Expecting the first argument of a 'STATIC_CALL' to be a StringLiteral")).
+            getValue();
+        // Cut off the double-quotes
+        methodName = methodName.substring(1, methodName.length() - 1);
+        sb.append(methodName).append("(");
+        for (int i = 1; i < arguments.size(); i++) {
+            Term arg = arguments.get(i);
+            if (i > 1) {
+                sb.append(", ");
+            }
+            if (arg instanceof VariableLiteral) {
+                VariableLiteral variableLiteralArg = (VariableLiteral) arg;
+                // "readBuffer" is the default name of the reader argument which is always available.
+                boolean isParserArg = "readBuffer".equals(variableLiteralArg.getName());
+                boolean isTypeArg = "_type".equals(variableLiteralArg.getName());
+                if (!isParserArg && !isTypeArg && parserArguments != null) {
+                    for (Argument parserArgument : parserArguments) {
+                        if (parserArgument.getName().equals(variableLiteralArg.getName())) {
+                            isParserArg = true;
+                            break;
+                        }
+                    }
+                }
+                if (isParserArg) {
+                    sb.append(variableLiteralArg.getName()).append(variableLiteralArg.getChild().map(child -> "." + toVariableExpressionRest(child)).orElse(""));
+                } else if (isTypeArg) {// We have to manually evaluate the type information at code-generation time.
+                    String part = variableLiteralArg.getChild().map(VariableLiteral::getName).orElse("");
+                    switch (part) {
+                        case "name":
+                            sb.append("\"").append(field.getTypeName()).append("\"");
+                            break;
+                        case "length":
+                            sb.append("\"").append(((SimpleTypeReference) field).getSizeInBits()).append("\"");
+                            break;
+                        case "encoding":
+                            String encoding = ((StringTypeReference) field.getType()).getEncoding();
+                            // Cut off the single quotes.
+                            encoding = encoding.substring(1, encoding.length() - 1);
+                            sb.append("\"").append(encoding).append("\"");
+                            break;
+                    }
+                } else {
+                    sb.append(toVariableParseExpression(field, variableLiteralArg, null));
+                }
+            } else if (arg instanceof StringLiteral) {
+                sb.append(((StringLiteral) arg).getValue());
+            }
+        }
+        sb.append(")");
+        return tracer + sb.toString();
+    }
+
+    private String toCastVariableParseExpression(TypedField field, VariableLiteral variableLiteral, List<Argument> parserArguments, Tracer tracer) {
+        tracer = tracer.dive("CAST");
+        StringBuilder sb = new StringBuilder(variableLiteral.getName());
+        List<Term> arguments = variableLiteral.getArgs().orElseThrow(() -> new RuntimeException("A Cast expression needs arguments"));
+        if (arguments.size() != 2) {
+            throw new RuntimeException("A CAST expression expects exactly two arguments.");
+        }
+        VariableLiteral firstArgument = arguments.get(0).asLiteral()
+            .orElseThrow(() -> new RuntimeException("First argument should be a literal"))
+            .asVariableLiteral()
+            .orElseThrow(() -> new RuntimeException("First argument should be a Variable literal"));
+        VariableLiteral secondArgument = arguments.get(1).asLiteral().orElseThrow(() -> new RuntimeException("Second argument should be a literal"))
+            .asVariableLiteral()
+            .orElseThrow(() -> new RuntimeException("Second argument should be a Variable literal"));
+        sb.append("(")
+            .append(toVariableParseExpression(field, firstArgument, parserArguments))
+            .append(", ")
+            .append(secondArgument.getName()).append(".class)");
+        return tracer + sb.toString() + variableLiteral.getChild().map(child -> "." + toVariableExpressionRest(child)).orElse("");
+    }
+
+    private String toVariableSerializationExpression(TypedField field, VariableLiteral variableLiteral, List<Argument> serialzerArguments) {
+        Tracer tracer = Tracer.start("variable serialization expression");
+        if ("STATIC_CALL".equals(variableLiteral.getName())) {
+            return toStaticCallSerializationExpression(field, variableLiteral, serialzerArguments, tracer);
+        }
+        // All uppercase names are not fields, but utility methods.
+        else if (variableLiteral.getName().equals(variableLiteral.getName().toUpperCase())) {
+            return toUpperCaseSerializationExpression(field, variableLiteral, serialzerArguments, tracer);
+        } else if (isVariableLiteralImplicitField(variableLiteral)) { // If we are accessing implicit fields, we need to rely on a local variable instead.
+            tracer = tracer.dive("implicit field");
+            return tracer + toSerializationExpression(getReferencedImplicitField(variableLiteral), getReferencedImplicitField(variableLiteral).getSerializeExpression(), serialzerArguments);
+        } else if (isVariableLiteralVirtualField(variableLiteral)) {
+            tracer = tracer.dive("virtual field");
+            return tracer + "_value." + toVariableExpressionRest(variableLiteral);
+        }
+        // The synthetic checksumRawData is a local field and should not be accessed as bean property.
+        boolean isSerializerArg = "checksumRawData".equals(variableLiteral.getName()) || "_value".equals(variableLiteral.getName()) || "element".equals(variableLiteral.getName()) || "size".equals(variableLiteral.getName());
+        boolean isTypeArg = "_type".equals(variableLiteral.getName());
+        if (!isSerializerArg && !isTypeArg && serialzerArguments != null) {
+            for (Argument serializerArgument : serialzerArguments) {
+                if (serializerArgument.getName().equals(variableLiteral.getName())) {
+                    isSerializerArg = true;
+                    break;
+                }
+            }
+        }
+        if (isSerializerArg) {
+            tracer = tracer.dive("serializer arg");
+            return tracer + variableLiteral.getName() + variableLiteral.getChild().map(child -> "." + toVariableExpressionRest(child)).orElse("");
+        } else if (isTypeArg) {
+            tracer = tracer.dive("type arg");
+            String part = variableLiteral.getChild().map(VariableLiteral::getName).orElse("");
+            switch (part) {
+                case "name":
+                    return tracer + "\"" + field.getTypeName() + "\"";
+                case "length":
+                    return tracer + "\"" + ((SimpleTypeReference) field).getSizeInBits() + "\"";
+                case "encoding":
+                    String encoding = ((StringTypeReference) field.getType()).getEncoding();
+                    // Cut off the single quotes.
+                    encoding = encoding.substring(1, encoding.length() - 1);
+                    return tracer + "\"" + encoding + "\"";
+                default:
+                    return tracer + "";
+            }
+        } else {
+            return tracer + "_value." + toVariableExpressionRest(variableLiteral);
+        }
+    }
+
+    private String toUpperCaseSerializationExpression(TypedField field, VariableLiteral variableLiteral, List<Argument> serialzerArguments, Tracer tracer) {
+        tracer = tracer.dive("UPPER_CASE");
+        StringBuilder sb = new StringBuilder(variableLiteral.getName());
+        if (variableLiteral.getArgs().isPresent()) {
+            sb.append("(");
+            boolean firstArg = true;
+            for (Term arg : variableLiteral.getArgs().get()) {
+                if (!firstArg) {
+                    sb.append(", ");
+                }
+
+                if (arg instanceof VariableLiteral) {
+                    VariableLiteral va = (VariableLiteral) arg;
+                    boolean isSerializerArg = "readBuffer".equals(va.getName()) || "writeBuffer".equals(va.getName());
+                    boolean isTypeArg = "_type".equals(va.getName());
+                    if (!isSerializerArg && !isTypeArg && serialzerArguments != null) {
+                        for (Argument serializerArgument : serialzerArguments) {
+                            if (serializerArgument.getName().equals(va.getName())) {
+                                isSerializerArg = true;
+                                break;
+                            }
+                        }
+                    }
+                    if (isSerializerArg) {
+                        sb.append(va.getName()).append(va.getChild().map(child -> "." + toVariableExpressionRest(child)).orElse(""));
+                    } else if (isTypeArg) {
+                        String part = va.getChild().map(VariableLiteral::getName).orElse("");
+                        switch (part) {
+                            case "name":
+                                sb.append("\"").append(field.getTypeName()).append("\"");
+                                break;
+                            case "length":
+                                sb.append("\"").append(((SimpleTypeReference) field).getSizeInBits()).append("\"");
+                                break;
+                            case "encoding":
+                                String encoding = ((StringTypeReference) field.getType()).getEncoding();
+                                // Cut off the single quotes.
+                                encoding = encoding.substring(1, encoding.length() - 1);
+                                sb.append("\"").append(encoding).append("\"");
+                                break;
+                        }
+                    } else {
+                        sb.append(toVariableSerializationExpression(field, va, serialzerArguments));
+                    }
+                } else if (arg instanceof StringLiteral) {
+                    sb.append(((StringLiteral) arg).getValue());
+                }
+                firstArg = false;
+            }
+            sb.append(")");
+        }
+        return tracer + sb.toString();
+    }
+
+    private String toStaticCallSerializationExpression(TypedField field, VariableLiteral variableLiteral, List<Argument> serialzerArguments, Tracer tracer) {
+        tracer = tracer.dive("STATIC_CALL");
+        StringBuilder sb = new StringBuilder();
+        List<Term> arguments = variableLiteral.getArgs().orElseThrow(() -> new RuntimeException("A STATIC_CALL expression needs arguments"));
+        if (arguments.size() < 1) {
+            throw new RuntimeException("A STATIC_CALL expression expects at least one argument.");
+        }
+        // Get the class and method name
+        String methodName = arguments.get(0).asLiteral()
+            .orElseThrow(() -> new RuntimeException("First argument should be a literal"))
+            .asStringLiteral()
+            .orElseThrow(() -> new RuntimeException("Expecting the first argument of a 'STATIC_CALL' to be a StringLiteral")).
+            getValue();
+        methodName = methodName.substring(1, methodName.length() - 1);
+        sb.append(methodName).append("(");
+        for (int i = 1; i < arguments.size(); i++) {
+            Term arg = arguments.get(i);
+            if (i > 1) {
+                sb.append(", ");
+            }
+            if (arg instanceof VariableLiteral) {
+                VariableLiteral va = (VariableLiteral) arg;
+                // "readBuffer" and "_value" are always available in every parser.
+                boolean isSerializerArg = "readBuffer".equals(va.getName()) || "writeBuffer".equals(va.getName()) || "_value".equals(va.getName()) || "element".equals(va.getName());
+                boolean isTypeArg = "_type".equals(va.getName());
+                if (!isSerializerArg && !isTypeArg && serialzerArguments != null) {
+                    for (Argument serializerArgument : serialzerArguments) {
+                        if (serializerArgument.getName().equals(va.getName())) {
+                            isSerializerArg = true;
+                            break;
+                        }
+                    }
+                }
+                if (isSerializerArg) {
+                    sb.append(va.getName()).append(va.getChild().map(child -> "." + toVariableExpressionRest(child)).orElse(""));
+                } else if (isTypeArg) {
+                    String part = va.getChild().map(VariableLiteral::getName).orElse("");
+                    switch (part) {
+                        case "name":
+                            sb.append("\"").append(field.getTypeName()).append("\"");
+                            break;
+                        case "length":
+                            sb.append("\"").append(((SimpleTypeReference) field).getSizeInBits()).append("\"");
+                            break;
+                        case "encoding":
+                            String encoding = ((StringTypeReference) field.getType()).getEncoding();
+                            // Cut off the single quotes.
+                            encoding = encoding.substring(1, encoding.length() - 1);
+                            sb.append("\"").append(encoding).append("\"");
+                            break;
+                    }
+                } else {
+                    sb.append(toVariableSerializationExpression(field, va, serialzerArguments));
+                }
+            } else if (arg instanceof StringLiteral) {
+                sb.append(((StringLiteral) arg).getValue());
+            }
+        }
+        sb.append(")");
+        return tracer + sb.toString();
+    }
+
+    private String toVariableExpressionRest(VariableLiteral variableLiteral) {
+        Tracer tracer = Tracer.start("variable expression rest");
+        // length is kind of a keyword in mspec, so we shouldn't be naming variables length. if we ask for the length of a object we can just return length().
+        // This way we can get the length of a string when serializing
+        String variableLiteralName = variableLiteral.getName();
+        if (variableLiteralName.equals("length")) {
+            tracer = tracer.dive("length");
+            return tracer + variableLiteralName + "()" + ((variableLiteral.isIndexed() ? "[" + variableLiteral.getIndex() + "]" : "") +
+                variableLiteral.getChild().map(child -> "." + toVariableExpressionRest(child)).orElse(""));
+        }
+        return tracer + "get" + WordUtils.capitalize(variableLiteralName) + "()" + ((variableLiteral.isIndexed() ? "[" + variableLiteral.getIndex() + "]" : "") +
+            variableLiteral.getChild().map(child -> "." + toVariableExpressionRest(child)).orElse(""));
+    }
+
+    public String getSizeInBits(ComplexTypeDefinition complexTypeDefinition, List<Argument> parserArguments) {
+        int sizeInBits = 0;
+        StringBuilder sb = new StringBuilder();
+        for (Field field : complexTypeDefinition.getFields()) {
+            if (field instanceof ArrayField) {
+                ArrayField arrayField = (ArrayField) field;
+                final SimpleTypeReference type = (SimpleTypeReference) arrayField.getType();
+                switch (arrayField.getLoopType()) {
+                    case COUNT:
+                        sb.append("(").append(toSerializationExpression(null, arrayField.getLoopExpression(), parserArguments)).append(" * ").append(type.getSizeInBits()).append(") + ");
+                        break;
+                    case LENGTH:
+                        sb.append("(").append(toSerializationExpression(null, arrayField.getLoopExpression(), parserArguments)).append(" * 8) + ");
+                        break;
+                    case TERMINATED:
+                        // No terminated.
+                        break;
+                }
+            } else if (field instanceof TypedField) {
+                TypedField typedField = (TypedField) field;
+                final TypeReference type = typedField.getType();
+                if (field instanceof ManualField) {
+                    ManualField manualField = (ManualField) field;
+                    sb.append("(").append(toSerializationExpression(null, manualField.getLengthExpression(), parserArguments)).append(") + ");
+                } else if (type instanceof SimpleTypeReference) {
+                    SimpleTypeReference simpleTypeReference = (SimpleTypeReference) type;
+                    if (simpleTypeReference instanceof StringTypeReference) {
+                        sb.append(toSerializationExpression(null, ((StringTypeReference) simpleTypeReference).getLengthExpression(), parserArguments)).append(" + ");
+                    } else {
+                        sizeInBits += simpleTypeReference.getSizeInBits();
+                    }
+                } else {
+                    // No ComplexTypeReference supported.
+                }
+            }
+        }
+        return sb.toString() + sizeInBits;
+    }
+
+    public String escapeValue(TypeReference typeReference, String valueString) {
+        if (valueString == null) {
+            return null;
+        }
+        if (typeReference instanceof SimpleTypeReference) {
+            SimpleTypeReference simpleTypeReference = (SimpleTypeReference) typeReference;
+            switch (simpleTypeReference.getBaseType()) {
+                case UINT:
+                case INT:
+                    // If it's a one character string and is numeric, output it as char.
+                    if (!NumberUtils.isParsable(valueString) && (valueString.length() == 1)) {
+                        return "'" + valueString + "'";
+                    }
+                    break;
+                case STRING:
+                case VSTRING:
+                    return "\"" + valueString + "\"";
+            }
+        }
+        return valueString;
+    }
+
+}
diff --git a/code-generation/language-java-ng/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.language.LanguageOutput b/code-generation/language-java-ng/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.language.LanguageOutput
new file mode 100644
index 0000000..924bc01
--- /dev/null
+++ b/code-generation/language-java-ng/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.language.LanguageOutput
@@ -0,0 +1,19 @@
+#
+# 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
+#
+#     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.
+#
+org.apache.plc4x.language.java.JavaLanguageOutput
diff --git a/code-generation/language-java-ng/src/main/resources/templates/java/data-io-template.java.ftlh b/code-generation/language-java-ng/src/main/resources/templates/java/data-io-template.java.ftlh
new file mode 100644
index 0000000..25ebc58
--- /dev/null
+++ b/code-generation/language-java-ng/src/main/resources/templates/java/data-io-template.java.ftlh
@@ -0,0 +1,425 @@
+<#--
+  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
+
+      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.
+-->
+<#-- 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.java.JavaLanguageTemplateHelper" -->
+<#-- @ftlvariable name="tracer" type="org.apache.plc4x.plugins.codegenerator.protocol.freemarker.Tracer" -->
+<#-- @ftlvariable name="type" type="org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition" -->
+${helper.packageName(protocolName, languageName, outputFlavor)?replace(".", "/")}/io/${type.name}IO.java
+/*
+ * 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
+ *
+ *   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.
+ */
+package ${helper.packageName(protocolName, languageName, outputFlavor)}.io;
+
+import static org.apache.plc4x.java.spi.generation.StaticHelper.*;
+
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.api.value.*;
+import org.apache.plc4x.java.spi.generation.EvaluationHelper;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.apache.plc4x.java.spi.generation.WriteBufferByteBased;
+import org.apache.plc4x.java.spi.generation.ByteOrder;
+import ${helper.packageName(protocolName, languageName, outputFlavor)}.*;
+import ${helper.packageName(protocolName, languageName, outputFlavor)}.types.*;
+import org.apache.plc4x.java.spi.values.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.math.BigInteger;
+import java.time.*;
+import java.util.*;
+import java.util.function.Supplier;
+
+// 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 class ${type.name}IO {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(${type.name}IO.class);
+    public static PlcValue staticParse(ReadBuffer readBuffer<#if type.parserArguments.isPresent()>, <#list type.parserArguments.orElseThrow() as parserArgument>${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)} ${parserArgument.name}<#sep>, </#sep></#list></#if>) throws ParseException {
+        <#assign defaultCaseOutput=false>
+        <#assign dataIoTypeDefinition=type.asDataIoTypeDefinition().orElseThrow()>
+        <#list dataIoTypeDefinition.switchField.cases as case>
+            <#if case.discriminatorValues?has_content>
+            <@compress single_line=true>
+            if(
+                <#list case.discriminatorValues as discriminatorValue>
+                    <#assign discriminatorExpression=dataIoTypeDefinition.switchField.discriminatorExpressions[discriminatorValue?index]>
+                EvaluationHelper.equals(
+                    ${helper.toParseExpression(null, discriminatorExpression, parserArguments)}
+                    ,
+                    <#if helper.discriminatorValueNeedsStringEqualityCheck(discriminatorExpression)>
+                        "${discriminatorValue}"
+                    <#elseif helper.isComplexTypeReference(parserArguments[discriminatorValue?index].type)>
+                        <#if helper.isEnumTypeReference(parserArguments[discriminatorValue?index].type)>
+                            ${helper.getLanguageTypeNameForTypeReference(parserArguments[discriminatorValue?index].type, false)}
+                            .
+                            ${discriminatorValue}
+                        <#else>
+                        ${discriminatorValue}
+                        </#if>
+                    <#else>
+            ${discriminatorValue}
+                    </#if>
+                )
+                <#sep> && </#sep>
+                </#list>
+            )
+            </...@compress>
+            <#else>
+                <#assign defaultCaseOutput=true>
+            </#if>{ // ${case.name}
+            <#assign valueDefined=false>
+            <#list case.fields as field>
+                <#switch field.typeName>
+                    <#case "array">
+                        <#assign arrayField=field.asArrayField().orElseThrow()>
+            // Array field (${arrayField.name})
+            <#-- Only update curPos if the length expression uses it -->
+                        <#if arrayField.loopExpression.contains("curPos")>
+            curPos = readBuffer.getPos() - startPos;
+                        </#if>
+            <#-- If this is a count array, we can directly initialize an array with the given size -->
+                        <#if field.isCountArrayField()>
+            // Count array
+            if(${helper.toParseExpression(arrayField, arrayField.loopExpression,parserArguments)} > Integer.MAX_VALUE) {
+                throw new ParseException("Array count of " + (${helper.toParseExpression(arrayField, arrayField.loopExpression,parserArguments)}) + " exceeds the maximum allowed count of " + Integer.MAX_VALUE);
+            }
+            List<PlcValue> ${arrayField.name};
+            {
+                int itemCount = (int) ${helper.toParseExpression(arrayField, arrayField.loopExpression,parserArguments)};
+                ${arrayField.name} = new LinkedList<>();
+                for(int curItem = 0; curItem < itemCount; curItem++) {
+                    ${arrayField.name}.add(new ${helper.getPlcValueTypeForTypeReference(arrayField.type)}((${helper.getNonPrimitiveLanguageTypeNameForField(arrayField)}) <#if helper.isSimpleTypeReference(arrayField.type)>${helper.getReadBufferReadMethodCall(arrayField.type.asSimpleTypeReference().orElseThrow(), "", arrayField)})<#else>${arrayField.type.asComplexTypeReference().orElseThrow().name}IO.staticParse(readBuffer<#if field.params.isPresent()>, <#list arrayField.params.orElseThrow [...]
+                }
+            }
+            <#-- 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 arrayField.isLengthArrayField()>
+            // Length array
+            long _${arrayField.name}Length = ${helper.toParseExpression(arrayField, arrayField.loopExpression,parserArguments)};
+            long ${arrayField.name}EndPos = readBuffer.getPos() + _${arrayField.name}Length;
+            List<PlcValue> value = new LinkedList<>();
+            while(readBuffer.getPos() < ${arrayField.name}EndPos) {
+                value.add(
+                <#if helper.isSimpleTypeReference(arrayField.type)>
+                    new ${helper.getPlcValueTypeForTypeReference(arrayField.type)}(${helper.getReadBufferReadMethodCall(arrayField.type.asSimpleTypeReference().orElseThrow(), "", arrayField)})
+                <#else>${arrayField.type.asComplexTypeReference().orElseThrow().name}IO.staticParse(readBuffer
+                    <#if arrayField.params.isPresent()>,
+                        <#list arrayField.params.orElseThrow() as parserArgument>
+                            (${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(arrayField.type, parserArgument?index), true)}) (${helper.toParseExpression(arrayField, parserArgument,parserArguments)})
+                            <#sep>, </#sep>
+                        </#list>
+                    </#if>
+                    )
+                </#if>
+                );
+            }
+            <#-- A terminated array keeps on reading data as long as the termination expression evaluates to false -->
+                            <#elseif arrayField.isTerminatedArrayField()>
+            // Terminated array
+            List<${helper.getNonPrimitiveLanguageTypeNameForField(arrayField)}> ${arrayField.name} = new LinkedList<>();
+            while(!((boolean) (${helper.toParseExpression(arrayField, arrayField.loopExpression,parserArguments)}))) {
+                ${arrayField.name}.add(<#if helper.isSimpleTypeReference(arrayField.type)>${helper.getReadBufferReadMethodCall(arrayField.type.asSimpleTypeReference().orElseThrow(), "", arrayField)}<#else>${arrayField.type.asComplexTypeReference().orElseThrow().name}IO.staticParse(readBuffer<#if arrayField.params.isPresent()>, <#list arrayField.params.orElseThrow() as parserArgument>(${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(arrayField.type, parserArgument?index [...]
+
+                <#-- After parsing, update the current position, but only if it's needed -->
+                                <#if arrayField.loopExpression.contains("curPos")>
+                curPos = readBuffer.getPos() - startPos;
+                                </#if>
+            }
+                            </#if>
+                        </#if>
+                        <#if arrayField.name == "value">
+                            <#assign valueDefined=true>
+                        </#if>
+                    <#break>
+                    <#case "const">
+                        <#assign constField=field.asConstField().orElseThrow()>
+
+            // Const Field (${constField.name})
+            ${helper.getNonPrimitiveLanguageTypeNameForField(constField)} ${constField.name} = ${helper.getReadBufferReadMethodCall(constField.type.asSimpleTypeReference().orElseThrow(), "", constField)};
+            if(${constField.name} != ${dataIoTypeDefinition.name}.${constField.name?upper_case}) {
+                throw new ParseException("Expected constant value " + ${dataIoTypeDefinition.name}.${constField.name?upper_case} + " but got " + ${constField.name});
+            }
+                        <#if constField.name == "value">
+                            <#assign valueDefined=true>
+                        </#if>
+                    <#break>
+                    <#case "enum">
+                        <#assign enumField=field.asEnumField().orElseThrow()>
+
+            // Enum field (${enumField.name})
+            ${helper.getNonPrimitiveLanguageTypeNameForField(enumField)} ${enumField.name} = ${helper.getNonPrimitiveLanguageTypeNameForField(enumField)}.enumForValue(${helper.getReadBufferReadMethodCall(helper.getEnumBaseTypeReference(enumField.type.asSimpleTypeReference().orElseThrow()), "", enumField)});
+                        <#if enumField.name == "value">
+                            <#assign valueDefined=true>
+                        </#if>
+                    <#break>
+                    <#case "manual">
+                        <#assign manualField=field.asManualField().orElseThrow()>
+
+            // Manual Field (${manualField.name})
+            ${helper.getLanguageTypeNameForField(field)} ${manualField.name} = (${helper.getLanguageTypeNameForField(manualField)}) (${helper.toParseExpression(manualField, manualField.parseExpression,parserArguments)});
+                        <#if manualField.name == "value">
+                            <#assign valueDefined=true>
+                        </#if>
+                    <#break>
+                    <#case "reserved">
+                        <#assign reservedField=field.asReservedField().orElseThrow()>
+
+            // Reserved Field (Compartmentalized so the "reserved" variable can't leak)
+            {
+                ${helper.getLanguageTypeNameForField(field)} reserved = ${helper.getReadBufferReadMethodCall(reservedField.type.asSimpleTypeReference().orElseThrow(), "", reservedField)};
+                if(reserved != ${helper.getReservedValue(reservedField)}) {
+                    LOGGER.info("Expected constant value " + ${reservedField.referenceValue} + " but got " + reserved + " for reserved field.");
+                }
+            }
+                    <#break>
+                    <#case "simple">
+                        <#assign simpleField=field.asSimpleField().orElseThrow()>
+
+                        <#if helper.isEnumField(simpleField)>
+            // Enum field (${simpleField.name})
+            ${helper.getNonPrimitiveLanguageTypeNameForField(simpleField)} ${simpleField.name} = ${helper.getNonPrimitiveLanguageTypeNameForField(simpleField)}.enumForValue(${helper.getReadBufferReadMethodCall(helper.getEnumBaseTypeReference(simpleField.type), "", simpleField)});
+                        <#else>
+            // Simple Field (${simpleField.name})
+            ${helper.getNonPrimitiveLanguageTypeNameForField(simpleField)} ${simpleField.name} = <#if helper.isSimpleTypeReference(simpleField.type)>${helper.getReadBufferReadMethodCall(simpleField.type.asSimpleTypeReference().orElseThrow(), "", simpleField)}<#else>${simpleField.type.asComplexTypeReference().orElseThrow().name}IO.staticParse(readBuffer<#if simpleField.params.isPresent()>, <#list field.params.orElseThrow() as parserArgument>(${helper.getLanguageTypeNameForTypeReference(he [...]
+                        </#if>
+                        <#if case.name == "Struct" ||
+                            ((case.name == "DATE_AND_TIME") && ((simpleField.name == "year") || (simpleField.name == "month") || (simpleField.name == "day") || (simpleField.name == "hour") || (simpleField.name == "minutes") || (simpleField.name == "seconds"))) ||
+                            ((case.name == "DATE_AND_TIME") && (simpleField.name == "secondsSinceEpoch")) ||
+                            ((case.name == "DATE") && ((simpleField.name == "year") || (simpleField.name == "month") || (simpleField.name == "day"))) ||
+                            ((case.name == "TIME_OF_DAY") && ((simpleField.name == "hour") || (simpleField.name == "minutes") || (simpleField.name == "seconds"))) ||
+                        simpleField.name == "value">
+                            <#assign valueDefined=true>
+                        </#if>
+                    <#break>
+                </#switch>
+            </#list>
+            <#if case.name == "Struct">
+
+                <#-- In this case we need to wrap each field in a PlcValue that matches it's natural type -->
+            Map<String, PlcValue> _map = new HashMap<>();
+                <#list case.fields as field>
+                    <#if field.isArrayField()>
+                        <#assign field=field.asArrayField().orElseThrow()>
+            _map.put("${field.name}", new PlcList(${field.name}));
+                    <#elseif field.isPropertyField()>
+                        <#assign field=field.asPropertyField().orElseThrow()>
+                        <#switch helper.getLanguageTypeNameForTypeReference(field.type)>
+                            <#case "Boolean">
+            _map.put("${field.name}", new PlcBOOL(${field.name}));
+                                <#break>
+                            <#case "Byte">
+            _map.put("${field.name}", new PlcSINT(${field.name}));
+                                <#break>
+                            <#case "Short">
+            _map.put("${field.name}", new PlcINT(${field.name}));
+                                <#break>
+                            <#case "Integer">
+            _map.put("${field.name}", new PlcDINT(${field.name}));
+                                <#break>
+                            <#case "Long">
+            _map.put("${field.name}", new PlcLINT(${field.name}));
+                                <#break>
+                            <#case "BigInteger">
+            _map.put("${field.name}", new PlcBigInteger(${field.name}));
+                                <#break>
+                            <#case "Float">
+            _map.put("${field.name}", new PlcREAL(${field.name}));
+                                <#break>
+                            <#case "Double">
+            _map.put("${field.name}", new PlcLREAL(${field.name}));
+                                <#break>
+                            <#case "BigDecimal">
+            _map.put("${field.name}", new PlcBigDecimal(${field.name}));
+                                <#break>
+                            <#case "String">
+            _map.put("${field.name}", new PlcSTRING(${field.name}));
+                                <#break>
+                            <#case "LocalTime">
+            _map.put("${field.name}", new PlcTIME_OF_DAY(${field.name}));
+                                <#break>
+                            <#case "LocalDate">
+            _map.put("${field.name}", new PlcDATE(${field.name}));
+                                <#break>
+                            <#case "LocalDateTime">
+            _map.put("${field.name}", new PlcDATE_AND_TIME(${field.name}));
+                                <#break>
+                        </#switch>
+                    </#if>
+                </#list>
+                <#assign valueDefined=true>
+            </#if>
+
+            <#if valueDefined>
+                <#switch case.name>
+                    <#case "TIME">
+            return new PlcTIME(value);
+                    <#break>
+                    <#case "DATE">
+                    <#if helper.hasFieldsWithNames(case.fields, "year", "month", "day")>
+            LocalDate value = LocalDate.of(year.intValue(), (month == 0) ? 1 : month.intValue(), (day == 0) ? 1 : day.intValue());
+                    </#if>
+            return new PlcDATE(value);
+                    <#break>
+                    <#case "TIME_OF_DAY">
+                    <#if helper.hasFieldsWithNames(case.fields, "hour", "minutes", "seconds", "nanos")>
+            LocalTime value = LocalTime.of(hour.intValue(), minutes.intValue(), seconds.intValue(), nanos.intValue());
+                    <#elseif helper.hasFieldsWithNames(case.fields, "hour", "minutes", "seconds")>
+            LocalTime value = LocalTime.of(hour.intValue(), minutes.intValue(), seconds.intValue());
+                    </#if>
+            return new PlcTIME_OF_DAY(value);
+                    <#break>
+                    <#case "DATE_AND_TIME">
+                    <#if helper.hasFieldsWithNames(case.fields, "year", "month", "day", "hour", "minutes", "seconds", "nanos")>
+            LocalDateTime value = LocalDateTime.of(year.intValue(), (month == 0) ? 1 : month.intValue(), (day == 0) ? 1 : day.intValue(), hour.intValue(), minutes.intValue(), seconds.intValue(), nanos.intValue());
+                    <#elseif helper.hasFieldsWithNames(case.fields, "year", "month", "day", "hour", "minutes", "seconds")>
+            LocalDateTime value = LocalDateTime.of(year.intValue(), (month == 0) ? 1 : month.intValue(), (day == 0) ? 1 : day.intValue(), hour.intValue(), minutes.intValue(), seconds.intValue());
+                    <#elseif helper.hasFieldsWithNames(case.fields, "secondsSinceEpoch")>
+            LocalDateTime value = LocalDateTime.ofEpochSecond(secondsSinceEpoch, 0, ZoneOffset.UTC);
+                    </#if>
+            return new PlcDATE_AND_TIME(value);
+                    <#break>
+                    <#case "Struct">
+            return new PlcStruct(_map);
+                    <#break>
+                    <#case "List">
+            return new PlcList(value);
+                    <#break>
+                    <#default>
+            return new Plc${case.name}(value);
+                </#switch>
+            </#if>
+        }<#sep> else </#sep></#list>
+        <#if !defaultCaseOutput>
+        return null;
+        </#if>
+    }
+
+<#if outputFlavor != "passive">
+    public static WriteBufferByteBased staticSerialize(PlcValue _value<#if type.parserArguments.isPresent()>, <#list type.parserArguments.orElseThrow() as parserArgument>${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)} ${parserArgument.name}<#sep>, </#sep></#list></#if>) throws ParseException {
+        return staticSerialize(_value<#if type.parserArguments.isPresent()>, <#list type.parserArguments.orElseThrow() as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if>, ByteOrder.BIG_ENDIAN);
+    }
+
+    public static WriteBufferByteBased staticSerialize(PlcValue _value<#if type.parserArguments.isPresent()>, <#list type.parserArguments.orElseThrow() as parserArgument>${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)} ${parserArgument.name}<#sep>, </#sep></#list></#if>, ByteOrder byteOrder) throws ParseException {
+        <#assign defaultCaseOutput=false>
+        <#assign dataIoTypeDefinition=type.asDataIoTypeDefinition().orElseThrow()>
+        <#list dataIoTypeDefinition.switchField.cases as case><#if case.discriminatorValues?has_content>if(<#list case.discriminatorValues as discriminatorValue>EvaluationHelper.equals(${helper.toParseExpression(null, dataIoTypeDefinition.switchField.discriminatorExpressions[discriminatorValue?index], type.parserArguments.orElseThrow())},<#if helper.discriminatorValueNeedsStringEqualityCheck(dataIoTypeDefinition.switchField.discriminatorExpressions[discriminatorValue?index])>"${discrimin [...]
+        WriteBufferByteBased writeBuffer = new WriteBufferByteBased((int) Math.ceil(((float) ${helper.getSizeInBits(case,parserArguments)}) / 8.0f), byteOrder);
+
+            <#list case.fields as field>
+                <#switch field.typeName>
+                    <#case "array">
+                        <#assign arrayField=field.asArrayField().orElseThrow()>
+            PlcList values = (PlcList) _value;
+
+                        <#if case.name == "Struct">
+            for (PlcValue val : ((List<PlcValue>) values.getStruct().get("${arrayField.name}").getList())) {
+                ${helper.getLanguageTypeNameForField(arrayField)} value = (${helper.getLanguageTypeNameForField(arrayField)}) val.get${helper.getLanguageTypeNameForField(arrayField)?cap_first}();
+                ${helper.getWriteBufferWriteMethodCall(arrayField.type.asSimpleTypeReference().orElseThrow(), "value", arrayField)};
+            }
+                        <#else>
+            for (PlcValue val : ((List<PlcValue>) values.getList())) {
+                ${helper.getLanguageTypeNameForField(arrayField)} value = (${helper.getLanguageTypeNameForField(arrayField)}) val.get${helper.getLanguageTypeNameForField(arrayField)?cap_first}();
+                ${helper.getWriteBufferWriteMethodCall(arrayField.type.asSimpleTypeReference().orElseThrow(), "(" + arrayField.name + ")", arrayField)};
+            }
+                        </#if>
+
+                    <#if case.name == "BOOL">
+            while (writeBuffer.getPos() < writeBuffer.getData().length) {
+                writeBuffer.writeBit(false);
+            }
+                    </#if>
+                    <#break>
+                    <#case "const">
+                        <#assign constField=field.asConstField().orElseThrow()>
+            // Const Field (${constField.name})
+            ${helper.getWriteBufferWriteMethodCall(constField.type.asSimpleTypeReference().orElseThrow(), constField.referenceValue, constField)};
+                    <#break>
+                    <#case "enum">
+                        <#assign enumField=field.asEnumField().orElseThrow()>
+            // Enum field (${enumField.name})
+            ${helper.getLanguageTypeNameForField(field)} ${enumField.name} = (${helper.getLanguageTypeNameForField(field)}) _value.get${enumField.name?cap_first}();
+            ${helper.getWriteBufferWriteMethodCall(helper.getEnumBaseTypeReference(field.asTypedField().orElseThrow().type), "(" + enumField.name + ".getValue())", enumField)};
+                    <#break>
+                    <#case "manual">
+                        <#assign manualField=field.asManualField().orElseThrow()>
+            // Manual Field (${manualField.name})
+            ${helper.toSerializationExpression(manualField, manualField.serializeExpression, type.parserArguments.orElse(null))};
+                    <#break>
+                    <#case "reserved">
+                        <#assign reservedField=field.asReservedField().orElseThrow()>
+            // Reserved Field
+            ${helper.getWriteBufferWriteMethodCall(reservedField.type.asSimpleTypeReference().orElseThrow(), helper.getReservedValue(reservedField), reservedField)};
+                    <#break>
+                    <#case "simple">
+                        <#assign simpleField=field.asSimpleField().orElseThrow()>
+            // Simple Field (${simpleField.name})
+                        <#if case.name == "Struct">
+            ${helper.getLanguageTypeNameForField(simpleField)} ${simpleField.name} = (${helper.getLanguageTypeNameForField(field)}) _value.getStruct().get("${simpleField.name}").get${helper.getLanguageTypeNameForField(simpleField)?cap_first}();
+                        <#else>
+                            <#if simpleField.name == "value">
+            ${helper.getLanguageTypeNameForField(simpleField)} ${simpleField.name} = (${helper.getLanguageTypeNameForField(field)}) _value.get${helper.getLanguageTypeNameForField(simpleField)?cap_first}();
+                            <#else>
+                                <#-- Just for now -->
+            ${helper.getLanguageTypeNameForField(simpleField)} ${simpleField.name} = ${helper.getNullValueForTypeReference(simpleField.type)};
+                            </#if>
+                        </#if>
+                        <#if helper.isSimpleTypeReference(simpleField.type)>
+            ${helper.getWriteBufferWriteMethodCall(simpleField.type.asSimpleTypeReference().orElseThrow(), "(" + simpleField.name + ")", simpleField)};
+                        <#else>
+            ${simpleField.type.asComplexTypeReference().orElseThrow().name}IO.staticSerialize(writeBuffer, ${simpleField.name});
+                        </#if>
+                    <#break>
+                </#switch>
+            </#list>
+            return writeBuffer;
+        }<#sep> else </#sep></#list>
+        <#if !defaultCaseOutput>
+        return null;
+        </#if>
+    }
+</#if>
+
+}
+</#outputformat>
diff --git a/code-generation/language-java-ng/src/main/resources/templates/java/enum-package-info-template.java.ftlh b/code-generation/language-java-ng/src/main/resources/templates/java/enum-package-info-template.java.ftlh
new file mode 100644
index 0000000..8c79f5b
--- /dev/null
+++ b/code-generation/language-java-ng/src/main/resources/templates/java/enum-package-info-template.java.ftlh
@@ -0,0 +1,50 @@
+<#--
+  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
+
+      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.
+-->
+<#-- 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.java.JavaLanguageTemplateHelper" -->
+<#-- @ftlvariable name="tracer" type="org.apache.plc4x.plugins.codegenerator.protocol.freemarker.Tracer" -->
+${helper.packageName(protocolName, languageName, outputFlavor)?replace(".", "/")}/types/package-info.java
+/*
+ * 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
+ *
+ *   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.
+ */
+
+// Code generated by code-generation. DO NOT EDIT.
+
+package ${helper.packageName(protocolName, languageName, outputFlavor)}.types;
+</#outputformat>
\ No newline at end of file
diff --git a/code-generation/language-java-ng/src/main/resources/templates/java/enum-template.java.ftlh b/code-generation/language-java-ng/src/main/resources/templates/java/enum-template.java.ftlh
new file mode 100644
index 0000000..321932f
--- /dev/null
+++ b/code-generation/language-java-ng/src/main/resources/templates/java/enum-template.java.ftlh
@@ -0,0 +1,144 @@
+<#--
+<#--
+  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
+
+      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.
+-->
+<#-- 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.java.JavaLanguageTemplateHelper" -->
+<#-- @ftlvariable name="tracer" type="org.apache.plc4x.plugins.codegenerator.protocol.freemarker.Tracer" -->
+<#-- @ftlvariable name="type" type="org.apache.plc4x.plugins.codegenerator.types.definitions.EnumTypeDefinition" -->
+${helper.packageName(protocolName, languageName, outputFlavor)?replace(".", "/")}/types/${type.name}.java
+/*
+ * 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
+ *
+ *   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.
+ */
+package ${helper.packageName(protocolName, languageName, outputFlavor)}.types;
+
+import org.apache.plc4x.java.spi.generation.Message;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+// Code generated by code-generation. DO NOT EDIT.
+
+public enum ${type.name} {
+
+<#list type.enumValues as enumValue>
+    ${enumValue.name}(<#if type.type?has_content>(${helper.getLanguageTypeNameForTypeReference(type.type, true)}) <#if type.type.isStringTypeReference()>"${enumValue.value}"<#elseif helper.isComplexTypeReference(type.type)><#if helper.isEnumTypeReference(type.type)>${helper.getLanguageTypeNameForTypeReference(type.type, true)}.${enumValue.value}<#else>${enumValue.value}</#if><#else>${enumValue.value}</#if></#if><#if type.constantNames?has_content><#if type.type?has_content>, </#if><#list [...]
+</#sep></#list>;
+
+<#if type.type?has_content>
+    private static final Logger logger = LoggerFactory.getLogger(${type.name}.class);
+
+    private static final Map<${helper.getLanguageTypeNameForTypeReference(type.type, false)}, ${type.name}> map;
+    static {
+        map = new HashMap<>();
+        for (${type.name} value : ${type.name}.values()) {
+            map.put((${helper.getLanguageTypeNameForTypeReference(type.type, true)}) value.getValue(), value);
+        }
+    }
+
+    private ${helper.getLanguageTypeNameForTypeReference(type.type, true)} value;
+</#if>
+<#if type.constantNames?has_content>
+    <#list type.constantNames as constantName>
+        private ${helper.getLanguageTypeNameForTypeReference(type.getConstantType(constantName), true)} ${constantName};
+    </#list>
+</#if>
+
+    ${type.name}(<#if type.type?has_content>${helper.getLanguageTypeNameForTypeReference(type.type, true)} value</#if><#if type.constantNames?has_content><#if type.type?has_content>, </#if><#list type.constantNames as constantName>${helper.getLanguageTypeNameForTypeReference(type.getConstantType(constantName), true)} ${constantName}<#sep>, </#sep></#list></#if>) {
+<#if type.type?has_content>        this.value = value;</#if>
+<#if type.constantNames?has_content>
+    <#list type.constantNames as constantName>
+        this.${constantName} = ${constantName};
+    </#list>
+</#if>
+    }
+
+<#if type.type?has_content>
+    public ${helper.getLanguageTypeNameForTypeReference(type.type, true)} getValue() {
+        return value;
+    }
+</#if>
+
+<#if type.constantNames?has_content>
+    <#list type.constantNames as constantName>
+    public ${helper.getLanguageTypeNameForTypeReference(type.getConstantType(constantName), true)} get${constantName?cap_first}() {
+        return ${constantName};
+    }
+
+    public static ${type.name} firstEnumForField${constantName?cap_first}(${helper.getLanguageTypeNameForTypeReference(type.getConstantType(constantName), true)} fieldValue) {
+        for (${type.name} _val : ${type.name}.values()) {
+            if(_val.get${constantName?cap_first}() == fieldValue) {
+                return _val;
+            }
+        }
+        return null;
+    }
+
+    public static List<${type.name}> enumsForField${constantName?cap_first}(${helper.getLanguageTypeNameForTypeReference(type.getConstantType(constantName), true)} fieldValue) {
+        List<${type.name}> _values = new ArrayList();
+        for (${type.name} _val : ${type.name}.values()) {
+            if(_val.get${constantName?cap_first}() == fieldValue) {
+                _values.add(_val);
+            }
+        }
+        return _values;
+    }
+
+    </#list>
+</#if>
+<#if type.type?has_content>
+    public static ${type.name} enumForValue(${helper.getLanguageTypeNameForTypeReference(type.type, true)} value) {
+        if (!map.containsKey(value)) {
+            logger.error("No ${type.name} for value {}", value);
+        }
+        return map.get(value);
+    }
+</#if>
+
+<#if type.type?has_content>
+    public static Boolean isDefined(${helper.getLanguageTypeNameForTypeReference(type.type, true)} value) {
+        return map.containsKey(value);
+    }
+</#if>
+
+}
+</#outputformat>
diff --git a/code-generation/language-java-ng/src/main/resources/templates/java/io-template.java.ftlh b/code-generation/language-java-ng/src/main/resources/templates/java/io-template.java.ftlh
new file mode 100644
index 0000000..ac31675
--- /dev/null
+++ b/code-generation/language-java-ng/src/main/resources/templates/java/io-template.java.ftlh
@@ -0,0 +1,936 @@
+<#--
+  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
+
+      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.
+-->
+<#-- 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.java.JavaLanguageTemplateHelper" -->
+<#-- @ftlvariable name="tracer" type="org.apache.plc4x.plugins.codegenerator.protocol.freemarker.Tracer" -->
+<#-- @ftlvariable name="type" type="org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition" -->
+${helper.packageName(protocolName, languageName, outputFlavor)?replace(".", "/")}/io/${type.name}IO.java
+/*
+ * 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
+ *
+ *   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.
+ */
+package ${helper.packageName(protocolName, languageName, outputFlavor)}.io;
+
+import static org.apache.plc4x.java.spi.generation.StaticHelper.*;
+
+import ${helper.packageName(protocolName, languageName, outputFlavor)}.*;
+<#if helper.getComplexTypeReferences()?has_content>import ${helper.packageName(protocolName, languageName, outputFlavor)}.io.*;</#if>
+import ${helper.packageName(protocolName, languageName, outputFlavor)}.types.*;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.spi.generation.*;
+import org.apache.plc4x.java.api.value.PlcValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.math.BigInteger;
+import java.time.*;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Supplier;
+
+// 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 class ${type.name}IO implements <#if outputFlavor != "passive">MessageIO<${type.name}, ${type.name}><#else>MessageInput<${type.name}></#if> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(${type.name}IO.class);
+
+<#-- The parse and serialize methods here are just proxies for forwardning the requests to static counterparts -->
+<#if !type.isDiscriminatedChildTypeDefinition()>
+    @Override
+    public ${type.name} 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 ${type.name}IO.staticParse(readBuffer<#if parserArguments?has_content>, <#list parserArguments as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if>);
+    }
+
+    <#if outputFlavor != "passive">
+    @Override
+    public void serialize(WriteBuffer writeBuffer, ${type.name} value, Object... args) throws ParseException {
+        <#if helper.getSerializerArguments(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 helper.getSerializerArguments(parserArguments) as serializerArgument>
+        if(!(args[${serializerArgument?index}] instanceof ${helper.getLanguageTypeNameForTypeReference(serializerArgument.type, false)})) {
+            throw new PlcRuntimeException("Argument ${serializerArgument?index} expected to be of type ${helper.getLanguageTypeNameForTypeReference(serializerArgument.type, false)} but was " + args[${serializerArgument?index}].getClass().getName());
+        }
+        ${helper.getLanguageTypeNameForTypeReference(serializerArgument.type, false)} ${serializerArgument.name} = (${helper.getLanguageTypeNameForTypeReference(serializerArgument.type, false)}) args[${serializerArgument?index}];
+        </#list>
+        </#if>
+        ${type.name}IO.staticSerialize(writeBuffer, value<#if helper.getSerializerArguments(parserArguments)?has_content>, <#list helper.getSerializerArguments(parserArguments) as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if>);
+    }
+
+    </#if>
+<#else>
+    @Override
+    public ${type.name} parse(ReadBuffer readBuffer, Object... args) throws ParseException {
+        return (${type.name}) new ${type.parentType.name}IO().parse(readBuffer, args);
+    }
+
+<#if outputFlavor != "passive">
+    @Override
+    public void serialize(WriteBuffer writeBuffer, ${type.name} value, Object... args) throws ParseException {
+        new ${type.parentType.name}IO().serialize(writeBuffer, value, args);
+    }
+
+</#if>
+</#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>
+    <#assign hasParentParserArguments=(type.parentType?? && type.parentType.parserArguments.isPresent() && type.parentType.parserArguments.orElseThrow()?filter(arg -> hasParserArguments && !parserArguments?map(argument->argument.name)?seq_contains(arg.name) || !hasParserArguments)?has_content)>
+    <#assign parentParserArgumentList><#if hasParentParserArguments><#list type.parentType.parserArguments.orElseThrow()?filter(arg -> hasParserArguments && !parserArguments?map(argument->argument.name)?seq_contains(arg.name) || !hasParserArguments) as parserArgument>${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)} ${parserArgument.name}<#sep>, </#sep></#list></#if></#assign>
+    public static ${type.name}<#if type.isDiscriminatedChildTypeDefinition()>Builder</#if> staticParse(ReadBuffer readBuffer<#if hasParserArguments>, ${parserArgumentList}</#if><#if hasParentParserArguments>, ${parentParserArgumentList}</#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()>
+        <#if arrayField.type.isByteBased()>
+        // Byte Array field (${arrayField.name})
+            <#assign numberOfBytesExpression>
+                <#compress>
+                    <#if field.isCountArrayField()>
+                        int numberOfBytes = ${helper.toParseExpression(arrayField, arrayField.loopExpression, parserArguments)};
+                    <#elseif field.isLengthArrayField()>
+                        int numberOfBytes = ${helper.toParseExpression(arrayField, arrayField.loopExpression, parserArguments)};
+                    <#elseif field.isTerminatedArrayField()>
+                    <#-- TODO: we need to find out to implement this-->
+                        int numberOfBytes := ${helper.toParseExpression(arrayField, arrayField.loopExpression, parserArguments)};
+                    <#else>
+                    <#-- TODO: we should throw a exception here-->
+                        int numberOfBytes := -1
+                    </#if>
+                </#compress>
+            </#assign>
+        ${numberOfBytesExpression}
+        byte[] ${arrayField.name} = readBuffer.readByteArray("${arrayField.name}", numberOfBytes);
+        <#else>
+        // Array field (${arrayField.name})
+        readBuffer.pullContext("${arrayField.name}", WithReaderWriterArgs.WithRenderAsList(true));
+            <#-- Only update curPos if the length expression uses it -->
+            <#if arrayField.loopExpression.contains("curPos")>
+        curPos = readBuffer.getPos() - startPos;
+            </#if>
+        <#-- If this is a count array, we can directly initialize an array with the given size -->
+            <#if field.isCountArrayField()>
+        // Count array
+        if(${helper.toParseExpression(arrayField, arrayField.loopExpression, parserArguments)} > Integer.MAX_VALUE) {
+            throw new ParseException("Array count of " + (${helper.toParseExpression(arrayField, arrayField.loopExpression, parserArguments)}) + " exceeds the maximum allowed count of " + Integer.MAX_VALUE);
+        }
+        ${helper.getLanguageTypeNameForField(field)}[] ${arrayField.name};
+        {
+            int itemCount = Math.max(0, (int) ${helper.toParseExpression(arrayField, arrayField.loopExpression, parserArguments)});
+            ${arrayField.name} = new ${helper.getLanguageTypeNameForField(field)}[itemCount];
+            for(int curItem = 0; curItem < itemCount; curItem++) {
+                <#-- When parsing simple types, there is nothing that could require the "lastItem" -->
+                <#if !helper.isSimpleTypeReference(arrayField.type)>boolean lastItem = curItem == (itemCount - 1);</#if>
+            <@compress single_line=true>
+                ${arrayField.name}[curItem] =
+                <#if helper.isSimpleTypeReference(arrayField.type)>
+                    ${helper.getReadBufferReadMethodCall("", arrayField.type.asSimpleTypeReference().orElseThrow(), "", arrayField)}
+                <#else>
+                    ${arrayField.type.asComplexTypeReference().orElseThrow().name}IO.staticParse(readBuffer
+                    <#if field.params.isPresent()>
+                        ,
+                        <#list field.params.orElseThrow() as parserArgument>
+                            <#if helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(arrayField.type, parserArgument?index), true) = 'String'>
+                                ${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(arrayField.type, parserArgument?index), true)}.valueOf(${helper.toParseExpression(arrayField, parserArgument, parserArguments)})<#sep>, </#sep>
+                            <#else>
+                                (${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(arrayField.type, parserArgument?index), true)}) (${helper.toParseExpression(arrayField, parserArgument, parserArguments)})<#sep>, </#sep>
+                            </#if>
+                        </#list>
+                    </#if>
+                    <#assign typeDefinition=helper.getTypeDefinitionForTypeReference(arrayField.type)>
+                    <#-- We expose the parentParserArguments to the child here too-->
+                    <#assign hasParentParseArguments=typeDefinition.parentType?? && typeDefinition.parentType.parserArguments.isPresent()>
+                    <#assign parentParseArguments><#if hasParentParseArguments><#list typeDefinition.parentType.parserArguments.orElseThrow() as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if></#assign>
+                    <#if hasParentParseArguments>, ${tracer.dive("count array parent parse arguments")} ${parentParseArguments}</#if>
+                    )
+                    <#if helper.getTypeDefinitionForTypeReference(arrayField.type).isDiscriminatedChildTypeDefinition()>
+                        .build()
+                    </#if>
+                </#if>;
+            </...@compress>
+            }
+        }
+        <#-- 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()>
+        // Length array
+        long _${arrayField.name}Length = ${helper.toParseExpression(arrayField, arrayField.loopExpression, parserArguments)};
+        List<${helper.getNonPrimitiveLanguageTypeNameForField(arrayField)}> _${arrayField.name}List = new LinkedList<>();
+        long ${arrayField.name}EndPos = readBuffer.getPos() + _${arrayField.name}Length;
+        while(readBuffer.getPos() < ${arrayField.name}EndPos) {
+                    <@compress single_line=true>
+            _${arrayField.name}List.add(
+                        <#if helper.isSimpleTypeReference(arrayField.type)>
+                            ${helper.getReadBufferReadMethodCall("", arrayField.type.asSimpleTypeReference().orElseThrow(), "", arrayField)}
+                        <#else>
+                            ${arrayField.type.asComplexTypeReference().orElseThrow().name}IO.staticParse(readBuffer
+                            <#if field.params.isPresent()>
+                                ,
+                                <#list field.params.orElseThrow() as parserArgument>
+                                    (${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(arrayField.type, parserArgument?index), true)})
+                                    (${helper.toParseExpression(arrayField, parserArgument, parserArguments)})<#sep>, </#sep>
+                                </#list>
+                            </#if>
+                            <#assign typeDefinition=helper.getTypeDefinitionForTypeReference(arrayField.type)>
+                        <#-- We expose the parentParserArguments to the child here too-->
+                            <#assign hasParentParseArguments=typeDefinition.parentType?? && typeDefinition.parentType.parserArguments.isPresent()>
+                            <#assign parentParseArguments><#if hasParentParseArguments><#list typeDefinition.parentType.parserArguments.orElseThrow() as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if></#assign>
+                            <#if hasParentParseArguments>, ${tracer.dive("count array parent parse arguments")} ${parentParseArguments}</#if>
+                            )
+                        </#if>
+                        );
+                    </...@compress>
+            <#-- After parsing, update the current position, but only if it's needed -->
+                <#if arrayField.loopExpression.contains("curPos")>
+            curPos = readBuffer.getPos() - startPos;
+                </#if>
+        }
+            <#-- A terminated array keeps on reading data as long as the termination expression evaluates to false -->
+                <#elseif field.isTerminatedArrayField()>
+        // Terminated array
+        List<${helper.getNonPrimitiveLanguageTypeNameForField(arrayField)}> _${arrayField.name}List = new LinkedList<>();
+        while(!((boolean) (${helper.toParseExpression(arrayField, arrayField.loopExpression, parserArguments)}))) {
+                    <@compress single_line=true>
+            _${arrayField.name}List.add(
+                        <#if helper.isSimpleTypeReference(arrayField.type)>
+                            ${helper.getReadBufferReadMethodCall("", arrayField.type.asSimpleTypeReference().orElseThrow(), "", arrayField)}
+                        <#else>
+                            ${arrayField.type.asComplexTypeReference().orElseThrow().name}IO.staticParse(readBuffer
+                            <#if field.params.isPresent()>
+                                ,
+                                <#list field.params.orElseThrow() as parserArgument>
+                                    (${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(arrayField.type, parserArgument?index), true)})
+                                    (${helper.toParseExpression(arrayField, parserArgument, parserArguments)})<#sep>, </#sep>
+                                </#list>
+                            </#if>
+                            <#assign typeDefinition=helper.getTypeDefinitionForTypeReference(arrayField.type)>
+                        <#-- We expose the parentParserArguments to the child here too-->
+                            <#assign hasParentParseArguments=typeDefinition.parentType?? && typeDefinition.parentType.parserArguments.isPresent()>
+                            <#assign parentParseArguments><#if hasParentParseArguments><#list typeDefinition.parentType.parserArguments.orElseThrow() as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if></#assign>
+                            <#if hasParentParseArguments>, ${tracer.dive("count array parent parse arguments")} ${parentParseArguments}</#if>
+                            )
+                        </#if>
+                        );
+                    </...@compress>
+
+            <#-- After parsing, update the current position, but only if it's needed -->
+                <#if arrayField.loopExpression.contains("curPos")>
+            curPos = readBuffer.getPos() - startPos;
+                </#if>
+        }
+                </#if>
+                <#--
+                    Convert the list into an array. However if the array is of a primitive
+                    type we have to iterate over it's elements and explicitly cast them.
+                    Otherwise a simple toArray call is fine.
+                -->
+                <#if helper.isSimpleTypeReference(arrayField.type)>
+        ${helper.getLanguageTypeNameForField(field)}[] ${arrayField.name} = new ${helper.getLanguageTypeNameForField(field)}[_${arrayField.name}List.size()];
+        for(int i = 0; i < _${arrayField.name}List.size(); i++) {
+            ${arrayField.name}[i] = (${helper.getLanguageTypeNameForField(field)}) _${arrayField.name}List.get(i);
+        }
+                <#else>
+        ${helper.getLanguageTypeNameForField(field)}[] ${arrayField.name} = _${arrayField.name}List.toArray(new ${helper.getNonPrimitiveLanguageTypeNameForField(arrayField)}[0]);
+                </#if>
+            </#if>
+            readBuffer.closeContext("${arrayField.name}", WithReaderWriterArgs.WithRenderAsList(true));
+        </#if>
+        <#break>
+    <#case "checksum">
+        <#assign checksumField = field.asChecksumField().orElseThrow()>
+        <#assign simpleTypeReference = checksumField.type.asSimpleTypeReference().orElseThrow()>
+
+        // Checksum Field (checksum)
+        {
+            ${helper.getLanguageTypeNameForField(field)} checksum = ${helper.getNullValueForTypeReference(checksumField.type)};
+            ${helper.getLanguageTypeNameForField(field)} checksumRef = ${helper.getReadBufferReadMethodCall("checksum", simpleTypeReference, "", checksumField)};
+            checksum = (${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(checksumField, checksumField.checksumExpression, parserArguments)});
+            if(checksum != checksumRef) {
+                throw new ParseException(String.format("Checksum verification failed. Expected %04X but got %04X", checksumRef & 0xFFFF, checksum & 0xFFFF));
+            }
+        }
+        <#break>
+    <#case "const">
+        <#assign constField = field.asConstField().orElseThrow()>
+
+        // Const Field (${constField.name})
+        <#if helper.isSimpleTypeReference(constField.type)>
+            <#assign simpleTypeReference = constField.type.asSimpleTypeReference().orElseThrow()>
+        ${helper.getLanguageTypeNameForField(field)} ${constField.name} = ${helper.getReadBufferReadMethodCall(constField.name, simpleTypeReference, "", constField)};
+        <#else>
+        ${helper.getLanguageTypeNameForField(field)} ${constField.name} = ${helper.getLanguageTypeNameForField(field)}.enumForValue(${helper.getReadBufferReadMethodCall(constField.name, helper.getEnumBaseTypeReference(constField.type), "", constField)});
+        </#if>
+        if(${constField.name} != ${type.name}.${constField.name?upper_case}) {
+            throw new ParseException("Expected constant value " + ${type.name}.${constField.name?upper_case} + " but got " + ${constField.name});
+        }
+        <#break>
+    <#case "discriminator">
+        <#assign discriminatorField = field.asDiscriminatorField().orElseThrow()>
+
+        // Discriminator Field (${discriminatorField.name}) (Used as input to a switch field)
+        <#if helper.isEnumField(field)>
+        ${helper.getLanguageTypeNameForField(discriminatorField)} ${discriminatorField.name} = ${helper.getLanguageTypeNameForField(discriminatorField)}.enumForValue(${helper.getReadBufferReadMethodCall(discriminatorField.name, helper.getEnumBaseTypeReference(discriminatorField.type), "", discriminatorField)});
+        <#else>
+            <#assign simpleTypeReference = discriminatorField.type.asSimpleTypeReference().orElseThrow()>
+            <@compress single_line=true>
+        ${helper.getLanguageTypeNameForField(discriminatorField)} ${discriminatorField.name} =
+                <#if helper.isSimpleTypeReference(discriminatorField.type)>
+                    ${helper.getReadBufferReadMethodCall(discriminatorField.name, simpleTypeReference, "", discriminatorField)}
+                <#else>
+                    ${discriminatorField.type.asComplexTypeReference().orElseThrow().name}IO.staticParse(readBuffer
+                    <#if discriminatorField.params.isPresent()>
+                        ,
+                        <#list discriminatorField.params.orElseThrow() as parserArgument>
+                            (${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(simpleTypeReference, parserArgument?index), true)})
+                            (${helper.toParseExpression(discriminatorField, parserArgument, null)})<#sep>, </#sep>
+                        </#list>
+                    </#if>
+                    )
+                </#if>;
+            </...@compress>
+        </#if>
+
+        <#break>
+    <#case "enum">
+        <#assign enumField = field.asEnumField().orElseThrow()>
+
+        readBuffer.pullContext("${enumField.name}");
+        // Enum field (${enumField.name})
+        <#if enumField.fieldName.isPresent()>
+        ${helper.getLanguageTypeNameForField(field)} ${enumField.name} = ${helper.getLanguageTypeNameForField(field)}.firstEnumForField${enumField.fieldName.orElseThrow()?cap_first}(${helper.getReadBufferReadMethodCall(helper.getTypeDefinitionForTypeReference(enumField.type).name, helper.getEnumBaseTypeReference(enumField.type), "", enumField)});
+        <#else>
+        ${helper.getLanguageTypeNameForField(field)} ${enumField.name} = ${helper.getLanguageTypeNameForField(field)}.enumForValue(${helper.getReadBufferReadMethodCall(helper.getTypeDefinitionForTypeReference(enumField.type).name, helper.getEnumBaseTypeReference(enumField.type), "", enumField)});
+        </#if>
+        readBuffer.closeContext("${enumField.name}");
+        <#break>
+    <#case "implicit">
+        <#assign implicitField = field.asImplicitField().orElseThrow()>
+        <#assign simpleTypeReference = implicitField.type.asSimpleTypeReference().orElseThrow()>
+
+        // Implicit Field (${implicitField.name}) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+        ${helper.getLanguageTypeNameForField(field)} ${implicitField.name} = ${helper.getReadBufferReadMethodCall(implicitField.name, simpleTypeReference, "", implicitField)};
+        <#break>
+    <#case "manualArray">
+        <#assign manualArrayField = field.asManualArrayField().orElseThrow()>
+
+        readBuffer.pullContext("${manualArrayField.name}", WithReaderWriterArgs.WithRenderAsList(true));
+        // Manual Array Field (${manualArrayField.name})
+        <#-- Only update curPos if the length expression uses it -->
+        <#if manualArrayField.loopExpression.contains("curPos")>
+        curPos = readBuffer.getPos() - startPos;
+        </#if>
+        <#-- If this is a count array, we can directly initialize an array with the given size -->
+        <#if field.isCountArrayField()>
+        // Count array
+        int _${manualArrayField.name}Count = ${helper.toParseExpression(manualArrayField, manualArrayField.loopExpression, parserArguments)};
+        ${helper.getLanguageTypeNameForField(field)}[] ${manualArrayField.name} = new ${helper.getLanguageTypeNameForField(field)}[_${manualArrayField.name}Count];
+        for(int i = 0; i < _${manualArrayField.name}Count; i++) {
+            ${manualArrayField.name}[i] = (${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(manualArrayField, manualArrayField.parseExpression, 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()>
+        // Length array
+        long _${manualArrayField.name}Length = ${helper.toParseExpression(manualArrayField, manualArrayField.loopExpression, parserArguments)};
+        List<${helper.getNonPrimitiveLanguageTypeNameForField(manualArrayField)}> _${manualArrayField.name}List = new LinkedList<>();
+        long ${manualArrayField.name}EndPos = readBuffer.getPos() + _${manualArrayField.name}Length;
+        while(readBuffer.getPos() < ${manualArrayField.name}EndPos) {
+            _${manualArrayField.name}List.add((${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(manualArrayField, manualArrayField.parseExpression, parserArguments)}));
+            <#-- After parsing, update the current position, but only if it's needed -->
+            <#if manualArrayField.loopExpression.contains("curPos")>
+            curPos = readBuffer.getPos() - startPos;
+            </#if>
+        }
+            <#-- A terminated array keeps on reading data as long as the termination expression evaluates to false -->
+            <#elseif field.isTerminatedArrayField()>
+        // Terminated array
+        List<${helper.getNonPrimitiveLanguageTypeNameForField(manualArrayField)}> _${manualArrayField.name}List = new LinkedList<>();
+        while(!((boolean) (${helper.toParseExpression(manualArrayField, manualArrayField.loopExpression, parserArguments)}))) {
+            _${manualArrayField.name}List.add((${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(manualArrayField, manualArrayField.parseExpression, parserArguments)}));
+
+            <#-- After parsing, update the current position, but only if it's needed -->
+            <#if manualArrayField.loopExpression.contains("curPos")>
+            curPos = readBuffer.getPos() - startPos;
+            </#if>
+        }
+            </#if>
+            <#--
+                Convert the list into an array. However if the array is of a primitive
+                type we have to iterate over it's elements and explicitly cast them.
+                Otherwise a simple toArray call is fine.
+            -->
+            <#if helper.isSimpleTypeReference(field.type)>
+        ${helper.getLanguageTypeNameForField(field)}[] ${manualArrayField.name} = new ${helper.getLanguageTypeNameForField(field)}[_${manualArrayField.name}List.size()];
+        for(int i = 0; i < _${manualArrayField.name}List.size(); i++) {
+            ${manualArrayField.name}[i] = (${helper.getLanguageTypeNameForField(field)}) _${manualArrayField.name}List.get(i);
+        }
+            <#else>
+        ${helper.getLanguageTypeNameForField(field)}[] ${manualArrayField.name} = _${manualArrayField.name}List.toArray(new ${helper.getNonPrimitiveLanguageTypeNameForField(manualArrayField)}[0]);
+            </#if>
+        </#if>
+        readBuffer.closeContext("${manualArrayField.name}", WithReaderWriterArgs.WithRenderAsList(true));
+        <#break>
+    <#case "manual">
+        <#assign manualField = field.asManualField().orElseThrow()>
+
+        // Manual Field (${manualField.name})
+        ${helper.getLanguageTypeNameForField(field)} ${manualField.name} = (${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(manualField, manualField.parseExpression, parserArguments)});
+        <#break>
+    <#case "optional">
+        <#assign optionalField = field.asOptionalField().orElseThrow()>
+
+        // Optional Field (${optionalField.name}) (Can be skipped, if a given expression evaluates to false)
+        ${helper.getLanguageTypeNameForField(optionalField)} ${optionalField.name} = null;
+        <#-- TODO: we need to initalize base types-->
+        curPos = readBuffer.getPos();
+        try {
+        <#if optionalField.conditionExpression.present && optionalField.conditionExpression.orElseThrow().contains("curPos")>
+        curPos = readBuffer.getPos();
+        </#if>
+        <#if optionalField.conditionExpression.present>
+        if(${helper.toParseExpression(optionalField, optionalField.conditionExpression.get(), parserArguments)}) {
+        </#if>
+        <@compress single_line=true>
+            ${optionalField.name} =
+            <#if helper.isEnumField(optionalField)>
+             ${helper.getLanguageTypeNameForField(optionalField)}.enumForValue(
+                ${helper.getReadBufferReadMethodCall(optionalField.name, helper.getEnumBaseTypeReference(optionalField.type), "", optionalField)}
+            )
+            <#elseif helper.isComplexTypeReference(optionalField.type)>
+                <#assign complexTypeReference=optionalField.type.asComplexTypeReference().orElseThrow()>
+                <#assign typeName=complexTypeReference.name>
+                <#assign typeDefinition=helper.getTypeDefinitionForTypeReference(complexTypeReference)>
+                <#if typeDefinition.isDiscriminatedChildTypeDefinition()>
+                    <#-- Usually you don't use child directly unless they are parameterized #-->
+                    <#assign typeName=typeDefinition.parentType.name>
+                    <#assign typeDefinition=typeDefinition.parentType>
+                </#if>
+                <#-- We downcast to the referenced type-->
+            (${complexTypeReference.name}) ${typeName}IO.staticParse(
+                readBuffer
+                <#if field.params.isPresent()>,
+                    <#list field.params.orElseThrow() as parserArgument>
+                        (${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(optionalField.type, parserArgument?index), true)})
+                        (${helper.toParseExpression(optionalField, parserArgument, parserArguments)})<#sep>, </#sep>
+                    </#list>
+                </#if>
+                <#if complexTypeReference.params.isPresent() && typeDefinition.parserArguments.isPresent()>
+                    <#assign argumentOffset=0>
+                    <#if field.params.isPresent()><#assign argumentOffset=field.params.orElseThrow()?size></#if>
+                    ,
+                    <#list complexTypeReference.params.orElseThrow() as typeParam>
+                    <#-- We cast here explicitly as java sometimes can't infer the type. e.g. 0 is a int and not a byte#-->
+                        (${helper.getLanguageTypeNameForTypeReference(typeDefinition.parserArguments.orElseThrow()[typeParam?index + argumentOffset].type, true)})
+                        ${helper.toAccessExpression(null, typeParam, null)}<#sep>, </#sep>
+                    </#list>
+                </#if>
+                )
+            <#else>
+             ${helper.getReadBufferReadMethodCall(optionalField.name, optionalField.type.asSimpleTypeReference().orElseThrow(), "", optionalField)}
+            </#if>
+            ;
+        </...@compress>
+
+        <#if optionalField.conditionExpression.present>
+        }
+        </#if>
+        } catch(ParseAssertException ignore){
+            readBuffer.reset(curPos);
+        }
+        <#break>
+    <#case "assert">
+        <#assign assertField = field.asAssertField().orElseThrow()>
+
+        // Assert Field
+        <#if helper.isEnumField(assertField)>
+        ${helper.getLanguageTypeNameForField(field)} ${assertField.name} = ${helper.getLanguageTypeNameForField(assertField)}.enumForValue(${helper.getReadBufferReadMethodCall(assertField.name, helper.getEnumBaseTypeReference(simpleTypeReference), "", assertField)});
+        <#elseif helper.isComplexTypeReference(assertField.type)>
+            <#assign complexTypeReference=assertField.type.asComplexTypeReference().orElseThrow()>
+            <#assign typeName=complexTypeReference.name>
+            <#assign typeDefinition=helper.getTypeDefinitionForTypeReference(complexTypeReference)>
+            <#if typeDefinition.isDiscriminatedChildTypeDefinition()>
+                <#assign typeName=typeDefinition.getParentType().name>
+            </#if>
+            <@compress single_line=true>
+        ${helper.getLanguageTypeNameForField(field)} ${assertField.name} = (${complexTypeReference.name}) ${typeName}IO.staticParse(readBuffer
+                <#if field.params.isPresent()>
+                    ,
+                    <#list field.params.orElseThrow() as parserArgument>
+                        (${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(simpleTypeReference, parserArgument?index), true)})
+                        (${helper.toParseExpression(assertField, parserArgument, parserArguments)})<#sep>, </#sep>
+                    </#list>
+                </#if>
+                <#if complexTypeReference.params.isPresent()>
+                    ,
+                    <#list complexTypeReference.params.orElseThrow() as typeParam>
+                        ${helper.toAccessExpression(null, typeParam, null)}<#sep>, </#sep>
+                    </#list>
+                </#if>);
+            </...@compress>
+        <#else>
+            <#assign simpleTypeReference = assertField.type.asSimpleTypeReference().orElseThrow()>
+        ${helper.getLanguageTypeNameForField(field)} ${assertField.name} = ${helper.getReadBufferReadMethodCall(assertField.name, simpleTypeReference.asSimpleTypeReference().orElseThrow(), "", assertField)};
+        </#if>
+        if(${assertField.name} != ${helper.toParseExpression(assertField.asTypedField().orElseThrow(), assertField.conditionExpression, parserArguments)}) {
+            throw new ParseAssertException("assertField ${assertField.name} with value " + ${assertField.name} + " didn't match the expected value " + ${helper.toParseExpression(assertField, assertField.conditionExpression, parserArguments)});
+        }
+        <#break>
+    <#case "padding">
+        <#assign paddingField = field.asPaddingField().orElseThrow()>
+        <#assign simpleTypeReference = paddingField.type.asSimpleTypeReference().orElseThrow()>
+
+        // Padding Field (padding)
+        {
+            readBuffer.pullContext("padding", WithReaderWriterArgs.WithRenderAsList(true));
+            int _timesPadding = (int) (${helper.toParseExpression(paddingField, paddingField.paddingCondition, parserArguments)});
+            while ((readBuffer.hasMore(${helper.getNumBits(simpleTypeReference)})) && (_timesPadding-- > 0)) {
+                // Just read the padding data and ignore it
+                ${helper.getReadBufferReadMethodCall(simpleTypeReference, "", paddingField)};
+            }
+            readBuffer.closeContext("padding", WithReaderWriterArgs.WithRenderAsList(true));
+        }
+        <#break>
+    <#case "reserved">
+        <#assign reservedField = field.asReservedField().orElseThrow()>
+        <#assign simpleTypeReference = reservedField.type.asSimpleTypeReference().orElseThrow()>
+
+        // Reserved Field (Compartmentalized so the "reserved" variable can't leak)
+        {
+            ${helper.getLanguageTypeNameForField(field)} reserved = ${helper.getReadBufferReadMethodCall("reserved", simpleTypeReference, "", reservedField)};
+            if(reserved != ${helper.getReservedValue(reservedField)}) {
+                LOGGER.info("Expected constant value " + ${reservedField.referenceValue} + " but got " + reserved + " for reserved field.");
+            }
+        }
+        <#break>
+    <#case "simple">
+        <#assign simpleField = field.asSimpleField().orElseThrow()>
+
+        <#if !helper.isSimpleTypeReference(simpleField.type)>
+        readBuffer.pullContext("${simpleField.name}");
+        </#if>
+
+        // Simple Field (${simpleField.name})
+        <@compress single_line=true>
+        ${helper.getLanguageTypeNameForField(simpleField)} ${simpleField.name} =
+        <#assign simpleFieldLogicalName><#if helper.isSimpleTypeReference(simpleField.type)>${simpleField.name}<#else>${simpleField.typeName}</#if></#assign>
+        <#if helper.isEnumField(simpleField)>
+        /* enum based simple field with type ${simpleField.type.name} */
+        ${helper.getLanguageTypeNameForField(simpleField)}.enumForValue(
+            ${helper.getReadBufferReadMethodCall(simpleField.type.name, helper.getEnumBaseTypeReference(simpleField.type), "", simpleField)}
+        );
+        <#elseif helper.isComplexTypeReference(simpleField.type)>
+            <#assign complexTypeReference = simpleField.type.asComplexTypeReference().orElseThrow()>
+            <#assign typeName=complexTypeReference.name>
+            <#assign typeDefinition = helper.getTypeDefinitionForTypeReference(complexTypeReference)>
+            <#if typeDefinition.isDiscriminatedChildTypeDefinition()>
+                <#-- Usually you don't use child directly unless they are parameterized #-->
+                <#assign typeName=typeDefinition.parentType.name>
+                <#assign typeDefinition=typeDefinition.parentType>
+            </#if>
+                <#-- We downcast to the referenced type -->
+        (${simpleField.type.name}) ${typeName}IO.staticParse(readBuffer
+            <#if field.params.isPresent()>,
+                <#list field.params.orElseThrow() as parserArgument>
+                    <#if helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(complexTypeReference, parserArgument?index), true) = 'String'>
+            ${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(complexTypeReference, parserArgument?index), true)}
+                .valueOf(${helper.toParseExpression(simpleField, parserArgument, parserArguments)})<#sep>, </#sep>
+                    <#else>
+                (${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(complexTypeReference, parserArgument?index), true)})
+                (${helper.toParseExpression(simpleField, parserArgument, parserArguments)})<#sep>, </#sep>
+                    </#if>
+                </#list>
+            </#if>
+            <#if complexTypeReference.params.isPresent() && typeDefinition.parserArguments.isPresent()>
+                <#assign argumentOffset=0>
+                <#if field.params.isPresent()><#assign argumentOffset=field.params.orElseThrow()?size></#if>
+            ,
+                    <#list complexTypeReference.params.orElseThrow() as typeParam>
+                            <#-- We cast here explicitly as java sometimes can't infer the type. e.g. 0 is a int and not a byte#-->
+            (${helper.getLanguageTypeNameForTypeReference(typeDefinition.parserArguments.orElseThrow()[typeParam?index + argumentOffset].type, true)})
+            ${helper.toAccessExpression(null, typeParam, null)}<#sep>, </#sep>
+                    </#list>
+            </#if>
+                )
+        <#else>
+        ${helper.getReadBufferReadMethodCall(simpleFieldLogicalName, simpleField.type.asSimpleTypeReference().orElseThrow(), "", simpleField)}
+        </#if>
+            ;
+        </...@compress>
+
+        <#if !helper.isSimpleTypeReference(simpleField.type)>
+        readBuffer.closeContext("${simpleField.name}");
+        </#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.discriminatorValues?has_content>
+                if(
+                <#list case.discriminatorValues as discriminatorValue>
+                    <#assign discriminatorExpression=switchField.discriminatorExpressions[discriminatorValue?index].asLiteral().orElseThrow().asVariableLiteral().orElseThrow()>
+                    EvaluationHelper.equals(
+                    ${helper.toParseExpression(null, discriminatorExpression, parserArguments)},
+                    <#if helper.discriminatorValueNeedsStringEqualityCheck(discriminatorExpression)>
+                        ${tracer.dive("discriminatorValueNeedsStringEqualityCheck")}"${discriminatorValue}"
+                    <#elseif helper.isComplexTypeReference(helper.getDiscriminatorTypes()[discriminatorExpression.name])>
+                        <#if helper.isEnumTypeReference(helper.getDiscriminatorTypes()[discriminatorExpression.name])>
+                            ${tracer.dive("isEnumTypeReference")}${helper.getDiscriminatorTypes()[discriminatorExpression.name].asComplexTypeReference().orElseThrow().name}.${discriminatorValue}
+                        <#else>
+                            ${tracer.dive("!isEnumTypeReference")}${discriminatorValue}
+                        </#if>
+                    <#else>
+                        ${tracer.dive("else")}${discriminatorValue}
+                    </#if>
+                    )
+                    <#sep> && </#sep>
+                    </#list>
+                )
+            </#if>{
+            </...@compress>
+            <@compress single_line=true>
+            <#assign hasCaseParseArguments=case.parserArguments.isPresent() && case.parserArguments.orElseThrow()?has_content>
+            <#assign caseParseArguments><#if hasCaseParseArguments><#list case.parserArguments.orElseThrow() as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if></#assign>
+            <#-- We expose the parentParserArguments to the child here too-->
+            <#assign hasParentCaseParseArguments=case.parentType?? && case.parentType.parserArguments.isPresent() && case.parentType.parserArguments.orElseThrow()?filter(arg -> hasCaseParseArguments && !case.parserArguments.orElseThrow()?map(argument->argument.name)?seq_contains(arg.name) || !hasCaseParseArguments)?has_content>
+            <#assign parentCaseParseArguments><#if hasParentCaseParseArguments><#list case.parentType.parserArguments.orElseThrow()?filter(arg -> hasCaseParseArguments && !case.parserArguments.orElseThrow()?map(argument->argument.name)?seq_contains(arg.name) || !hasCaseParseArguments) as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if></#assign>
+            builder = ${case.name}IO.staticParse(readBuffer<#if hasCaseParseArguments>, ${tracer.dive("case parse arguments")} ${caseParseArguments}</#if><#if hasParentCaseParseArguments>, ${tracer.dive("case parent parse arguments")} ${parentCaseParseArguments}</#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 simpleTypeReference = unknownField.type.asSimpleTypeReference().orElseThrow()>
+
+        // Unknown Field
+        ${helper.getReadBufferReadMethodCall("unknown" , simpleTypeReference, "", unknownField)};
+        <#break>
+
+    <#case "virtual">
+        <#assign virtualField = field.asVirtualField().orElseThrow()>
+
+        // Virtual field (Just declare a local variable so we can access it in the parser)
+        <#if helper.getLanguageTypeNameForField(field) = 'String'>
+        ${helper.getLanguageTypeNameForField(field)} ${virtualField.name} = ${helper.getLanguageTypeNameForField(field)}.valueOf(${helper.toParseExpression(virtualField, virtualField.valueExpression, parserArguments)});
+        <#else>
+        ${helper.getLanguageTypeNameForField(field)} ${virtualField.name} = (${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(virtualField, virtualField.valueExpression, parserArguments)});
+        </#if>
+        <#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 outputFlavor != "passive">
+    public static void staticSerialize(WriteBuffer writeBuffer, ${type.name} _value<#if helper.getSerializerArguments(parserArguments)?has_content>, <#list helper.getSerializerArguments(parserArguments) as parserArgument>${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)} ${parserArgument.name}<#sep>, </#sep></#list></#if>) throws ParseException {
+    <#if helper.hasFieldOfType("unknown")>
+        throw new ParseException("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()>
+
+        // Array Field (${arrayField.name})
+        if(_value.get${arrayField.name?cap_first}() != null) {
+            <#if arrayField.type.isByteBased()>
+            // Byte Array field (${arrayField.name})
+            writeBuffer.writeByteArray("${arrayField.name}", _value.get${arrayField.name?cap_first}());
+            <#else>
+            writeBuffer.pushContext("${arrayField.name}", WithReaderWriterArgs.WithRenderAsList(true));
+            int itemCount = (int) _value.get${arrayField.name?cap_first}().length;
+            int curItem = 0;
+            for(${helper.getLanguageTypeNameForField(arrayField)} element : _value.get${arrayField.name?cap_first}()) {
+                    <#if helper.isSimpleTypeReference(arrayField.type)>
+                        <#assign simpleTypeReference = arrayField.type.asSimpleTypeReference().orElseThrow()>
+                ${helper.getWriteBufferWriteMethodCall("", simpleTypeReference, "element", arrayField)};
+                    <#else>
+                        <#assign complexTypeReference = arrayField.type.asComplexTypeReference().orElseThrow()>
+                boolean lastItem = curItem == (itemCount - 1);
+                ${complexTypeReference.name}IO.staticSerialize(writeBuffer, element<#if helper.getSerializerTerms(field.params.orElse(null))?has_content>, <#list helper.getSerializerTerms(field.params.orElseThrow()) as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if>);
+                    </#if>
+                curItem++;
+            }
+            writeBuffer.popContext("${arrayField.name}", WithReaderWriterArgs.WithRenderAsList(true));
+            </#if>
+        }
+        <#break>
+    <#case "checksum">
+        <#assign checksumField = field.asChecksumField().orElseThrow()>
+        <#assign simpleTypeReference = checksumField.type.asSimpleTypeReference().orElseThrow()>
+
+        // Checksum Field (checksum) (Calculated)
+        {
+            ${helper.getLanguageTypeNameForField(field)} _checksum = ${helper.getNullValueForTypeReference(checksumField.type)};
+            _checksum = (${helper.getLanguageTypeNameForField(field)}) (${helper.toSerializationExpression(checksumField, checksumField.checksumExpression, parserArguments)});
+            ${helper.getWriteBufferWriteMethodCall("checksum", simpleTypeReference, "(_checksum)", checksumField)};
+        }
+        <#break>
+    <#case "const">
+        <#assign constField = field.asConstField().orElseThrow()>
+
+        // Const Field (${constField.name})
+        <#if helper.isSimpleTypeReference(constField.type)>
+            <#assign simpleTypeReference = constField.type.asSimpleTypeReference().orElseThrow()>
+            <#if helper.getLanguageTypeNameForField(field) = 'float'>
+        ${helper.getWriteBufferWriteMethodCall(simpleTypeReference, constField.referenceValue + "f", constField)};
+            <#else>
+        ${helper.getWriteBufferWriteMethodCall(constField.name, simpleTypeReference, constField.referenceValue, constField)};
+            </#if>
+        <#else>
+            <#assign enumTypeReference = constField.type>
+            ${helper.getWriteBufferWriteMethodCall(helper.getTypeDefinitionForTypeReference(enumTypeReference).name, helper.getEnumBaseTypeReference(enumTypeReference), "(" + type.name + "." + constField.name?upper_case + ".getValue())", constField, "WithReaderWriterArgs.WithAdditionalStringRepresentation("+ type.name + "." + constField.name?upper_case + ".name())")};
+        </#if>
+        <#break>
+    <#case "discriminator">
+        <#assign discriminatorField = field.asDiscriminatorField().orElseThrow()>
+
+        // Discriminator Field (${discriminatorField.name}) (Used as input to a switch field)
+        ${helper.getLanguageTypeNameForField(field)} ${discriminatorField.name} = (${helper.getLanguageTypeNameForField(field)}) _value.get${discriminatorField.name?cap_first}();
+        <#if helper.isSimpleTypeReference(discriminatorField.type)>
+            <#assign simpleTypeReference = discriminatorField.type.asSimpleTypeReference().orElseThrow()>
+            ${helper.getWriteBufferWriteMethodCall(discriminatorField.name, simpleTypeReference, "(" + discriminatorField.name + ")", discriminatorField)};
+        <#else>
+            <#assign complexTypeReference = discriminatorField.type>
+            <#if helper.isEnumField(field)>
+                ${helper.getWriteBufferWriteMethodCall(discriminatorField.name, helper.getEnumBaseTypeReference(discriminatorField.type), "(" + discriminatorField.name + ".getValue())", discriminatorField)};
+            <#else>
+                ${complexTypeReference.name}IO.staticSerialize(writeBuffer, ${discriminatorField.name});
+            </#if>
+        </#if>
+        <#break>
+    <#case "enum">
+        <#assign enumField = field.asEnumField().orElseThrow()>
+
+        writeBuffer.pushContext("${enumField.name}");
+        // Enum field (${enumField.name})
+        ${helper.getLanguageTypeNameForField(field)} ${enumField.name} = (${helper.getLanguageTypeNameForField(enumField)}) _value.get${enumField.name?cap_first}();
+        <#if enumField.fieldName.isPresent()>
+        ${helper.getWriteBufferWriteMethodCall(helper.getTypeDefinitionForTypeReference(enumField.type).name, helper.getEnumFieldSimpleTypeReference(enumField.type, enumField.fieldName.orElseThrow()), "(" + enumField.name + ".get" + enumField.fieldName.orElseThrow()?cap_first + "())", enumField, "WithReaderWriterArgs.WithAdditionalStringRepresentation(${enumField.name}.name())")};
+        <#else>
+        ${helper.getWriteBufferWriteMethodCall(helper.getTypeDefinitionForTypeReference(enumField.type).name, helper.getEnumBaseTypeReference(enumField.type), "(" + enumField.name + ".getValue())", enumField, "WithReaderWriterArgs.WithAdditionalStringRepresentation(${enumField.name}.name())")};
+        </#if>
+        writeBuffer.popContext("${enumField.name}");
+        <#break>
+    <#case "implicit">
+        <#assign implicitField = field.asImplicitField().orElseThrow()>
+        <#assign simpleTypeReference = implicitField.type.asSimpleTypeReference().orElseThrow()>
+
+        // Implicit Field (${implicitField.name}) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+        ${helper.getLanguageTypeNameForField(field)} ${implicitField.name} = (${helper.getLanguageTypeNameForField(field)}) (${helper.toSerializationExpression(implicitField, implicitField.serializeExpression, parserArguments)});
+        ${helper.getWriteBufferWriteMethodCall(implicitField.name, simpleTypeReference, "(" + implicitField.name + ")", implicitField)};
+        <#break>
+    <#case "manualArray">
+        <#assign manualArrayField = field.asManualArrayField().orElseThrow()>
+
+        // Manual Array Field (${manualArrayField.name})
+        if(_value.get${manualArrayField.name?cap_first}() != null) {
+            writeBuffer.pushContext("${manualArrayField.name}", WithReaderWriterArgs.WithRenderAsList(true));
+            for(${helper.getLanguageTypeNameForField(field)} element : _value.get${manualArrayField.name?cap_first}()) {
+                ${helper.toSerializationExpression(manualArrayField, manualArrayField.serializeExpression, parserArguments)};
+            }
+            writeBuffer.popContext("${manualArrayField.name}", WithReaderWriterArgs.WithRenderAsList(true));
+        }
+        <#break>
+    <#case "manual">
+        <#assign manualField = field.asTypedField().orElseThrow()>
+
+        // Manual Field (${manualField.name})
+        ${helper.toSerializationExpression(manualField, manualField.serializeExpression, parserArguments)};
+       <#break>
+    <#case "optional">
+        <#assign optionalField = field.asOptionalField().orElseThrow()>
+
+        // Optional Field (${optionalField.name}) (Can be skipped, if the value is null)
+        ${helper.getLanguageTypeNameForField(field)} ${optionalField.name} = null;
+        if(_value.get${optionalField.name?cap_first}() != null) {
+            ${optionalField.name} = (${helper.getLanguageTypeNameForField(field)}) _value.get${optionalField.name?cap_first}();
+            <#if helper.isSimpleTypeReference(optionalField.type)>
+                <#assign simpleTypeReference = optionalField.type.asSimpleTypeReference().orElseThrow()>
+            ${helper.getWriteBufferWriteMethodCall(optionalField.name, simpleTypeReference, "(" + optionalField.name + ")", optionalField)};
+            <#else>
+                <#assign complexTypeReference = optionalField.type>
+                <#if helper.isEnumField(field)>
+            ${helper.getWriteBufferWriteMethodCall(optionalField.name, helper.getEnumBaseTypeReference(optionalField.type), "(" + optionalField.name + ".getValue())", optionalField)};
+                <#else>
+            ${complexTypeReference.name}IO.staticSerialize(writeBuffer, ${optionalField.name});
+                </#if>
+            </#if>
+        }
+        <#break>
+    <#case "padding">
+        <#assign paddingField = field.asPaddingField().orElseThrow()>
+        <#assign simpleTypeReference = paddingField.type.asSimpleTypeReference().orElseThrow()>
+
+        // Padding Field (padding)
+        {
+            writeBuffer.pushContext("padding", WithReaderWriterArgs.WithRenderAsList(true));
+            int _timesPadding = (int) (${helper.toSerializationExpression(paddingField, paddingField.paddingCondition, parserArguments)});
+            while (_timesPadding-- > 0) {
+                ${helper.getLanguageTypeNameForField(field)} _paddingValue = (${helper.getLanguageTypeNameForField(field)}) (${helper.toSerializationExpression(paddingField, paddingField.paddingValue, parserArguments)});
+                ${helper.getWriteBufferWriteMethodCall("", simpleTypeReference, "(_paddingValue)", paddingField)};
+            }
+            writeBuffer.popContext("padding", WithReaderWriterArgs.WithRenderAsList(true));
+        }
+        <#break>
+    <#case "reserved">
+        <#assign reservedField = field.asReservedField().orElseThrow()>
+        <#assign simpleTypeReference = reservedField.type.asSimpleTypeReference().orElseThrow()>
+
+        // Reserved Field (reserved)
+        ${helper.getWriteBufferWriteMethodCall("reserved", simpleTypeReference, helper.getReservedValue(reservedField), reservedField)};
+        <#break>
+    <#case "simple">
+        <#assign simpleField = field.asSimpleField().orElseThrow()>
+
+        // Simple Field (${simpleField.name})
+        ${helper.getLanguageTypeNameForField(field)} ${simpleField.name} = (${helper.getLanguageTypeNameForField(field)}) _value.get${simpleField.name?cap_first}();
+        <#if helper.isSimpleTypeReference(simpleField.type)>
+            <#assign simpleTypeReference = simpleField.type.asSimpleTypeReference().orElseThrow()>
+            <#if helper.isEnumField(field)>
+        // enum based simple field with type ${simpleField.type.asComplexTypeReference().orElseThrow().name}
+        ${helper.getWriteBufferWriteMethodCall(simpleField.type.asComplexTypeReference().orElseThrow().name, simpleTypeReference, "(" + simpleField.name + ")", simpleField, "WithReaderWriterArgs.WithAdditionalStringRepresentation(${simpleField.name}.name())")};
+            <#else>
+        ${helper.getWriteBufferWriteMethodCall(simpleField.name, simpleTypeReference, "(" + simpleField.name + ")", simpleField)};
+            </#if>
+        <#else>
+        writeBuffer.pushContext("${simpleField.name}");
+            <#assign complexTypeReference = simpleField.type>
+            <#if helper.isEnumField(field)>
+        // enum field with type ${complexTypeReference.name}
+        ${helper.getWriteBufferWriteMethodCall(complexTypeReference.name, helper.getEnumBaseTypeReference(simpleField.type), "(" + simpleField.name + ".getValue())", simpleField, "WithReaderWriterArgs.WithAdditionalStringRepresentation(${simpleField.name}.name())")};
+            <#else>
+        ${complexTypeReference.name}IO.staticSerialize(writeBuffer, ${simpleField.name});
+            </#if>
+        writeBuffer.popContext("${simpleField.name}");
+        </#if>
+        <#break>
+    <#case "switch">
+        <#assign switchField = field.asSwitchField().orElseThrow()>
+
+        // Switch field (Depending on the discriminator values, passes the instantiation to a sub-type)
+        <#list switchField.cases as case>
+        if(_value instanceof ${case.name}) {
+            ${case.name}IO.staticSerialize(writeBuffer, (${case.name}) _value);
+        }<#sep> else </#sep>
+        </#list>
+        <#break>
+    <#case "virtual">
+        <#break>
+</#switch>
+</#list>
+        writeBuffer.popContext("${type.name}");
+    </#if>
+    }
+</#if>
+
+<#if type.isDiscriminatedParentTypeDefinition()>
+    public static interface ${type.name}Builder {
+        ${type.name} build(<#list type.propertyFields as field>${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#if> ${field.name}<#sep>, </#sep></#list>);
+    }
+
+</#if>
+<#if type.isDiscriminatedChildTypeDefinition()>
+    public static class ${type.name}Builder implements ${type.parentType.name}IO.${type.parentType.name}Builder {
+        <#if type.propertyFields?has_content>
+            <#list type.propertyFields as field>
+        private final ${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#if> ${field.name};
+            </#list>
+        </#if>
+
+        public ${type.name}Builder(<#list type.propertyFields as field>${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#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>${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#if> ${field.name}<#sep>, </#sep></#list>) {
+            return new ${type.name}(<#list type.allPropertyFields as field>${field.name}<#sep>, </#sep></#list>);
+        }
+    }
+
+</#if>
+}
+</#outputformat>
diff --git a/code-generation/language-java-ng/src/main/resources/templates/java/pojo-template.java.ftlh b/code-generation/language-java-ng/src/main/resources/templates/java/pojo-template.java.ftlh
new file mode 100644
index 0000000..e34be2e
--- /dev/null
+++ b/code-generation/language-java-ng/src/main/resources/templates/java/pojo-template.java.ftlh
@@ -0,0 +1,394 @@
+<#--
+  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
+
+      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.
+-->
+<#-- 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.java.JavaLanguageTemplateHelper" -->
+<#-- @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.packageName(protocolName, languageName, outputFlavor)?replace(".", "/")}/${type.name}.java
+/*
+ * 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
+ *
+ *   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.
+ */
+package ${helper.packageName(protocolName, languageName, outputFlavor)};
+
+import static org.apache.plc4x.java.spi.generation.StaticHelper.*;
+
+import ${helper.packageName(protocolName, languageName, outputFlavor)}.io.*;
+import ${helper.packageName(protocolName, languageName, outputFlavor)}.types.*;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.plc4x.java.api.value.*;
+import org.apache.plc4x.java.spi.generation.Message;
+import org.apache.plc4x.java.spi.generation.MessageIO;
+
+import java.time.*;
+import java.util.*;
+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 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)>
+    public ${helper.getLanguageTypeNameForTypeReference(helper.getDiscriminatorTypes()[discriminatorName])} get${discriminatorName?cap_first}() {
+            <#if !helper.isComplexTypeReference(helper.getDiscriminatorTypes()[discriminatorName])>
+        return <#if helper.getLanguageTypeNameForTypeReference(helper.getDiscriminatorTypes()[discriminatorName]) = "String"><#if discriminatorValue??>"${discriminatorValue}"<#else>${helper.getNullValueForTypeReference(helper.getDiscriminatorTypes()[discriminatorName])}</#if><#else><#if discriminatorValue??>${helper.adjustLiterals(helper.getLanguageTypeNameForTypeReference(helper.getDiscriminatorTypes()[discriminatorName]),discriminatorValue)}<#else>${helper.getNullValueForTypeReference( [...]
+            <#else>
+        return <#if helper.getLanguageTypeNameForTypeReference(helper.getDiscriminatorTypes()[discriminatorName]) = "String"><#if discriminatorValue??>"${discriminatorValue}"<#else>${helper.getNullValueForTypeReference(helper.getDiscriminatorTypes()[discriminatorName])}</#if><#else><#if discriminatorValue??>${helper.getLanguageTypeNameForTypeReference(helper.getDiscriminatorTypes()[discriminatorName])}.${discriminatorValue}<#else>${helper.getLanguageTypeNameForTypeReference(helper.getDis [...]
+            </#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)>
+    public abstract ${helper.getLanguageTypeNameForTypeReference(discriminatorType)} get${discriminatorName?cap_first}();
+        </#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 static final ${helper.getLanguageTypeNameForField(field)} ${field.name?upper_case} = <#if helper.getLanguageTypeNameForField(field) = 'float'>${field.referenceValue}f<#else>${field.referenceValue}</#if>;
+</#list>
+</#if>
+<#-- Property fields are fields that require a property in the pojo -->
+<#if type.propertyFields?has_content>
+
+    // Properties.
+<#list type.propertyFields as field>
+    private final ${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#if> ${field.name};
+</#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)}<#if field.loopType??>[]</#if> ${field.name}<#sep>, </#sep></#list>) {
+<#if type.getParentPropertyFields()?has_content>
+        super(<#list type.getParentPropertyFields() as field>${field.name}<#sep>, </#sep></#list>);
+</#if>
+<#list type.propertyFields as field>
+        this.${field.name} = ${field.name};
+</#list>
+    }
+
+<#list type.abstractFields as field>
+    public abstract ${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#if> get${field.asNamedField().orElseThrow().name?cap_first}();
+
+</#list>
+<#list type.propertyFields as field>
+    public ${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#if> get${field.name?cap_first}() {
+        return ${field.name};
+    }
+
+</#list>
+<#list type.virtualFields as field>
+    <#if !type.isDiscriminatorField(field.name)>
+    public ${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#if> get${field.name?cap_first}() {
+        <#if helper.getLanguageTypeNameForField(field) = 'String'>
+        return ${helper.getLanguageTypeNameForField(field)}.valueOf(${helper.toAccessExpression(field, field.valueExpression, parserArguments)});
+        <#else>
+        return (${helper.getLanguageTypeNameForField(field)}) (${helper.toAccessExpression(field, field.valueExpression, parserArguments)});
+        </#if>
+    }
+    </#if>
+
+</#list>
+    @Override
+    public int getLengthInBytes() {
+        return getLengthInBits() / 8;
+    }
+
+    @Override
+    public int getLengthInBits() {
+        return getLengthInBitsConditional(false);
+    }
+
+<#-- TODO: use serializer args instead of a fixed bool for one case -->
+    public int getLengthInBitsConditional(boolean lastItem) {
+        int lengthInBits = <#if type.parentType??>super.getLengthInBitsConditional(lastItem)<#else>0</#if>;
+        ${helper.getLanguageTypeNameForTypeReference(type.typeReference)} _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}.length;
+        <#elseif arrayField.isCountArrayField()>
+            int i=0;
+            <#assign complexTypeReference = arrayField.type.asComplexTypeReference().orElseThrow()>
+            for(${complexTypeReference.name} element : ${arrayField.name}) {
+                boolean last = ++i >= ${arrayField.name}.length;
+                lengthInBits += element.getLengthInBitsConditional(last);
+            }
+        <#else>
+            for(Message element : ${arrayField.name}) {
+                lengthInBits += element.getLengthInBits();
+            }
+        </#if>
+        }
+        <#break>
+    <#case "checksum">
+        <#assign checksumField = field>
+        <#assign simpleTypeReference = checksumField.type>
+
+        // Checksum Field (checksum)
+        lengthInBits += ${simpleTypeReference.sizeInBits};
+        <#break>
+    <#case "const">
+        <#assign constField = field>
+        <#assign typeReference = constField.type>
+
+        // Const Field (${constField.name})
+        <#if helper.isSimpleTypeReference(typeReference)>
+        <#assign simpleTypeReference = typeReference>
+        lengthInBits += ${simpleTypeReference.sizeInBits};
+        <#else>
+        lengthInBits += ${helper.getEnumBaseTypeReference(typeReference).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 helper.getLanguageTypeNameForTypeReference(discriminatorField.type) = "String">
+        lengthInBits += ${helper.toSerializationExpression(discriminatorField, simpleTypeReference.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>
+
+        // Enum Field (${enumField.name})
+        lengthInBits += ${helper.getEnumBaseTypeReference(enumField.type).sizeInBits};
+        <#break>
+    <#case "implicit">
+        <#assign implicitField = field.asImplicitField().orElseThrow()>
+        <#assign simpleTypeReference = implicitField.type>
+
+        // Implicit Field (${implicitField.name})
+        lengthInBits += ${simpleTypeReference.sizeInBits};
+        //${helper.getLanguageTypeNameForField(field)} ${implicitField.name} = (${helper.getLanguageTypeNameForField(field)}) (${helper.toSerializationExpression(implicitField, implicitField.serializeExpression, parserArguments)});
+        <#break>
+    <#case "manualArray">
+        <#assign manualArrayField = field.asManualArrayField().orElseThrow()>
+
+        // Manual Array Field (${manualArrayField.name})
+        lengthInBits += ${helper.toParseExpression(manualArrayField, manualArrayField.lengthExpression, parserArguments)} * 8;
+        <#break>
+    <#case "manual">
+        <#assign manualField = field.asManualField().orElseThrow()>
+
+        // Manual Field (${manualField.name})
+        lengthInBits += ${helper.toParseExpression(manualField, 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 helper.getLanguageTypeNameForTypeReference(optionalField.type) = "String">
+            lengthInBits += ${helper.toSerializationExpression(discriminatorField, simpleTypeReference.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>
+
+        // 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, paddingField.paddingCondition, parserArguments)});
+        while (_timesPadding-- > 0) {
+            lengthInBits += ${simpleTypeReference.sizeInBits};
+        }
+        <#break>
+    <#case "reserved">
+        <#assign reservedField = field>
+        <#assign simpleTypeReference = reservedField.type>
+
+        // Reserved Field (reserved)
+        lengthInBits += ${simpleTypeReference.sizeInBits};
+        <#break>
+    <#case "simple">
+        <#assign simpleField = field.asSimpleField().orElseThrow()>
+
+        // Simple field (${simpleField.name})
+        <#if helper.isSimpleTypeReference(simpleField.type)>
+                <#if helper.getLanguageTypeNameForTypeReference(simpleField.type) = "String">
+                    <#assign stringTypeReference = simpleField.type.asStringTypeReference().orElseThrow()>
+        lengthInBits += ${helper.toSerializationExpression(simpleField, stringTypeReference.getLengthExpression(), parserArguments)};
+                <#else>
+                    <#assign simpleTypeReference = simpleField.type.asSimpleTypeReference().orElseThrow()>
+        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>
+
+        // Length of sub-type elements will be added by sub-type...
+        <#break>
+    <#case "unknown">
+        <#assign unknownField = field>
+        <#assign simpleTypeReference = unknownField.type>
+
+        // 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;
+    }
+
+    @Override
+    public MessageIO<<#if type.parentType??>${type.parentType.name}<#else>${type.name}</#if>, <#if type.parentType??>${type.parentType.name}<#else>${type.name}</#if>> getMessageIO() {
+        return new <#if type.parentType??>${type.parentType.name}<#else>${type.name}</#if>IO();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof ${type.name})) {
+            return false;
+        }
+        ${type.name} that = (${type.name}) o;
+        return
+            <#if type.propertyFields?has_content>
+            <#list type.propertyFields as field>
+            (get${field.name?cap_first}() == that.get${field.name?cap_first}()) &&
+            </#list>
+            </#if>
+            <#if type.parentType??>
+            super.equals(that) &&
+            </#if>
+            true;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+            <#if type.parentType??>
+            super.hashCode()<#if type.propertyFields?has_content>,</#if>
+            </#if>
+            <#if type.propertyFields?has_content>
+            <#list type.propertyFields as field>
+            get${field.name?cap_first}()<#sep>,</#sep>
+            </#list>
+            </#if>
+        );
+    }
+
+    @Override
+    public String toString() {
+        return toString(ToStringStyle.SHORT_PREFIX_STYLE);
+    }
+
+    public String toString(ToStringStyle style) {
+        return new ToStringBuilder(this, style)
+            <#if type.parentType??>
+            .appendSuper(super.toString(style))
+            </#if>
+            <#if type.propertyFields?has_content>
+            <#list type.propertyFields as field>
+            .append("${field.name}", get${field.name?cap_first}())
+            </#list>
+            </#if>
+            <#list type.virtualFields as field>
+            .append("${field.name}", get${field.name?cap_first}())
+            </#list>
+            .toString();
+    }
+
+}
+</#outputformat>
\ No newline at end of file
diff --git a/code-generation/language-java-ng/src/test/resources/integration-test/pom.xml b/code-generation/language-java-ng/src/test/resources/integration-test/pom.xml
new file mode 100644
index 0000000..21530ff
--- /dev/null
+++ b/code-generation/language-java-ng/src/test/resources/integration-test/pom.xml
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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
+
+      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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.plc4x</groupId>
+    <artifactId>plc4x-code-generation</artifactId>
+    <version>@project.version@</version>
+    <relativePath>../../../..</relativePath>
+  </parent>
+
+  <artifactId>plc4j-java-mspec-test</artifactId>
+
+  <name>PLC4J: Driver: Java Mpsec Test</name>
+  <description></description>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.plc4x.plugins</groupId>
+        <artifactId>plc4x-maven-plugin</artifactId>
+        <version>${plc4x-code-generation.version}</version>
+        <executions>
+          <execution>
+            <id>generate-driver</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>generate-driver</goal>
+            </goals>
+            <configuration>
+              <protocolName>test</protocolName>
+              <languageName>java</languageName>
+              <outputFlavor>read-write</outputFlavor>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.karaf.tooling</groupId>
+        <artifactId>karaf-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>generate-feature-xml</id>
+            <phase>compile</phase>
+            <goals>
+              <!-- Generate the feature.xml -->
+              <goal>features-generate-descriptor</goal>
+              <!-- Check the feature.xml -->
+              <goal>verify</goal>
+            </goals>
+            <configuration>
+              <enableGeneration>true</enableGeneration>
+              <aggregateFeatures>true</aggregateFeatures>
+            </configuration>
+          </execution>
+          <execution>
+            <id>build-kar</id>
+            <phase>package</phase>
+            <goals>
+              <!--
+                Build a kar archive (Jar containing the feature.xml
+                as well as the module content and it's dependencies.
+              -->
+              <goal>kar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
+            <Bundle-Activator>org.apache.plc4x.java.osgi.DriverActivator</Bundle-Activator>
+            <Export-Service>org.apache.plc4x.java.api.PlcDriver,org.apache.plc4x.protocol.test
+            </Export-Service>
+            <Import-Package>
+              com.fasterxml.jackson.annotation;resolution:=optional,
+              *
+            </Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <configuration>
+          <usedDependencies combine.children="append">
+            <usedDependency>org.apache.plc4x:plc4x-code-generation-language-java</usedDependency>
+            <usedDependency>org.apache.plc4x:plc4x-code-generation-protocol-test</usedDependency>
+          </usedDependencies>
+        </configuration>
+      </plugin>
+
+      <!--
+          Make the failsafe execute all integration-tests
+        -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>integration-test</goal>
+              <goal>verify</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <!--
+                Notice the @ instead of the $ as prefix? That's late evaluation.
+          -->
+          <!--argLine>@{failsafeArgLine}</argLine-->
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+<dependencies>
+  <dependency>
+    <groupId>org.apache.plc4x</groupId>
+    <artifactId>plc4j-api</artifactId>
+    <version>@project.version@</version>
+  </dependency>
+
+  <dependency>
+    <groupId>org.apache.plc4x</groupId>
+    <artifactId>plc4j-spi</artifactId>
+    <version>@project.version@</version>
+  </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-transport-tcp</artifactId>
+      <version>@project.version@</version>
+    </dependency>
+
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-buffer</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-annotations</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-utils-test-utils</artifactId>
+      <version>@project.version@</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4x-code-generation-language-java</artifactId>
+      <version>@project.version@</version>
+      <!-- Scope is 'provided' as this way it's not shipped with the driver -->
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4x-code-generation-protocol-test</artifactId>
+      <version>@project.version@</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4x-code-generation-protocol-test</artifactId>
+      <version>@project.version@</version>
+      <classifier>tests</classifier>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+</project>
+
diff --git a/code-generation/language-java-ng/src/test/resources/settings.xml b/code-generation/language-java-ng/src/test/resources/settings.xml
new file mode 100644
index 0000000..2eb8990
--- /dev/null
+++ b/code-generation/language-java-ng/src/test/resources/settings.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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
+
+      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.
+  -->
+<settings>
+  <profiles>
+    <profile>
+      <id>it-repo</id>
+      <activation>
+        <activeByDefault>true</activeByDefault>
+      </activation>
+      <repositories>
+        <repository>
+          <id>local.central</id>
+          <url>@localRepositoryUrl@</url>
+          <releases>
+            <enabled>true</enabled>
+          </releases>
+          <snapshots>
+            <enabled>true</enabled>
+          </snapshots>
+        </repository>
+      </repositories>
+      <pluginRepositories>
+        <pluginRepository>
+          <id>local.central</id>
+          <url>@localRepositoryUrl@</url>
+          <releases>
+            <enabled>true</enabled>
+          </releases>
+          <snapshots>
+            <enabled>true</enabled>
+          </snapshots>
+        </pluginRepository>
+      </pluginRepositories>
+    </profile>
+  </profiles>
+</settings>
\ No newline at end of file
diff --git a/code-generation/pom.xml b/code-generation/pom.xml
index cb7e142..be95f65 100644
--- a/code-generation/pom.xml
+++ b/code-generation/pom.xml
@@ -39,6 +39,7 @@
     <module>protocol-test</module>
 
     <module>language-java</module>
+    <module>language-java-ng</module>
   </modules>
 
   <profiles>
diff --git a/code-generation/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/MSpec.g4 b/code-generation/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/MSpec.g4
index 0a3edd4..fe8bbfa 100644
--- a/code-generation/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/MSpec.g4
+++ b/code-generation/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/MSpec.g4
@@ -151,8 +151,8 @@ dataType
  | base='byte'
  | base='int' size=INTEGER_LITERAL
  | base='uint' size=INTEGER_LITERAL
- | base='float' exponent=INTEGER_LITERAL '.' mantissa=INTEGER_LITERAL
- | base='ufloat' exponent=INTEGER_LITERAL '.' mantissa=INTEGER_LITERAL
+ | base='float' size=INTEGER_LITERAL
+ | base='ufloat' size=INTEGER_LITERAL
  | base='string' size=INTEGER_LITERAL
  | base='vstring' (length=expression)?
  | base='time'
diff --git a/code-generation/protocol-test/src/main/resources/protocols/test/test.mspec b/code-generation/protocol-test/src/main/resources/protocols/test/test.mspec
index e2f060c..f5be727 100644
--- a/code-generation/protocol-test/src/main/resources/protocols/test/test.mspec
+++ b/code-generation/protocol-test/src/main/resources/protocols/test/test.mspec
@@ -73,8 +73,8 @@
 /*
  * TODO: doesn't compile in java
 [type 'UFloatTypeTest'
-    [simple ufloat 8.23 'ufloatField']
-    [simple ufloat 11.52 'udoubleField']
+    [simple ufloat 32 'ufloatField']
+    [simple ufloat 64 'udoubleField']
 ]
 */
 
@@ -92,8 +92,8 @@
     [simple byte 'byteField']
     [simple int 8 'intField']
     [simple uint 8 'uintField']
-    [simple float 8.23 'floatField']
-    [simple float 11.52 'doubleField']
+    [simple float 32 'floatField']
+    [simple float 64 'doubleField']
     [simple string 8 'stringField']
 ]
 
@@ -103,16 +103,16 @@
     [abstract bit 'abstractBitField']
     [abstract int 8 'abstractIntField']
     [abstract uint 8 'abstractUintField']
-    [abstract float 8.23 'abstractFloatField']
-    [abstract float 11.52 'abstractDoubleField']
+    [abstract float 32 'abstractFloatField']
+    [abstract float 64 'abstractDoubleField']
     [abstract string 8 'abstractStringField']
     [typeSwitch 'simpleField'
         ['0' AbstractedType
             [simple bit 'abstractBitField']
             [simple int 8 'abstractIntField']
             [simple uint 8 'abstractUintField']
-            [simple float 8.23 'abstractFloatField']
-            [simple float 11.52 'abstractDoubleField']
+            [simple float 32 'abstractFloatField']
+            [simple float 64 'abstractDoubleField']
             [simple string 8 'abstractStringField']
         ]
     ]
@@ -124,8 +124,8 @@
     [abstract bit 'abstractBitField']
     [abstract int 8 'abstractIntField']
     [abstract uint 8 'abstractUintField']
-    [abstract float 8.23 'abstractFloatField']
-    [abstract float 11.52 'abstractDoubleField']
+    [abstract float 32 'abstractFloatField']
+    [abstract float 64 'abstractDoubleField']
     [abstract string 8 'abstractStringField']
     [typeSwitch 'simpleField'
         ['0' AbstractedType
@@ -133,8 +133,8 @@
             [simple bit 'abstractBitField']
             [simple int 8 'abstractIntField']
             [simple uint 8 'abstractUintField']
-            [simple float 8.23 'abstractFloatField']
-            [simple float 11.52 'abstractDoubleField']
+            [simple float 32 'abstractFloatField']
+            [simple float 64 'abstractDoubleField']
             [simple string 8 'abstractStringField']
         ]
     ]
@@ -144,8 +144,8 @@
     [array bit 'bitField' count      '5']
     [array int 8 'intField' count      '5']
     [array uint 8 'uintField' count      '5']
-    [array float 8.23 'floatField' count      '5']
-    [array float 11.52 'doubleField' count      '5']
+    [array float 32 'floatField' count      '5']
+    [array float 64 'doubleField' count      '5']
     [array string 8 'stringField' count      '5']
 ]
 
@@ -156,8 +156,8 @@
     //[checksum int 8 'intField' '100']
     //[checksum uint 8 'uintField' '100']
     //Float fields cannot be used as checksums
-    //[checksum float 8.23 'floatField' '100.0f']
-    //[checksum float 11.52 'doubleField' '100.0']
+    //[checksum float 32 'floatField' '100.0f']
+    //[checksum float 64 'doubleField' '100.0']
     //String field cannot be used as a checksum
     //[checksum vstring '11 * 8' 'stringField' '"HELLO TODDY"']
 //]
@@ -166,8 +166,8 @@
     [const bit 'bitField' 'true']
     [const int 8 'intField' '100']
     [const uint 8 'uintField' '100']
-    [const float 8.23 'floatField' '100.0']
-    [const float 11.52 'doubleField' '100.0']
+    [const float 32 'floatField' '100.0']
+    [const float 64 'doubleField' '100.0']
     [const string 8 'stringField' '"HELLO TODDY"']
 ]
 
@@ -193,8 +193,8 @@
     [implicit bit 'bitField' 'simpleField > 0']
     [implicit int 8 'intField' 'simpleField']
     [implicit uint 8 'uintField' 'simpleField']
-    [implicit float 8.23 'floatField' 'simpleField']
-    [implicit float 11.52 'doubleField' 'simpleField']
+    [implicit float 32 'floatField' 'simpleField']
+    [implicit float 64 'doubleField' 'simpleField']
     //TODO: String literals can't be used in the expression
     //[implicit string 8 'stringField' 'simpleField > 0 ? "HELLO TODDY" : "BYE TODDY"']
 ]
@@ -219,8 +219,8 @@
 //    [virtual bit 'virtualBitField' 'simpleField == 0']
 //    [virtual int 8 'virtualIntField' 'simpleField']
 //    [virtual uint 8 'virtualUintField' 'simpleField']
-//    [virtual float 8.23 'virtualFloatField' 'simpleField']
-//    [virtual float 11.52 'virtualDoubleField' 'simpleField']
+//    [virtual float 32 'virtualFloatField' 'simpleField']
+//    [virtual float 64 'virtualDoubleField' 'simpleField']
 //    [virtual string 24 'virtualStringField' 'simpleField']
 //]
 
@@ -230,8 +230,8 @@
 //    [virtual bit 'virtualBitField' 'simpleField == 0']
 //    [virtual int 8 'virtualIntField' 'simpleField']
 //    [virtual uint 8 'virtualUintField' 'simpleField']
-//    [virtual float 8.23 'virtualFloatField' 'simpleField']
-//    [virtual float 11.52 'virtualDoubleField' 'simpleField']
+//    [virtual float 32 'virtualFloatField' 'simpleField']
+//    [virtual float 64 'virtualDoubleField' 'simpleField']
 //    [virtual string 24 'virtualStringField' 'simpleField']
 //    [typeSwitch 'simpleField'
 //        ['0' DiscriminatedVirtualType
@@ -387,14 +387,14 @@
 ]
 
 //TODO:  C doesn't support non integer switch fields
-//[enum float 8.23 'EnumTypeFloat'
+//[enum float 32 'EnumTypeFloat'
 //    ['100.0' LOW]
 //    ['101.0' MID]
 //    ['102.0' BIG]
 //]
 
 //TODO:  C doesn't support non integer switch fields
-//[enum float 11.52 'EnumTypeDouble'
+//[enum float 64 'EnumTypeDouble'
 //    ['100.0' LOW]
 //    ['101.0' MID]
 //    ['102.0' BIG]
@@ -413,7 +413,7 @@
 //]
 
 //TODO:  Float parameters aren't implemented for constants in enums in C
-//[enum int 8 'EnumTypeAllTest'  [bit 'bitType', int 8 'intType', uint 8 'uintType', float 8.23 'floatType', float 11.52 'doubleType', string 32 'stringType', EnumType 'enumType']
+//[enum int 8 'EnumTypeAllTest'  [bit 'bitType', int 8 'intType', uint 8 'uintType', float 32 'floatType', float 64 'doubleType', string 32 'stringType', EnumType 'enumType']
 //    ['0x01' BOOL             ['false'      , '1'               , '1'                 , '100.0'                  , '100.0'              , 'BOOL'         , 'BOOL']]
 //    ['0x02' BYTE             ['true'       , '2'               , '2'                 , '101.1'                  , '101.1'              , 'BYTE'         , 'UINT']]
 //]
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/FieldCommons.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/FieldCommons.java
new file mode 100644
index 0000000..449ca79
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/FieldCommons.java
@@ -0,0 +1,67 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen;
+
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+public interface FieldCommons {
+
+    default Optional<ByteOrder> extractByteOder(WithReaderArgs... readerArgs) {
+        return extractByteOder(Stream.of(readerArgs).map(WithReaderWriterArgs.class::cast).toArray(WithReaderWriterArgs[]::new));
+    }
+
+    default Optional<ByteOrder> extractByteOder(WithWriterArgs... writerArgs) {
+        return extractByteOder(Stream.of(writerArgs).map(WithReaderWriterArgs.class::cast).toArray(WithReaderWriterArgs[]::new));
+    }
+
+    default Optional<ByteOrder> extractByteOder(WithReaderWriterArgs... readerWriterArgs) {
+        for (WithReaderWriterArgs arg : readerWriterArgs) {
+            if (arg instanceof withOptionByteOrder) {
+                return Optional.of(((withOptionByteOrder) arg).byteOrder());
+            }
+        }
+        return Optional.empty();
+    }
+
+    default <T> T switchByteOrderIfNecessary(RunWrapped<T> runnable, DataReader<T> dataReader, ByteOrder wantedByteOrder) throws ParseException {
+        Objects.requireNonNull(runnable);
+        Objects.requireNonNull(dataReader);
+        ByteOrder currentByteOrder = dataReader.getByteOrder();
+        if (wantedByteOrder == null || currentByteOrder == wantedByteOrder) {
+            return runnable.run();
+        }
+        try {
+            dataReader.setByteOrder(wantedByteOrder);
+            return runnable.run();
+        } finally {
+            dataReader.setByteOrder(currentByteOrder);
+        }
+    }
+
+    @FunctionalInterface
+    interface RunWrapped<T> {
+        T run() throws ParseException;
+    }
+
+}
\ No newline at end of file
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/WithOption.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/WithOption.java
new file mode 100644
index 0000000..f202cab
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/WithOption.java
@@ -0,0 +1,50 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen;
+
+import org.apache.plc4x.java.spi.generation.ByteOrder;
+import org.apache.plc4x.java.spi.generation.WithReaderWriterArgs;
+
+public interface WithOption extends WithReaderWriterArgs {
+
+    static WithOption WithByteOrder(ByteOrder byteOrder) {
+        return (withOptionByteOrder) () -> byteOrder;
+    }
+
+    static WithOption WithEncoding(String encoding) {
+        return (withOptionEncoding) () -> encoding;
+    }
+
+    static WithOption WithSerializationContext(String context) {
+        return (withOptionSerializationContext) () -> context;
+    }
+
+}
+
+interface withOptionByteOrder extends WithOption {
+    ByteOrder byteOrder();
+}
+
+interface withOptionEncoding extends WithOption {
+    String encoding();
+}
+
+interface withOptionSerializationContext extends WithOption {
+    String context();
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReader.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReader.java
new file mode 100644
index 0000000..c130ecf
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReader.java
@@ -0,0 +1,31 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.FieldCommons;
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+
+public interface FieldReader<T> extends FieldCommons {
+
+    T readField(String logicalName, DataReader<T> dataIO, WithReaderArgs... readerArgs) throws ParseException;
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderAbstract.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderAbstract.java
new file mode 100644
index 0000000..6618671
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderAbstract.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderAbstract<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderAbstract.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderArray.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderArray.java
new file mode 100644
index 0000000..99f37a9
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderArray.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderArray<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderArray.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderAssert.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderAssert.java
new file mode 100644
index 0000000..01b85fa
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderAssert.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderAssert<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderAssert.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderChecksum.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderChecksum.java
new file mode 100644
index 0000000..321a48b
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderChecksum.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderChecksum<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderChecksum.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderConst.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderConst.java
new file mode 100644
index 0000000..e41d3f3
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderConst.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderConst<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderConst.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderDiscriminator.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderDiscriminator.java
new file mode 100644
index 0000000..0f906dc
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderDiscriminator.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderDiscriminator<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderDiscriminator.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderEnum.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderEnum.java
new file mode 100644
index 0000000..755310b
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderEnum.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderEnum<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderEnum.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderImplicit.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderImplicit.java
new file mode 100644
index 0000000..c68e1bc
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderImplicit.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderImplicit<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderImplicit.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderManual.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderManual.java
new file mode 100644
index 0000000..b3e8cda
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderManual.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderManual<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderManual.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderManualArray.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderManualArray.java
new file mode 100644
index 0000000..c4b5f6a
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderManualArray.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderManualArray<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderManualArray.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderOptional.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderOptional.java
new file mode 100644
index 0000000..f3b0332
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderOptional.java
@@ -0,0 +1,53 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseAssertException;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderOptional<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderOptional.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+    public T readOptionalField(String logicalName, DataReader<T> dataReader, boolean condition, WithOption... options) throws ParseException {
+        if (!condition) {
+            LOGGER.debug("Condition doesnt match for field {}", logicalName);
+            return null;
+        }
+
+        int curPos = dataReader.getPos();
+        try {
+            return dataReader.read(logicalName, options);
+        } catch (ParseAssertException e) {
+            LOGGER.trace("Assertion doesnt match for field {}", logicalName, e);
+            dataReader.setPos(curPos);
+            return null;
+        }
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderPadding.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderPadding.java
new file mode 100644
index 0000000..c9747e0
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderPadding.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderPadding<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderPadding.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderReserved.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderReserved.java
new file mode 100644
index 0000000..4e8913e
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderReserved.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderReserved<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderReserved.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderSimple.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderSimple.java
new file mode 100644
index 0000000..3c02c54
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderSimple.java
@@ -0,0 +1,32 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+
+public class FieldReaderSimple<T> implements FieldReader<T> {
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        return switchByteOrderIfNecessary(() -> dataReader.read(logicalName, readerArgs), dataReader, extractByteOder(readerArgs).orElse(null));
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderTypeSwitch.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderTypeSwitch.java
new file mode 100644
index 0000000..44f78c1
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderTypeSwitch.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderTypeSwitch<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderTypeSwitch.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderUnknown.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderUnknown.java
new file mode 100644
index 0000000..0944a0d
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderUnknown.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderUnknown<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderUnknown.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderVirtual.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderVirtual.java
new file mode 100644
index 0000000..00dcbd3
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderVirtual.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataReader;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FieldReaderVirtual<T> implements FieldReader<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderVirtual.class);
+
+    @Override
+    public T readField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException("not possible with optional field");
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldWriter.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldWriter.java
new file mode 100644
index 0000000..90256d6
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldWriter.java
@@ -0,0 +1,28 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.fields;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.codegen.io.DataWriter;
+
+public interface FieldWriter<T> {
+
+    void writeField(T value, DataWriter<T> dataWriter, WithOption... options);
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReader.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReader.java
new file mode 100644
index 0000000..b3f2ceb
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReader.java
@@ -0,0 +1,35 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.ByteOrder;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+
+public interface DataReader<T> {
+    T read(String logicalName, WithReaderArgs... readerArgs) throws ParseException;
+
+    int getPos();
+
+    void setPos(int position);
+
+    ByteOrder getByteOrder();
+
+    void setByteOrder(ByteOrder byteOrder);
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderComplex.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderComplex.java
new file mode 100644
index 0000000..662ef53
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderComplex.java
@@ -0,0 +1,30 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+
+import java.util.function.Supplier;
+
+public interface DataReaderComplex<T> extends DataReader<T> {
+
+    T read(String logicalName, Supplier<T> complexSupplier, WithReaderArgs... readerArgs) throws ParseException;
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderComplexDefault.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderComplexDefault.java
new file mode 100644
index 0000000..3f4dca8
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderComplexDefault.java
@@ -0,0 +1,60 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+
+import java.util.function.Supplier;
+
+public class DataReaderComplexDefault<T> implements DataReaderComplex<T> {
+    private final Supplier<T> complexSupplier;
+
+    // TODO: maybe replace with resetable or something so its clear that that's the only purpose
+    private final ReadBuffer readBuffer;
+
+    public DataReaderComplexDefault(Supplier<T> complexSupplier, ReadBuffer readBuffer) {
+        this.complexSupplier = complexSupplier;
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    @Override
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+
+    @Override
+    public T read(String logicalName, WithReaderArgs... readerArgs) throws ParseException {
+        return read(logicalName, complexSupplier, readerArgs);
+    }
+
+    public T read(String logicalName, Supplier<T> complexSupplier, WithReaderArgs... readerArgs) throws ParseException {
+        readBuffer.pullContext(logicalName,readerArgs);
+        final T t = complexSupplier.get();
+        readBuffer.closeContext(logicalName,readerArgs);
+        return t;
+    }
+}
+
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderEnum.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderEnum.java
new file mode 100644
index 0000000..fad7756
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderEnum.java
@@ -0,0 +1,28 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+
+public interface DataReaderEnum<T> extends DataReader {
+
+    T read(String logicalName, WithReaderArgs... readerArgs) throws ParseException;
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderEnumDefault.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderEnumDefault.java
new file mode 100644
index 0000000..0f73266
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderEnumDefault.java
@@ -0,0 +1,57 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithReaderArgs;
+
+import java.util.function.Function;
+
+public class DataReaderEnumDefault<T, I> implements DataReaderEnum<T> {
+
+    private final Function<I, T> enumResolver;
+    private final DataReaderSimple<I> dataReader;
+
+    public DataReaderEnumDefault(Function<I, T> enumResolver, DataReaderSimple<I> dataReader) {
+        this.enumResolver = enumResolver;
+        this.dataReader = dataReader;
+    }
+
+    @Override
+    public int getPos() {
+        return dataReader.getPos();
+    }
+
+    @Override
+    public void setPos(int position) {
+        dataReader.setPos(position);
+    }
+
+    @Override
+    public T read(String logicalName, WithReaderArgs... readerArgs) throws ParseException {
+        return read(logicalName, enumResolver, readerArgs);
+    }
+
+    public T read(String logicalName, Function<I, T> enumResolver, WithReaderArgs... readerArgs) throws ParseException {
+        // TODO: ensure type safety
+        I rawValue = (I) dataReader.read(logicalName, readerArgs);
+        return enumResolver.apply(rawValue);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimple.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimple.java
new file mode 100644
index 0000000..dda35e6
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimple.java
@@ -0,0 +1,31 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+public interface DataReaderSimple<T> extends DataReader<T> {
+
+    default T read(String logicalName, WithReaderArgs... readerArgs) throws ParseException {
+        throw new IllegalStateException(DataReaderSimple.class.getSimpleName() + " can't be called without a bit length");
+    }
+
+    T read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException;
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleBigDecimal.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleBigDecimal.java
new file mode 100644
index 0000000..a68a74c
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleBigDecimal.java
@@ -0,0 +1,46 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.math.BigDecimal;
+
+public class DataReaderSimpleBigDecimal implements DataReaderSimple<BigDecimal> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleBigDecimal(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public BigDecimal read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException {
+        return readBuffer.readBigDecimal(logicalName, bitLength, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleBoolean.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleBoolean.java
new file mode 100644
index 0000000..418d8bf
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleBoolean.java
@@ -0,0 +1,48 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.Objects;
+
+public class DataReaderSimpleBoolean implements DataReaderSimple<Boolean> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleBoolean(ReadBuffer readBuffer) {
+        this.readBuffer = Objects.requireNonNull(readBuffer);
+    }
+
+    @Override
+    public Boolean read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException  {
+        if(bitLength != 1) {
+            throw new ParseException("Bit fields only support bitLength of 1");
+        }
+        return readBuffer.readBit(logicalName, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleByte.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleByte.java
new file mode 100644
index 0000000..0cec2da
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleByte.java
@@ -0,0 +1,46 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+public class DataReaderSimpleByte implements DataReaderSimple<Byte> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleByte(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public Byte read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException  {
+        if(bitLength != 8) {
+            throw new ParseException("Byte fields only support bitLength of 8");
+        }
+        return readBuffer.readByte(logicalName, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleByteArray.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleByteArray.java
new file mode 100644
index 0000000..c26b558
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleByteArray.java
@@ -0,0 +1,46 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+public class DataReaderSimpleByteArray implements DataReaderSimple<byte[]> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleByteArray(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public byte[] read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException {
+        if (bitLength % 8 != 0) {
+            throw new ParseException("ByteArray fields only support bitLength which are multiples of 8");
+        }
+        return readBuffer.readByteArray(logicalName, bitLength * 8, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleDouble.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleDouble.java
new file mode 100644
index 0000000..8426dd4
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleDouble.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+public class DataReaderSimpleDouble implements DataReaderSimple<Double> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleDouble(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public Double read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException  {
+        return readBuffer.readDouble(logicalName, bitLength, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleFloat.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleFloat.java
new file mode 100644
index 0000000..69f4a28
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleFloat.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+public class DataReaderSimpleFloat implements DataReaderSimple<Float> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleFloat(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public Float read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException  {
+        return readBuffer.readFloat(logicalName, bitLength, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedBigInteger.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedBigInteger.java
new file mode 100644
index 0000000..df1962e
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedBigInteger.java
@@ -0,0 +1,45 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.math.BigInteger;
+
+public class DataReaderSimpleSignedBigInteger implements DataReaderSimple<BigInteger> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleSignedBigInteger(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public BigInteger read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException  {
+        return readBuffer.readBigInteger(logicalName, bitLength, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedByte.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedByte.java
new file mode 100644
index 0000000..831a43a
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedByte.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+public class DataReaderSimpleSignedByte implements DataReaderSimple<Byte> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleSignedByte(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public Byte read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException  {
+        return readBuffer.readSignedByte(logicalName, bitLength, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedInt.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedInt.java
new file mode 100644
index 0000000..30290f9
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedInt.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+public class DataReaderSimpleSignedInt implements DataReaderSimple<Integer> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleSignedInt(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public Integer read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException  {
+        return readBuffer.readInt(logicalName, bitLength, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedLong.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedLong.java
new file mode 100644
index 0000000..3ddaada
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedLong.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+public class DataReaderSimpleSignedLong implements DataReaderSimple<Long> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleSignedLong(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public Long read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException  {
+        return readBuffer.readLong(logicalName, bitLength, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedShort.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedShort.java
new file mode 100644
index 0000000..423bc08
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleSignedShort.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+public class DataReaderSimpleSignedShort implements DataReaderSimple<Short> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleSignedShort(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public Short read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException  {
+        return readBuffer.readShort(logicalName, bitLength, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedBigInteger.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedBigInteger.java
new file mode 100644
index 0000000..7715bea
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedBigInteger.java
@@ -0,0 +1,45 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.math.BigInteger;
+
+public class DataReaderSimpleUnsignedBigInteger implements DataReaderSimple<BigInteger> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleUnsignedBigInteger(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public BigInteger read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException  {
+        return readBuffer.readUnsignedBigInteger(logicalName, bitLength, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedByte.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedByte.java
new file mode 100644
index 0000000..0aef2b9
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedByte.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+public class DataReaderSimpleUnsignedByte implements DataReaderSimple<Byte> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleUnsignedByte(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public Byte read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException  {
+        return readBuffer.readUnsignedByte(logicalName, bitLength, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedInt.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedInt.java
new file mode 100644
index 0000000..8f77b4a
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedInt.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+public class DataReaderSimpleUnsignedInt implements DataReaderSimple<Integer> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleUnsignedInt(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public Integer read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException  {
+        return readBuffer.readUnsignedInt(logicalName, bitLength, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedLong.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedLong.java
new file mode 100644
index 0000000..e0631f6
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedLong.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+public class DataReaderSimpleUnsignedLong implements DataReaderSimple<Long> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleUnsignedLong(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public Long read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException  {
+        return readBuffer.readUnsignedLong(logicalName, bitLength, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedShort.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedShort.java
new file mode 100644
index 0000000..62e9bac
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedShort.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+public class DataReaderSimpleUnsignedShort implements DataReaderSimple<Short> {
+
+    private final ReadBuffer readBuffer;
+
+    public DataReaderSimpleUnsignedShort(ReadBuffer readBuffer) {
+        this.readBuffer = readBuffer;
+    }
+
+    @Override
+    public Short read(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException  {
+        return readBuffer.readUnsignedShort(logicalName, bitLength, readerArgs);
+    }
+
+    public int getPos() {
+        return readBuffer.getPos();
+    }
+
+    public void setPos(int position) {
+        readBuffer.reset(position);
+    }
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriter.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriter.java
new file mode 100644
index 0000000..19617d7
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriter.java
@@ -0,0 +1,27 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+public interface DataWriter<T> {
+
+    void write(String logicalName, int bitLength, T value, WithWriterArgs... writerArgs) throws ParseException;
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterBigDecimal.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterBigDecimal.java
new file mode 100644
index 0000000..da4ef23
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterBigDecimal.java
@@ -0,0 +1,39 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.math.BigDecimal;
+import java.util.Objects;
+
+public class DataWriterBigDecimal implements DataWriter<BigDecimal> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterBigDecimal(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, BigDecimal value, WithWriterArgs... writerArgs) throws ParseException {
+        writeBuffer.writeBigDecimal(logicalName, bitLength, value, writerArgs);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterBoolean.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterBoolean.java
new file mode 100644
index 0000000..bff5a7c
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterBoolean.java
@@ -0,0 +1,41 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.Objects;
+
+public class DataWriterBoolean implements DataWriter<Boolean> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterBoolean(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, Boolean value, WithWriterArgs... writerArgs) throws ParseException {
+        if (bitLength != 1) {
+            throw new ParseException("Bit fields only support bitLength of 1");
+        }
+        writeBuffer.writeBit(logicalName, value, writerArgs);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterByte.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterByte.java
new file mode 100644
index 0000000..b250e30
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterByte.java
@@ -0,0 +1,41 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.Objects;
+
+public class DataWriterByte implements DataWriter<Byte> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterByte(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, Byte value, WithWriterArgs... writerArgs) throws ParseException {
+        if(bitLength != 8) {
+            throw new ParseException("Byte fields only support bitLength of 8");
+        }
+        writeBuffer.writeByte(logicalName, value, writerArgs);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterByteArray.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterByteArray.java
new file mode 100644
index 0000000..37a60c7
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterByteArray.java
@@ -0,0 +1,42 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.Objects;
+
+public class DataWriterByteArray implements DataWriter<byte[]> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterByteArray(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, byte[] value, WithWriterArgs... writerArgs) throws ParseException {
+        if (bitLength != 8) {
+            throw new ParseException("ByteArray fields only support bitLength of 8");
+        }
+        // TODO: Get a WirReaderArgs parameter for the number of bytes ...
+        writeBuffer.writeByteArray(logicalName, value, writerArgs);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterDouble.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterDouble.java
new file mode 100644
index 0000000..c201e53
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterDouble.java
@@ -0,0 +1,38 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.Objects;
+
+public class DataWriterDouble implements DataWriter<Double> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterDouble(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, Double value, WithWriterArgs... writerArgs) throws ParseException {
+        writeBuffer.writeDouble(logicalName, bitLength, value, writerArgs);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterFloat.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterFloat.java
new file mode 100644
index 0000000..70d598f
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterFloat.java
@@ -0,0 +1,40 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WithWriterArgs;
+import org.apache.plc4x.java.spi.generation.WriteBuffer;
+
+import java.util.Objects;
+
+public class DataWriterFloat implements DataWriter<Float> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterFloat(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, Float value, WithWriterArgs... writerArgs) throws ParseException {
+        writeBuffer.writeFloat(logicalName, bitLength, value);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedBigInteger.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedBigInteger.java
new file mode 100644
index 0000000..33aee9e
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedBigInteger.java
@@ -0,0 +1,39 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.math.BigInteger;
+import java.util.Objects;
+
+public class DataWriterSignedBigInteger implements DataWriter<BigInteger> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterSignedBigInteger(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, BigInteger value, WithWriterArgs... writerArgs) throws ParseException {
+        writeBuffer.writeBigInteger(logicalName, bitLength, value, writerArgs);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedByte.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedByte.java
new file mode 100644
index 0000000..88c2c3f
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedByte.java
@@ -0,0 +1,38 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.Objects;
+
+public class DataWriterSignedByte implements DataWriter<Byte> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterSignedByte(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, Byte value, WithWriterArgs... writerArgs) throws ParseException {
+        writeBuffer.writeSignedByte(logicalName, bitLength, value, writerArgs);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedInt.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedInt.java
new file mode 100644
index 0000000..effd5be
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedInt.java
@@ -0,0 +1,38 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.Objects;
+
+public class DataWriterSignedInt implements DataWriter<Integer> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterSignedInt(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, Integer value, WithWriterArgs... writerArgs) throws ParseException {
+        writeBuffer.writeInt(logicalName, bitLength, value, writerArgs);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedLong.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedLong.java
new file mode 100644
index 0000000..b0f8bd8
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedLong.java
@@ -0,0 +1,38 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.Objects;
+
+public class DataWriterSignedLong implements DataWriter<Long> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterSignedLong(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, Long value, WithWriterArgs... writerArgs) throws ParseException {
+        writeBuffer.writeLong(logicalName, bitLength, value, writerArgs);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedShort.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedShort.java
new file mode 100644
index 0000000..5bc64c1
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSignedShort.java
@@ -0,0 +1,38 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.Objects;
+
+public class DataWriterSignedShort implements DataWriter<Short> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterSignedShort(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, Short value, WithWriterArgs... writerArgs) throws ParseException {
+        writeBuffer.writeShort(logicalName, bitLength, value, writerArgs);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterUnsignedBigInteger.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterUnsignedBigInteger.java
new file mode 100644
index 0000000..a9ab701
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterUnsignedBigInteger.java
@@ -0,0 +1,39 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.math.BigInteger;
+import java.util.Objects;
+
+public class DataWriterUnsignedBigInteger implements DataWriter<BigInteger> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterUnsignedBigInteger(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, BigInteger value, WithWriterArgs... writerArgs) throws ParseException {
+        writeBuffer.writeUnsignedBigInteger(logicalName, bitLength, value, writerArgs);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterUnsignedByte.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterUnsignedByte.java
new file mode 100644
index 0000000..c386c8e
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterUnsignedByte.java
@@ -0,0 +1,38 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.Objects;
+
+public class DataWriterUnsignedByte implements DataWriter<Byte> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterUnsignedByte(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, Byte value, WithWriterArgs... writerArgs) throws ParseException {
+        writeBuffer.writeUnsignedByte(logicalName, bitLength, value, writerArgs);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterUnsignedInt.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterUnsignedInt.java
new file mode 100644
index 0000000..bbe8c24
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterUnsignedInt.java
@@ -0,0 +1,38 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.Objects;
+
+public class DataWriterUnsignedInt implements DataWriter<Integer> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterUnsignedInt(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, Integer value, WithWriterArgs... writerArgs) throws ParseException {
+        writeBuffer.writeUnsignedInt(logicalName, bitLength, value, writerArgs);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterUnsignedLong.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterUnsignedLong.java
new file mode 100644
index 0000000..3af0c82
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterUnsignedLong.java
@@ -0,0 +1,38 @@
+/*
+ * 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
+ *
+ *   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.
+ */
+package org.apache.plc4x.java.spi.codegen.io;
+
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.Objects;
+
+public class DataWriterUnsignedLong implements DataWriter<Long> {
+
+    private final WriteBuffer writeBuffer;
+
+    public DataWriterUnsignedLong(WriteBuffer writeBuffer) {
+        this.writeBuffer = Objects.requireNonNull(writeBuffer);
+    }
+
+    @Override
+    public void write(String logicalName, int bitLength, Long value, WithWriterArgs... writerArgs) throws ParseException {
+        writeBuffer.writeUnsignedLong(logicalName, bitLength, value, writerArgs);
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferByteBased.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferByteBased.java
index f45f72a..85f27ca 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferByteBased.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferByteBased.java
@@ -275,19 +275,19 @@ public class WriteBufferByteBased implements WriteBuffer {
     }
 
     @Override
-    public void writeFloat(String logicalName, float value, int bitsExponent, int bitsMantissa, WithWriterArgs... writerArgs) throws ParseException {
-        if (bitsExponent != 8 || bitsMantissa != 23) {
+    public void writeFloat(String logicalName, int bitLength, float value, WithWriterArgs... writerArgs) throws ParseException {
+        if (bitLength != 32) {
             throw new UnsupportedOperationException("Error writing float: Exponent and/or Mantissa non standard size");
         }
-        writeInt(logicalName, 1 + bitsExponent + bitsMantissa, Float.floatToRawIntBits(value));
+        writeInt(logicalName, bitLength, Float.floatToRawIntBits(value));
     }
 
     @Override
-    public void writeDouble(String logicalName, double value, int bitsExponent, int bitsMantissa, WithWriterArgs... writerArgs) throws ParseException {
-        if (bitsExponent != 11 || bitsMantissa != 52) {
+    public void writeDouble(String logicalName, int bitLength, double value, WithWriterArgs... writerArgs) throws ParseException {
+        if (bitLength != 64) {
             throw new UnsupportedOperationException("Error writing double: Exponent and/or Mantissa non standard size");
         }
-        writeLong(logicalName, 1 + bitsExponent + bitsMantissa, Double.doubleToRawLongBits(value));
+        writeLong(logicalName, bitLength, Double.doubleToRawLongBits(value));
     }
 
     @Override
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferJsonBased.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferJsonBased.java
index 1779981..4db800e 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferJsonBased.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferJsonBased.java
@@ -207,20 +207,18 @@ public class WriteBufferJsonBased implements WriteBuffer, BufferCommons, AutoClo
     }
 
     @Override
-    public void writeFloat(String logicalName, float value, int bitsExponent, int bitsMantissa, WithWriterArgs... writerArgs) throws ParseException {
+    public void writeFloat(String logicalName, int bitLength, float value, WithWriterArgs... writerArgs) throws ParseException {
         final String sanitizedLogicalName = sanitizeLogicalName(logicalName);
         wrapIfNecessary(() -> {
-            int bitLength = (value < 0 ? 1 : 0) + bitsExponent + bitsMantissa;
             writeAttr(sanitizedLogicalName, rwFloatKey, bitLength, writerArgs);
             generator.writeNumberField(logicalName, value);
         });
     }
 
     @Override
-    public void writeDouble(String logicalName, double value, int bitsExponent, int bitsMantissa, WithWriterArgs... writerArgs) throws ParseException {
+    public void writeDouble(String logicalName, int bitLength, double value, WithWriterArgs... writerArgs) throws ParseException {
         final String sanitizedLogicalName = sanitizeLogicalName(logicalName);
         wrapIfNecessary(() -> {
-            int bitLength = (value < 0 ? 1 : 0) + bitsExponent + bitsMantissa;
             writeAttr(sanitizedLogicalName, rwFloatKey, bitLength, writerArgs);
             generator.writeNumberField(sanitizedLogicalName, value);
         });
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferXmlBased.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferXmlBased.java
index de94548..d09858f 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferXmlBased.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferXmlBased.java
@@ -18,7 +18,6 @@
  */
 package org.apache.plc4x.java.spi.generation;
 
-import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 
@@ -172,15 +171,13 @@ public class WriteBufferXmlBased implements WriteBuffer, BufferCommons {
     }
 
     @Override
-    public void writeFloat(String logicalName, float value, int bitsExponent, int bitsMantissa, WithWriterArgs... writerArgs) throws ParseException {
-        int bitLength = (value < 0 ? 1 : 0) + bitsExponent + bitsMantissa;
+    public void writeFloat(String logicalName, int bitLength, float value, WithWriterArgs... writerArgs) throws ParseException {
         createAndAppend(logicalName, rwFloatKey, bitLength, Float.toString(value), writerArgs);
         move(bitLength);
     }
 
     @Override
-    public void writeDouble(String logicalName, double value, int bitsExponent, int bitsMantissa, WithWriterArgs... writerArgs) throws ParseException {
-        int bitLength = (value < 0 ? 1 : 0) + bitsExponent + bitsMantissa;
+    public void writeDouble(String logicalName, int bitLength, double value, WithWriterArgs... writerArgs) throws ParseException {
         createAndAppend(logicalName, rwFloatKey, bitLength, Double.toString(value), writerArgs);
         move(bitLength);
     }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLREAL.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLREAL.java
index 83be2bf..3a675fc 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLREAL.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLREAL.java
@@ -28,7 +28,6 @@ import org.apache.plc4x.java.spi.generation.WriteBuffer;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.nio.charset.StandardCharsets;
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "className")
 public class PlcLREAL extends PlcIECValue<Double> {
@@ -266,7 +265,7 @@ public class PlcLREAL extends PlcIECValue<Double> {
 
     @Override
     public void serialize(WriteBuffer writeBuffer) throws ParseException {
-        writeBuffer.writeDouble(getClass().getSimpleName(), value, 11, 52);
+        writeBuffer.writeDouble(getClass().getSimpleName(), 64, value);
     }
 
 }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcREAL.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcREAL.java
index 21f4a43..9032127 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcREAL.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcREAL.java
@@ -25,7 +25,6 @@ import org.apache.plc4x.java.spi.generation.WriteBuffer;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.nio.charset.StandardCharsets;
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "className")
 public class PlcREAL extends PlcIECValue<Float> {
@@ -257,7 +256,7 @@ public class PlcREAL extends PlcIECValue<Float> {
 
     @Override
     public void serialize(WriteBuffer writeBuffer) throws ParseException {
-        writeBuffer.writeDouble(getClass().getSimpleName(), value, 8, 23);
+        writeBuffer.writeDouble(getClass().getSimpleName(), 32, value);
     }
 
 }
diff --git a/protocols/ads/src/main/resources/protocols/ads/ads.mspec b/protocols/ads/src/main/resources/protocols/ads/ads.mspec
index b62fb90..40050d3 100644
--- a/protocols/ads/src/main/resources/protocols/ads/ads.mspec
+++ b/protocols/ads/src/main/resources/protocols/ads/ads.mspec
@@ -448,10 +448,10 @@
         // Floating point values
         // -----------------------------------------
         ['IEC61131_REAL' REAL
-            [simple float 8.23  'value']
+            [simple float 32  'value']
         ]
         ['IEC61131_LREAL' LREAL
-            [simple float 11.52 'value']
+            [simple float 64 'value']
         ]
 
         // -----------------------------------------
diff --git a/protocols/bacnetip/src/main/resources/protocols/bacnetip/bacnetip.mspec b/protocols/bacnetip/src/main/resources/protocols/bacnetip/bacnetip.mspec
index aac3cbe..eea1894 100644
--- a/protocols/bacnetip/src/main/resources/protocols/bacnetip/bacnetip.mspec
+++ b/protocols/bacnetip/src/main/resources/protocols/bacnetip/bacnetip.mspec
@@ -348,10 +348,10 @@
             [array int 8 'data' length '(lengthValueType == 5) ? extLength : lengthValueType']
         ]
         ['REAL' BACnetComplexTagReal [uint 3 'lengthValueType', uint 8 'extLength']
-            [simple float 8.23 'value']
+            [simple float 32 'value']
         ]
         ['DOUBLE' BACnetComplexTagDouble [uint 3 'lengthValueType', uint 8 'extLength']
-            [simple float 11.52 'value']
+            [simple float 64 'value']
         ]
         ['OCTET_STRING' BACnetComplexTagOctetString [uint 32 'actualLength']
             // TODO: The reader expects int but uint32 get's mapped to long so even uint32 would easily overflow...
@@ -561,10 +561,10 @@
             [array int 8 'data' length '(lengthValueType == 5) ? extLength : lengthValueType']
         ]
         ['APPLICATION_TAGS','0x4' BACnetTagApplicationReal [uint 3 'lengthValueType', uint 8 'extLength']
-            [simple float 8.23 'value']
+            [simple float 32 'value']
         ]
         ['APPLICATION_TAGS','0x5' BACnetTagApplicationDouble [uint 3 'lengthValueType', uint 8 'extLength']
-            [simple float 11.52 'value']
+            [simple float 64 'value']
         ]
         ['APPLICATION_TAGS','0x6' BACnetTagApplicationOctetString
         ]
diff --git a/protocols/canopen/src/main/resources/protocols/can/canopen.mspec b/protocols/canopen/src/main/resources/protocols/can/canopen.mspec
index b613ad5..1f0bfab 100644
--- a/protocols/canopen/src/main/resources/protocols/can/canopen.mspec
+++ b/protocols/canopen/src/main/resources/protocols/can/canopen.mspec
@@ -324,10 +324,10 @@
             [simple int 64 'value']
         ]
         ['REAL32' REAL
-            [simple float 8.23 'value']
+            [simple float 32 'value']
         ]
         ['REAL64' LREAL
-            [simple float 11.52 'value']
+            [simple float 64 'value']
         ]
         ['RECORD' List [int 32 'size']
             [array int 8 'value' length 'size']
diff --git a/protocols/genericcan/src/main/resources/protocols/can/genericcan.mspec b/protocols/genericcan/src/main/resources/protocols/can/genericcan.mspec
index 26f491f..60f1b03 100644
--- a/protocols/genericcan/src/main/resources/protocols/can/genericcan.mspec
+++ b/protocols/genericcan/src/main/resources/protocols/can/genericcan.mspec
@@ -97,10 +97,10 @@
             [simple int 64 'value']
         ]
         ['REAL32' REAL
-            [simple float 8.23 'value']
+            [simple float 32 'value']
         ]
         ['REAL64' LREAL
-            [simple float 11.52 'value']
+            [simple float 64 'value']
         ]
     ]
 ]
diff --git a/protocols/knxnetip/src/main/resources/protocols/knxnetip/knxnetip.mspec b/protocols/knxnetip/src/main/resources/protocols/knxnetip/knxnetip.mspec
index 3788de8..f749916 100644
--- a/protocols/knxnetip/src/main/resources/protocols/knxnetip/knxnetip.mspec
+++ b/protocols/knxnetip/src/main/resources/protocols/knxnetip/knxnetip.mspec
@@ -836,7 +836,7 @@
             [simple   uint 16       'value']
         ]
         ['PDT_KNX_FLOAT' REAL
-            [simple   float 4.11    'value']
+            [simple   float 16      'value' encoding='"KNXFloat"']
         ]
         ['PDT_DATE' Struct
             [reserved uint 3 '0x00']
@@ -860,10 +860,10 @@
             [simple   uint 32       'value']
         ]
         ['PDT_FLOAT' REAL
-            [simple   float 8.23    'value']
+            [simple   float 32    'value']
         ]
         ['PDT_DOUBLE' LREAL
-            [simple   float 11.52   'value']
+            [simple   float 64   'value']
         ]
         ['PDT_CHAR_BLOCK' List
             [array uint 8           'value' count '10']
diff --git a/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec b/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec
index 8f29eec..750dc76 100644
--- a/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec
+++ b/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec
@@ -352,16 +352,16 @@
             [array uint 64 'value' count 'numberOfValues']
         ]
         ['REAL','1' REAL
-            [simple float 8.23  'value']
+            [simple float 32  'value']
         ]
         ['REAL' List
-            [array float 8.23 'value' count 'numberOfValues']
+            [array float 32 'value' count 'numberOfValues']
         ]
         ['LREAL','1' LREAL
-            [simple float 11.52  'value']
+            [simple float 64  'value']
         ]
         ['LREAL' List
-            [array float 11.52 'value' count 'numberOfValues']
+            [array float 64 'value' count 'numberOfValues']
         ]
         ['CHAR','1' CHAR
             [simple uint 8 'value']
diff --git a/protocols/s7/src/main/resources/protocols/s7/s7.mspec b/protocols/s7/src/main/resources/protocols/s7/s7.mspec
index af96a7e..9e35fa6 100644
--- a/protocols/s7/src/main/resources/protocols/s7/s7.mspec
+++ b/protocols/s7/src/main/resources/protocols/s7/s7.mspec
@@ -587,10 +587,10 @@
         // Floating point values
         // -----------------------------------------
         ['IEC61131_REAL' REAL
-            [simple float 8.23  'value']
+            [simple float 32  'value']
         ]
         ['IEC61131_LREAL' LREAL
-            [simple float 11.52 'value']
+            [simple float 64 'value']
         ]
 
         // -----------------------------------------
diff --git a/protocols/simulated/src/main/resources/protocols/simulated/simulated.mspec b/protocols/simulated/src/main/resources/protocols/simulated/simulated.mspec
index a5d5bb5..7cd895a 100644
--- a/protocols/simulated/src/main/resources/protocols/simulated/simulated.mspec
+++ b/protocols/simulated/src/main/resources/protocols/simulated/simulated.mspec
@@ -104,16 +104,16 @@
             [array uint 64 'value' count 'numberOfValues']
         ]
         ['REAL','1' REAL
-            [simple float 8.23  'value']
+            [simple float 32  'value']
         ]
         ['REAL' List
-            [array float 8.23 'value' count 'numberOfValues']
+            [array float 32 'value' count 'numberOfValues']
         ]
         ['LREAL','1' LREAL
-            [simple float 11.52  'value']
+            [simple float 64  'value']
         ]
         ['LREAL' List
-            [array float 11.52 'value' count 'numberOfValues']
+            [array float 64 'value' count 'numberOfValues']
         ]
         ['CHAR','1' CHAR
             [simple uint 8 'value']