Repository: xlate/staedi (java)

Issue:
NPE on Validator#handleImplementationSelected, line 740, v1.10.2
It works with 1.10.1, but fails with 1.10.2. Test case is attached.

[validation-npe-impl-schema.zip](https://github.com/xlate/staedi/files/5183575/validation-npe-impl-schema.zip)



Pull request:
Clear `Validator` element fields upon validation of new segment
Fixes #106 

Patch:
diff --git a/src/main/java/io/xlate/edi/internal/stream/validation/Validator.java b/src/main/java/io/xlate/edi/internal/stream/validation/Validator.java
index 837bc3e..c204e24 100644
--- a/src/main/java/io/xlate/edi/internal/stream/validation/Validator.java
+++ b/src/main/java/io/xlate/edi/internal/stream/validation/Validator.java
@@ -185,12 +185,8 @@ public class Validator {
         depth = 1;
 
         segmentExpected = false;
-        composite = null;
-        element = null;
-
         implSegmentSelected = false;
-        implComposite = null;
-        implElement = null;
+        clearElements();
 
         implSegmentCandidates.clear();
         useErrors.clear();
@@ -198,6 +194,14 @@ public class Validator {
         initial = true;
     }
 
+    void clearElements() {
+        composite = null;
+        element = null;
+
+        implComposite = null;
+        implElement = null;
+    }
+
     public boolean isPendingDiscrimination() {
         return !implSegmentCandidates.isEmpty();
     }
@@ -380,6 +384,7 @@ public class Validator {
         initial = false;
         segmentExpected = true;
         implSegmentSelected = false;
+        clearElements();
 
         final int startDepth = this.depth;
 
@@ -935,12 +940,8 @@ public class Validator {
         }
 
         boolean valueReceived = value != null && value.length() > 0;
+        clearElements();
         elementErrors.clear();
-        this.composite = null;
-        this.element = null;
-
-        this.implComposite = null;
-        this.implElement = null;
 
         int elementPosition = position.getElementPosition() - 1;
         int componentIndex = position.getComponentPosition() - 1;


Test patch:
diff --git a/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamReaderTest.java b/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamReaderTest.java
index e046049..c2ceff1 100644
--- a/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamReaderTest.java
+++ b/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamReaderTest.java
@@ -1647,4 +1647,42 @@ class StaEDIStreamReaderTest implements ConstantsTest {
             reader.close();
         }
     }
+
+    /**
+     * Original issue: https://github.com/xlate/staedi/issues/106
+     *
+     * @throws Exception
+     */
+    @Test
+    void testValidatorResetElement_Issue106() throws Exception {
+        EDIInputFactory factory = EDIInputFactory.newFactory();
+        Schema transSchema = SchemaFactory.newFactory().createSchema(getClass().getResourceAsStream("/EDIFACT/issue106/null-pointer-impl-schema.xml"));
+        factory.setProperty(EDIInputFactory.EDI_VALIDATE_CONTROL_STRUCTURE, true);
+        factory.setProperty(EDIInputFactory.EDI_VALIDATE_CONTROL_CODE_VALUES, false);
+        EDIStreamReader reader = factory.createEDIStreamReader(getClass().getResourceAsStream("/EDIFACT/issue106/null-pointer-example.edi"));
+        List<Object> unexpected = new ArrayList<>();
+
+        try {
+            while (reader.hasNext()) {
+                switch (reader.next()) {
+                case START_TRANSACTION:
+                    reader.setTransactionSchema(transSchema);
+                    break;
+                case SEGMENT_ERROR:
+                case ELEMENT_OCCURRENCE_ERROR:
+                case ELEMENT_DATA_ERROR:
+                    unexpected.add(reader.getErrorType());
+                    break;
+                default:
+                    break;
+                }
+            }
+        } catch (Exception e) {
+            unexpected.add(e);
+        } finally {
+            reader.close();
+        }
+
+        assertEquals(0, unexpected.size());
+    }
 }
diff --git a/src/test/resources/EDIFACT/issue106/null-pointer-example.edi b/src/test/resources/EDIFACT/issue106/null-pointer-example.edi
new file mode 100644
index 0000000..4093e63
--- /dev/null
+++ b/src/test/resources/EDIFACT/issue106/null-pointer-example.edi
@@ -0,0 +1,12 @@
+UNA:+.?*'
+UNB+IATA:1+1A+ONIP+200220:1201+0001+++X'
+UNH+1+PNRGOV:11:1:IA+200220/1201/NT/6604'
+TVL+210220:1200:210220:1415+LPA+DSS+NT+6604'
+EQN+1'
+SRC'
+RCI+1A:QE66TI::130220:1119'
+TVL+210220:1200:210220:1415+LPA+DSS+NT+6604:Y'
+RCI+1A:QE66TI::130220:1119'
+DAT'
+UNT+10+1'
+UNZ+1+0001'
\ No newline at end of file
diff --git a/src/test/resources/EDIFACT/issue106/null-pointer-impl-schema.xml b/src/test/resources/EDIFACT/issue106/null-pointer-impl-schema.xml
new file mode 100644
index 0000000..ee51d84
--- /dev/null
+++ b/src/test/resources/EDIFACT/issue106/null-pointer-impl-schema.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<schema xmlns="http://xlate.io/EDISchema/v4">
+    <include schemaLocation="file:./src/test/resources/EDIFACT/issue106/null-pointer-transaction-schema.xml" />
+
+    <implementation>
+        <sequence>
+            <segment type="TVL" />
+            <segment type="EQN" />
+            <loop code="GR1_SRC_PUSH" type="GR1_SRC">
+                <sequence>
+                    <segment type="SRC" />
+                    <segment type="RCI" />
+                    <loop code="GR5_TVL_PUSH" type="GR5_TVL">
+                        <sequence>
+                            <segment type="TVL" />
+                            <segment type="RCI" />
+                            <loop code="GR6_DAT_PUSH" type="GR6_DAT">
+                                <sequence>
+                                    <segment type="DAT" />
+                                </sequence>
+                            </loop>
+                        </sequence>
+                    </loop>
+               </sequence>
+            </loop>
+        </sequence>
+    </implementation>
+</schema>
\ No newline at end of file
diff --git a/src/test/resources/EDIFACT/issue106/null-pointer-transaction-schema.xml b/src/test/resources/EDIFACT/issue106/null-pointer-transaction-schema.xml
new file mode 100644
index 0000000..41deff3
--- /dev/null
+++ b/src/test/resources/EDIFACT/issue106/null-pointer-transaction-schema.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+
+<schema xmlns="http://xlate.io/EDISchema/v4">
+
+    <transaction>
+        <sequence>
+            <segment type="TVL" minOccurs="1" maxOccurs="1" />
+            <segment type="EQN" minOccurs="1" maxOccurs="1" />
+            <loop code="GR1_SRC" maxOccurs="999">
+                <sequence>
+                    <segment type="SRC" minOccurs="1" maxOccurs="1" />
+                    <segment type="RCI" minOccurs="1" maxOccurs="1" />
+                    <loop code="GR5_TVL" maxOccurs="999">
+                        <sequence>
+                            <segment type="TVL" minOccurs="1" maxOccurs="1" />
+                            <segment type="RCI" minOccurs="0" maxOccurs="1" />
+                            <loop code="GR6_DAT" maxOccurs="999">
+                                <sequence>
+                                    <segment type="DAT" minOccurs="1" maxOccurs="1" />
+                                </sequence>
+                            </loop>
+                        </sequence>
+                    </loop>
+                </sequence>
+            </loop>
+        </sequence>
+    </transaction>
+
+    <!-- ==================================== Element types ===================================== -->
+
+    <!-- Numbers -->
+    <elementType name="EQNumber" base="numeric" maxLength="3" />
+
+    <!-- Dates -->
+    <elementType name="Date" base="date" minLength="6" maxLength="6" />
+    <elementType name="DateFreeText" base="string" maxLength="35" />
+    <elementType name="Time" base="time" minLength="4" maxLength="4" />
+    <elementType name="DateTimeQualifier" base="string" maxLength="3" />
+    
+    <!-- Identifiers -->
+    <elementType name="CompanyId" base="string" maxLength="3" />
+    <elementType name="ReservationCtrlNumber" base="string" maxLength="20" />
+
+    <!-- Codes -->
+    <elementType name="ReservationCtrlType" base="string" maxLength="1" />
+
+    <!-- ==================================== Composite types ===================================== -->
+
+    <compositeType name="DateTimeCompo">
+        <sequence>
+            <element type="DateTimeQualifier" minOccurs="0"/>
+            <element type="DateFreeText" minOccurs="0"/>
+            <element type="Time" minOccurs="0"/>
+        </sequence>
+    </compositeType>
+
+    <compositeType name="ReservationCtrlInfoCompo">
+        <sequence>
+            <element type="CompanyId" minOccurs="1"/>
+            <element type="ReservationCtrlNumber" minOccurs="1"/>
+            <element type="ReservationCtrlType" minOccurs="0"/>
+            <element type="Date" minOccurs="0"/>
+            <element type="Time" minOccurs="0"/>
+        </sequence>
+    </compositeType>
+
+    <!-- ==================================== Segment types ===================================== -->
+
+    <segmentType name="RCI">
+        <sequence>
+            <!-- "The composite C330 will appear at least once and may be repeated up to eight more times." -->
+            <!-- FIXME With maxOccurs="9" doesn't work and with explicit repetitions it throws NPE -->
+            <composite type="ReservationCtrlInfoCompo" minOccurs="1" />
+            <composite type="ReservationCtrlInfoCompo" minOccurs="0" />
+            <composite type="ReservationCtrlInfoCompo" minOccurs="0" />
+            <composite type="ReservationCtrlInfoCompo" minOccurs="0" />
+            <composite type="ReservationCtrlInfoCompo" minOccurs="0" />
+            <composite type="ReservationCtrlInfoCompo" minOccurs="0" />
+            <composite type="ReservationCtrlInfoCompo" minOccurs="0" />
+            <composite type="ReservationCtrlInfoCompo" minOccurs="0" />
+            <composite type="ReservationCtrlInfoCompo" minOccurs="0" />
+        </sequence>
+    </segmentType>
+
+    <segmentType name="DAT">
+        <sequence>
+            <composite type="DateTimeCompo" minOccurs="0" />
+        </sequence>
+    </segmentType>
+
+    <segmentType name="SRC">
+        <sequence />
+    </segmentType>
+
+    <segmentType name="EQN">
+        <sequence>
+            <element type="EQNumber" minOccurs="1" />
+        </sequence>
+    </segmentType>
+
+    <segmentType name="TVL">
+        <sequence>
+            <any maxOccurs="10" />
+        </sequence>
+    </segmentType>
+
+</schema>