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

[plc4x] 01/02: fix(plc4j/codgen): implement array type reading with new readern

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

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

commit 90e40fd5c02b0384ccfa205cfb8e109bf56f6e1b
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Oct 22 18:23:04 2021 +0200

    fix(plc4j/codgen): implement array type reading with new readern
---
 .../resources/templates/java/io-template.java.ftlh | 122 ++-------------------
 .../plc4x/java/spi/codegen/fields/FieldReader.java |   1 +
 .../java/spi/codegen/fields/FieldReaderArray.java  |  56 ++++++++--
 .../spi/codegen/fields/FieldReaderFactory.java     |  54 ++++++++-
 4 files changed, 104 insertions(+), 129 deletions(-)

diff --git a/code-generation/language-java/src/main/resources/templates/java/io-template.java.ftlh b/code-generation/language-java/src/main/resources/templates/java/io-template.java.ftlh
index 8aeadd8..af61334 100644
--- a/code-generation/language-java/src/main/resources/templates/java/io-template.java.ftlh
+++ b/code-generation/language-java/src/main/resources/templates/java/io-template.java.ftlh
@@ -148,6 +148,7 @@ public class ${type.name}IO implements <#if outputFlavor != "passive">MessageIO<
     <#-- TODO: Port this -->
     <#case "array">
         <#assign arrayField = field.asArrayField().orElseThrow()>
+
         <#if arrayField.type.isByteBased()>
         // Byte Array field (${arrayField.name})
             <#assign numberOfBytesExpression>
@@ -169,7 +170,6 @@ public class ${type.name}IO implements <#if outputFlavor != "passive">MessageIO<
         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;
@@ -177,130 +177,22 @@ public class ${type.name}IO implements <#if outputFlavor != "passive">MessageIO<
         <#-- 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>
-            }
-        }
+        Object ${arrayField.name}Object = readCountArrayField(${helper.getLanguageTypeNameForField(field)}.class, "${arrayField.name}", ${helper.getDataReaderCall(arrayField.type)}, ${helper.toParseExpression(arrayField, arrayField.loopExpression, parserArguments)});
+        ${helper.getLanguageTypeNameForField(field)}[] ${arrayField.name} = (${helper.getLanguageTypeNameForField(field)}[]) ${arrayField.name}Object;
         <#-- 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>
-        }
+        Object ${arrayField.name}Object = readLengthArrayField(${helper.getLanguageTypeNameForField(field)}.class, "${arrayField.name}", ${helper.getDataReaderCall(arrayField.type)}, ${helper.toParseExpression(arrayField, arrayField.loopExpression, parserArguments)});
+        ${helper.getLanguageTypeNameForField(field)}[] ${arrayField.name} = (${helper.getLanguageTypeNameForField(field)}[]) ${arrayField.name}Object;
             <#-- 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]);
+        Object ${arrayField.name}Object = readTerminatedArrayField(${helper.getLanguageTypeNameForField(field)}.class, "${arrayField.name}", ${helper.getDataReaderCall(arrayField.type)}, () -> ((boolean) (${helper.toParseExpression(arrayField, arrayField.loopExpression, parserArguments)})));
+        ${helper.getLanguageTypeNameForField(field)}[] ${arrayField.name} = (${helper.getLanguageTypeNameForField(field)}[]) ${arrayField.name}Object;
                 </#if>
             </#if>
-            readBuffer.closeContext("${arrayField.name}", WithReaderWriterArgs.WithRenderAsList(true));
         </#if>
         <#break>
     <#case "assert">
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
index 382e3ef..86d43f3 100644
--- 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
@@ -25,6 +25,7 @@ import org.apache.plc4x.java.spi.generation.WithReaderArgs;
 
 public interface FieldReader<T> extends FieldCommons {
 
+    // TODO: this is useless here as you always operate on the concrete instance
     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/FieldReaderArray.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderArray.java
index 1724c62..251d305 100644
--- 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
@@ -18,31 +18,65 @@
  */
 package org.apache.plc4x.java.spi.codegen.fields;
 
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.NotImplementedException;
 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;
+import org.apache.plc4x.java.spi.generation.WithReaderWriterArgs;
 
-public class FieldReaderArray<T> implements FieldReader<T> {
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Supplier;
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(FieldReaderArray.class);
+public class FieldReaderArray<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));
+        throw new NotImplementedException("use dedicated methods instead");
     }
 
