Repository: xlate/staedi (java)
Issue: Incorrect element position reported for composite syntax errors
Incorrect element position reported for composite syntax errors. The element position is being reported using the segment's position in the loop/transaction rather than the composite position in the segment.
Pull request description: Use composite index as element position for syntax error event 
Fixes #140 

Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index a8cc7eb..43d3a77 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -36,7 +36,7 @@ jobs:
       - uses: actions/checkout@v2
         name: Checkout
 
-      - uses: actions/setup-java@v1.3.0
+      - uses: actions/setup-java@v1
         name: Setup JDK ${{matrix.java}}
         with:
           java-version: ${{matrix.java}}
@@ -69,7 +69,7 @@ jobs:
 
     steps:
       - uses: actions/checkout@v2
-      - uses: actions/setup-java@v1.3.0
+      - uses: actions/setup-java@v1
         with:
           java-version: 11
 
@@ -93,7 +93,7 @@ jobs:
       - run: |
           git fetch --prune --unshallow --tags --force
 
-      - uses: actions/setup-java@v1.3.0
+      - uses: actions/setup-java@v1
         with:
           java-version: 11
 
diff --git a/src/main/java/io/xlate/edi/internal/stream/validation/SyntaxValidator.java b/src/main/java/io/xlate/edi/internal/stream/validation/SyntaxValidator.java
index 3f5cea4..53a6367 100644
--- a/src/main/java/io/xlate/edi/internal/stream/validation/SyntaxValidator.java
+++ b/src/main/java/io/xlate/edi/internal/stream/validation/SyntaxValidator.java
@@ -169,7 +169,7 @@ interface SyntaxValidator {
 
     static int getElementPosition(UsageNode structure, int position) {
         if (structure.isNodeType(EDIType.Type.COMPOSITE)) {
-            return structure.getParent().getIndex() + 1;
+            return structure.getIndex() + 1;
         }
 
         return position;


Test patch:
diff --git a/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamReaderSyntaxTest.java b/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamReaderSyntaxTest.java
index 8e604c6..e2dbf0c 100644
--- a/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamReaderSyntaxTest.java
+++ b/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamReaderSyntaxTest.java
@@ -8,6 +8,8 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import io.xlate.edi.schema.EDISchemaException;
 import io.xlate.edi.schema.Schema;
@@ -49,53 +51,36 @@ class StaEDIStreamReaderSyntaxTest {
         return new Object[] { errors, errorLocations };
     }
 
-    @Test
-    void testFirstSyntaxValidation_NonePresent() throws Exception {
-        Object[] result = readInterchange(""
-                + "UNB+UNOA:3+SENDER+RECEIVER+200906:1148+1'"
-                + "UNH+1+INVOIC:D:93A:UN'"
-                + "UXA+++'"
-                + "UNT+3+1'"
-                + "UNZ+1+1'", "/EDIFACT/fragment-first-syntax-validation.xml");
-
-        List<EDIStreamValidationError> errors = (List<EDIStreamValidationError>) result[0];
-        assertEquals(0, errors.size(), () -> "Unexpected errors: " + errors);
-    }
-
-    @Test
-    void testFirstSyntaxValidation_FirstOnlyPresent() throws Exception {
-        Object[] result = readInterchange(""
-                + "UNB+UNOA:3+SENDER+RECEIVER+200906:1148+1'"
-                + "UNH+1+INVOIC:D:93A:UN'"
-                + "UXA+FIRST++'"
-                + "UNT+3+1'"
-                + "UNZ+1+1'", "/EDIFACT/fragment-first-syntax-validation.xml");
-
-        List<EDIStreamValidationError> errors = (List<EDIStreamValidationError>) result[0];
-        assertEquals(0, errors.size(), () -> "Unexpected errors: " + errors);
-    }
-
-    @Test
-    void testFirstSyntaxValidation_FirstAndSecondPresent() throws Exception {
-        Object[] result = readInterchange(""
-                + "UNB+UNOA:3+SENDER+RECEIVER+200906:1148+1'"
-                + "UNH+1+INVOIC:D:93A:UN'"
-                + "UXA+FIRST+SECOND+'"
-                + "UNT+3+1'"
-                + "UNZ+1+1'", "/EDIFACT/fragment-first-syntax-validation.xml");
-
-        List<EDIStreamValidationError> errors = (List<EDIStreamValidationError>) result[0];
-        assertEquals(0, errors.size(), () -> "Unexpected errors: " + errors);
-    }
-
-    @Test
-    void testFirstSyntaxValidation_SecondAndThirdPresent() throws Exception {
-        Object[] result = readInterchange(""
-                + "UNB+UNOA:3+SENDER+RECEIVER+200906:1148+1'"
-                + "UNH+1+INVOIC:D:93A:UN'"
-                + "UXA++SECOND+THIRD'"
-                + "UNT+3+1'"
-                + "UNZ+1+1'", "/EDIFACT/fragment-first-syntax-validation.xml");
+    @ParameterizedTest
+    @ValueSource(
+            strings = {
+                        "" // None Present
+                                + "UNB+UNOA:3+SENDER+RECEIVER+200906:1148+1'"
+                                + "UNH+1+INVOIC:D:93A:UN'"
+                                + "UXA+++'"
+                                + "UNT+3+1'"
+                                + "UNZ+1+1'",
+                        "" // First Only Present
+                                + "UNB+UNOA:3+SENDER+RECEIVER+200906:1148+1'"
+                                + "UNH+1+INVOIC:D:93A:UN'"
+                                + "UXA+FIRST++'"
+                                + "UNT+3+1'"
+                                + "UNZ+1+1'",
+                        "" // First And Second Present
+                                + "UNB+UNOA:3+SENDER+RECEIVER+200906:1148+1'"
+                                + "UNH+1+INVOIC:D:93A:UN'"
+                                + "UXA+FIRST+SECOND+'"
+                                + "UNT+3+1'"
+                                + "UNZ+1+1'",
+                        "" // Second And Third Present
+                                + "UNB+UNOA:3+SENDER+RECEIVER+200906:1148+1'"
+                                + "UNH+1+INVOIC:D:93A:UN'"
+                                + "UXA++SECOND+THIRD'"
+                                + "UNT+3+1'"
+                                + "UNZ+1+1'"
+            })
+    void testFirstSyntaxValidation_Valid(String interchange) throws Exception {
+        Object[] result = readInterchange(interchange, "/EDIFACT/fragment-first-syntax-validation.xml");
 
         List<EDIStreamValidationError> errors = (List<EDIStreamValidationError>) result[0];
         assertEquals(0, errors.size(), () -> "Unexpected errors: " + errors);
diff --git a/src/test/java/io/xlate/edi/internal/stream/validation/ListSyntaxValidatorTest.java b/src/test/java/io/xlate/edi/internal/stream/validation/ListSyntaxValidatorTest.java
index 63c9637..7c6374c 100644
--- a/src/test/java/io/xlate/edi/internal/stream/validation/ListSyntaxValidatorTest.java
+++ b/src/test/java/io/xlate/edi/internal/stream/validation/ListSyntaxValidatorTest.java
@@ -5,9 +5,9 @@ import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -113,10 +113,7 @@ class ListSyntaxValidatorTest extends SyntaxValidatorTestBase {
     @Test
     void testValidateListConditionalMissingRequired_Composite() {
         when(structure.isNodeType(EDIType.Type.COMPOSITE)).thenReturn(true);
-
-        UsageNode parent = mock(UsageNode.class);
-        when(structure.getParent()).thenReturn(parent);
-        when(parent.getIndex()).thenReturn(0); // composite is the first element in the parent segment
+        when(structure.getIndex()).thenReturn(2); // composite is the third element in the parent segment
 
         when(syntax.getPositions()).thenReturn(Arrays.asList(1, 3, 4));
 
@@ -125,24 +122,23 @@ class ListSyntaxValidatorTest extends SyntaxValidatorTestBase {
                                                  mockUsageNode(false, 3)/*,
                                                                         mockUsageNode(false, 4)*/);
         when(structure.getChildren()).thenReturn(children);
-        final AtomicInteger count = new AtomicInteger(0);
-        final AtomicInteger element = new AtomicInteger(0);
+        final List<Integer> elements = new ArrayList<>();
 
         doAnswer((Answer<Void>) invocation -> {
-            count.incrementAndGet();
-            element.set(invocation.getArgument(4));
+            elements.add(invocation.getArgument(5));
             return null;
         }).when(handler)
           .elementError(eq(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR),
                         eq(EDIStreamValidationError.CONDITIONAL_REQUIRED_DATA_ELEMENT_MISSING),
                         nullable(EDIReference.class),
                         nullable(CharSequence.class),
-                        any(Integer.class),
+                        eq(3),
                         any(Integer.class),
                         any(Integer.class));
 
         validator.validate(syntax, structure, handler);
-        assertEquals(1, element.get());
-        assertEquals(2, count.get()); // Error for both positions 3 and 4
+        assertEquals(2, elements.size()); // Error for both positions 3 and 4
+        assertEquals(3, elements.get(0));
+        assertEquals(4, elements.get(1));
     }
 }
diff --git a/src/test/java/io/xlate/edi/internal/stream/validation/SyntaxValidatorTestBase.java b/src/test/java/io/xlate/edi/internal/stream/validation/SyntaxValidatorTestBase.java
index f3ec06d..4067323 100644
--- a/src/test/java/io/xlate/edi/internal/stream/validation/SyntaxValidatorTestBase.java
+++ b/src/test/java/io/xlate/edi/internal/stream/validation/SyntaxValidatorTestBase.java
@@ -43,7 +43,6 @@ abstract class SyntaxValidatorTestBase {
         when(typeReference.getReferencedType()).thenReturn(type);
         when(node.getLink()).thenReturn(typeReference);
         when(node.isUsed()).thenReturn(used);
-        when(node.getParent()).thenReturn(structure);
         when(node.getIndex()).thenReturn(index);
 
         return node;