-    public T[] readFieldCount(String logicalName, DataReader<T> dataReader, int count, WithReaderArgs... readerArgs) throws ParseException {
-        return null;
+    public List<T> readFieldCount(String logicalName, DataReader<T> dataReader, long count, WithReaderArgs... readerArgs) throws ParseException {
+        if (count > Integer.MAX_VALUE) {
+            throw new ParseException("Array count of " + count + " exceeds the maximum allowed count of " + Integer.MAX_VALUE);
+        }
+        // Ensure we have the render as list argument present
+        readerArgs = ArrayUtils.add(readerArgs, WithReaderWriterArgs.WithRenderAsList(true));
+        dataReader.pullContext(logicalName, readerArgs);
+        int itemCount = Math.max(0, (int) count);
+        List<T> result = new ArrayList<>(itemCount);
+        for (int curItem = 0; curItem < itemCount; curItem++) {
+            result.set(curItem, dataReader.read("", readerArgs));
+        }
+        dataReader.closeContext(logicalName, readerArgs);
+        return result;
     }
 
-    public T[] readFieldLength(String logicalName, DataReader<T> dataReader, int length, WithReaderArgs... readerArgs) throws ParseException {
-        return null;
+    public List<T> readFieldLength(String logicalName, DataReader<T> dataReader, int length, WithReaderArgs... readerArgs) throws ParseException {
+        // Ensure we have the render as list argument present
+        readerArgs = ArrayUtils.add(readerArgs, WithReaderWriterArgs.WithRenderAsList(true));
+        dataReader.pullContext(logicalName, readerArgs);
+        List<T> result = new LinkedList<>();
+        while (dataReader.getPos() < length) {
+            result.add(dataReader.read("", readerArgs));
+        }
+        dataReader.closeContext(logicalName, readerArgs);
+        return result;
     }
 
-    public T[] readFieldTerminated(String logicalName, DataReader<T> dataReader, T termination, WithReaderArgs... readerArgs) throws ParseException {
-        return null;
+    public List<T> readFieldTerminated(String logicalName, DataReader<T> dataReader, Supplier<Boolean> termination, WithReaderArgs... readerArgs) throws ParseException {
+        // Ensure we have the render as list argument present
+        readerArgs = ArrayUtils.add(readerArgs, WithReaderWriterArgs.WithRenderAsList(true));
+        dataReader.pullContext(logicalName, readerArgs);
+        List<T> result = new LinkedList<>();
+        while (!termination.get()) {
+            result.add(dataReader.read("", readerArgs));
+        }
+        dataReader.closeContext(logicalName, readerArgs);
+        return result;
     }
 
 }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderFactory.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderFactory.java
index da87857..567a050 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderFactory.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldReaderFactory.java
@@ -22,14 +22,62 @@ 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 java.lang.reflect.Array;
+import java.util.List;
+import java.util.function.Supplier;
+
 public class FieldReaderFactory {
 
     public static <T> T readAbstractField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
         return new FieldReaderAbstract<T>().readField(logicalName, dataReader, readerArgs);
     }
 
-    public static <T> T readArrayField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
-        return new FieldReaderArray<T>().readField(logicalName, dataReader, readerArgs);
+    @Deprecated
+    @SuppressWarnings("unchecked")
+    public static <T> T[] readCountArrayField(Class<T> t, String logicalName, DataReader<T> dataReader, long count, WithReaderArgs... readerArgs) throws ParseException {
+        List<T> innerResult = readCountArrayField(logicalName, dataReader, count, readerArgs);
+        T[] result = (T[]) Array.newInstance(t, innerResult.size());
+        for (int curItem = 0; curItem < innerResult.size(); curItem++) {
+            result[curItem] = innerResult.get(curItem);
+        }
+        dataReader.closeContext(logicalName, readerArgs);
+        return result;
+    }
+
+    public static <T> List<T> readCountArrayField(String logicalName, DataReader<T> dataReader, long count, WithReaderArgs... readerArgs) throws ParseException {
+        return new FieldReaderArray<T>().readFieldCount(logicalName, dataReader, count, readerArgs);
+    }
+
+    @Deprecated
+    @SuppressWarnings("unchecked")
+    public static <T> T[] readLengthArrayField(Class<T> t, String logicalName, DataReader<T> dataReader, int length, WithReaderArgs... readerArgs) throws ParseException {
+        List<T> innerResult = readLengthArrayField(logicalName, dataReader, length, readerArgs);
+        T[] result = (T[]) Array.newInstance(t, innerResult.size());
+        for (int curItem = 0; curItem < innerResult.size(); curItem++) {
+            result[curItem] = innerResult.get(curItem);
+        }
+        dataReader.closeContext(logicalName, readerArgs);
+        return result;
+    }
+
+    public static <T> List<T> readLengthArrayField(String logicalName, DataReader<T> dataReader, int length, WithReaderArgs... readerArgs) throws ParseException {
+        return new FieldReaderArray<T>().readFieldLength(logicalName, dataReader, length, readerArgs);
+    }
+
+    @Deprecated
+    @SuppressWarnings("unchecked")
+    public static <T> T[] readTerminatedArrayField(Class<T> t, String logicalName, DataReader<T> dataReader, Supplier<Boolean> termination, WithReaderArgs... readerArgs) throws ParseException {
+        List<T> innerResult = readTerminatedArrayField(logicalName, dataReader, termination, readerArgs);
+        T[] result = (T[]) Array.newInstance(t, innerResult.size());
+        for (int curItem = 0; curItem < innerResult.size(); curItem++) {
+            result[curItem] = innerResult.get(curItem);
+        }
+        dataReader.closeContext(logicalName, readerArgs);
+        return result;
+    }
+
+    public static <T> List<T> readTerminatedArrayField(String logicalName, DataReader<T> dataReader, Supplier<Boolean> termination, WithReaderArgs... readerArgs) throws ParseException {
+        return new FieldReaderArray<T>().readFieldTerminated(logicalName, dataReader, termination, readerArgs);
     }
 
     public static <T> T readAssertField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
@@ -69,7 +117,7 @@ public class FieldReaderFactory {
     }
 
     public static <T> T readSimpleField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {
-        return new FieldReaderSimple<T>().readField(logicalName,dataReader,readerArgs);
+        return new FieldReaderSimple<T>().readField(logicalName, dataReader, readerArgs);
     }
 
     public static <T> T readUnknownField(String logicalName, DataReader<T> dataReader, WithReaderArgs... readerArgs) throws ParseException {