Repository: xlate/staedi
Pull request #86 (2020-08-17 12:03:40)

Return impl schema types from validator when available
- Update START_SEGMENT event with impl segment when selected


Patch:
diff --git a/src/main/java/io/xlate/edi/internal/stream/tokenization/ProxyEventHandler.java b/src/main/java/io/xlate/edi/internal/stream/tokenization/ProxyEventHandler.java
index 55a3d9d..5027ded 100644
--- a/src/main/java/io/xlate/edi/internal/stream/tokenization/ProxyEventHandler.java
+++ b/src/main/java/io/xlate/edi/internal/stream/tokenization/ProxyEventHandler.java
@@ -212,7 +212,7 @@ public class ProxyEventHandler implements EventHandler {
 
         if (validator != null && !dialect.isServiceAdviceSegment(segmentTag)) {
             validator.validateSegment(this, segmentTag);
-            typeReference = validator.getSegmentReferenceCode();
+            typeReference = validator.getSegmentReference();
             eventsReady = !validator.isPendingDiscrimination();
         }
 
@@ -227,7 +227,7 @@ public class ProxyEventHandler implements EventHandler {
             // Now the control validator after setting transaction to false
             validator = validator();
             validator.validateSegment(this, segmentTag);
-            typeReference = validator().getSegmentReferenceCode();
+            typeReference = validator().getSegmentReference();
         }
 
         enqueueEvent(EDIStreamEvent.START_SEGMENT, EDIStreamValidationError.NONE, segmentTag, typeReference, location);
@@ -247,7 +247,7 @@ public class ProxyEventHandler implements EventHandler {
         if (validator != null) {
             validator.validateSyntax(dialect, this, this, location, false);
             validator.validateVersionConstraints(dialect, this);
-            typeReference = validator.getSegmentReferenceCode();
+            typeReference = validator.getSegmentReference();
         }
 
         location.clearSegmentLocations();
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 1c0f642..1e7bde9 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
@@ -202,7 +202,7 @@ public class Validator {
         return !implSegmentCandidates.isEmpty();
     }
 
-    public EDIReference getSegmentReferenceCode() {
+    public EDIReference getSegmentReference() {
         if (implSegmentSelected) {
             return implNode.getLink();
         }
@@ -210,6 +210,10 @@ public class Validator {
     }
 
     public EDIReference getCompositeReference() {
+        if (implSegmentSelected && implComposite != null) {
+            return implComposite.getLink();
+        }
+
         return composite != null ? composite.getLink() : null;
     }
 
@@ -227,12 +231,24 @@ public class Validator {
     }
 
     public EDIReference getElementReference() {
+        if (implSegmentSelected) {
+            if (implComposite != null) {
+                return implComposite.getLink();
+            }
+
+            if (implElement != null) {
+                return implElement.getLink();
+            }
+        }
+
         if (composite != null) {
             return composite.getLink();
         }
+
         if (element != null) {
             return element.getLink();
         }
+
         return null;
     }
 
@@ -696,8 +712,8 @@ public class Validator {
                 handleImplementationSelected(candidate, implSeg, handler);
 
                 if (implNode.isFirstChild()) {
-                    //start of loop
-                    setLoopReferenceCode(events, index, count - 1, implType);
+                    //start of loop, update the loop and segment references that were already reported
+                    updateEventReferences(events, index, count - 1, implType, implSeg.getLink());
 
                     // Replace the standard loop with the implementation on the stack
                     loopStack.pop();
@@ -772,15 +788,17 @@ public class Validator {
      * @param count
      * @param implType
      */
-    static void setLoopReferenceCode(StreamEvent[] events, int index, int count, PolymorphicImplementation implType) {
+    static void updateEventReferences(StreamEvent[] events, int index, int count, EDIReference implType, EDIReference implSeg) {
         for (int i = index; i < count; i++) {
-            String stdRefCode = events[i].getReferenceCode();
-            // This is the reference code of the impl's standard type
-            String implRefCode = ((EDIComplexType) implType.getReferencedType()).getCode();
+            setReferenceWhenMatched(events[i], EDIStreamEvent.START_LOOP, implType);
+            setReferenceWhenMatched(events[i], EDIStreamEvent.START_SEGMENT, implSeg);
+        }
+    }
 
-            if (events[i].getType() == EDIStreamEvent.START_LOOP && Objects.equals(stdRefCode, implRefCode)) {
-                events[i].setTypeReference(implType);
-            }
+    static void setReferenceWhenMatched(StreamEvent event, EDIStreamEvent type, EDIReference override) {
+        // The reference type code from override is the reference code of the impl's standard type
+        if (event.getType() == type && Objects.equals(event.getReferenceCode(), override.getReferencedType().getCode())) {
+            event.setTypeReference(override);
         }
     }
 


Test patch:
diff --git a/src/test/java/io/xlate/edi/internal/schema/implementation/ElementImplTest.java b/src/test/java/io/xlate/edi/internal/schema/implementation/ElementImplTest.java
index 0f7b0a9..8dd08c4 100644
--- a/src/test/java/io/xlate/edi/internal/schema/implementation/ElementImplTest.java
+++ b/src/test/java/io/xlate/edi/internal/schema/implementation/ElementImplTest.java
@@ -4,15 +4,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertNotSame;
 
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 
 import org.junit.jupiter.api.Test;
 
 import io.xlate.edi.schema.EDIReference;
+import io.xlate.edi.schema.EDISchemaException;
 import io.xlate.edi.schema.EDISimpleType;
 import io.xlate.edi.schema.EDIType;
+import io.xlate.edi.schema.SchemaFactory;
+import io.xlate.edi.stream.EDIInputFactory;
+import io.xlate.edi.stream.EDIStreamException;
+import io.xlate.edi.stream.EDIStreamReader;
+import io.xlate.edi.stream.EDIStreamValidationError;
 
 class ElementImplTest {
 
@@ -80,46 +89,63 @@ class ElementImplTest {
         assertNotEquals(e1, new ElementImpl(2, DFLT_MAX_OCCURS, DFLT_ID, DFLT_POSITION, DFLT_VALUES, DFLT_TITLE, DFLT_DESCR));
         assertNotEquals(e1, new ElementImpl(DFLT_MIN_OCCURS, 10, DFLT_ID, DFLT_POSITION, DFLT_VALUES, DFLT_TITLE, DFLT_DESCR));
         assertNotEquals(e1, new ElementImpl(DFLT_MIN_OCCURS, DFLT_MAX_OCCURS, "E0002", DFLT_POSITION, DFLT_VALUES, DFLT_TITLE, DFLT_DESCR));
-        assertNotEquals(e1, new ElementImpl(DFLT_MIN_OCCURS, DFLT_MAX_OCCURS, DFLT_ID, DFLT_POSITION, DFLT_VALUES, "Another Title", DFLT_DESCR));
-        assertNotEquals(e1, new ElementImpl(DFLT_MIN_OCCURS, DFLT_MAX_OCCURS, DFLT_ID, DFLT_POSITION, DFLT_VALUES, DFLT_TITLE, "Do not use!"));
+        assertNotEquals(e1,
+                        new ElementImpl(DFLT_MIN_OCCURS,
+                                        DFLT_MAX_OCCURS,
+                                        DFLT_ID,
+                                        DFLT_POSITION,
+                                        DFLT_VALUES,
+                                        "Another Title",
+                                        DFLT_DESCR));
+        assertNotEquals(e1,
+                        new ElementImpl(DFLT_MIN_OCCURS, DFLT_MAX_OCCURS, DFLT_ID, DFLT_POSITION, DFLT_VALUES, DFLT_TITLE, "Do not use!"));
 
         EDISimpleType standard = new EDISimpleType() {
             @Override
             public String getId() {
                 return "E0003";
             }
+
             @Override
             public String getCode() {
                 return "3";
             }
+
             @Override
             public Type getType() {
                 return Type.ELEMENT;
             }
+
             @Override
             public Base getBase() {
                 return Base.STRING;
             }
+
             @Override
             public int getNumber() {
                 return 3;
             }
+
             @Override
             public long getMinLength() {
                 return 0;
             }
+
             @Override
             public long getMaxLength() {
                 return 5;
             }
+
             @Override
             public Set<String> getValueSet() {
                 return Collections.emptySet();
             }
+
             @Override
             public String getTitle() {
                 return null;
             }
+
             @Override
             public String getDescription() {
                 return null;
@@ -140,10 +166,12 @@ class ElementImplTest {
             public int getMaxOccurs() {
                 return DFLT_MAX_OCCURS;
             }
+
             @Override
             public String getTitle() {
                 return null;
             }
+
             @Override
             public String getDescription() {
                 return null;
@@ -164,4 +192,57 @@ class ElementImplTest {
         assertEquals(e1.toString(), e2.toString());
     }
 
+    @Test
+    void testElementTypeDescriptionAccess() throws EDIStreamException, EDISchemaException {
+        EDIInputFactory factory = EDIInputFactory.newFactory();
+        ByteArrayInputStream stream = new ByteArrayInputStream((""
+                + "ISA*00*          *00*          *ZZ*ReceiverID     *ZZ*Sender         *200711*0100*^*00501*000000001*0*T*:~"
+                + "GS*HC*99999999999*888888888888*20111219*1340*1*X*005010X222~"
+                + "ST*837*0001*005010X222~"
+                + "BHT*0019*00*565743*20110523*154959*CH~"
+                + "NM1*41*2*SAMPLE INC*****46*496103~"
+                + "PER*IC*EDI DEPT*EM*FEEDBACK@example.com*TE*3305551212~"
+                + "NM1*40*2*PPO BLUE*****46*54771~"
+                + "HL*1**20~"
+                + "SE*6*0001~"
+                + "GE*1*1~"
+                + "IEA*1*000000001~").getBytes());
+
+        EDIStreamReader reader = factory.createEDIStreamReader(stream);
+        List<EDIStreamValidationError> errors = new ArrayList<>();
+        List<EDIReference> nm103references = new ArrayList<>();
+
+        while (reader.hasNext()) {
+            switch (reader.next()) {
+            case START_TRANSACTION:
+                reader.setTransactionSchema(SchemaFactory.newFactory()
+                                                         .createSchema(getClass().getResource("/x12/005010X222/837_loop1000_only.xml")));
+                break;
+            case ELEMENT_DATA:
+                if ("NM103".equals(reader.getReferenceCode())) {
+                    nm103references.add(reader.getSchemaTypeReference());
+                }
+                break;
+            case SEGMENT_ERROR:
+            case ELEMENT_OCCURRENCE_ERROR:
+            case ELEMENT_DATA_ERROR:
+                errors.add(reader.getErrorType());
+                System.out.println("Unexpected error: " + reader.getErrorType() + "; " + reader.getText() + "; " + reader.getLocation());
+                break;
+            default:
+                break;
+            }
+        }
+
+        assertEquals(0, errors.size(), () -> errors.toString());
+
+        // Values set in 837_loop1000_only.xml
+        assertEquals(2, nm103references.size());
+        assertEquals("Submitter Last or Organization Name", nm103references.get(0).getTitle());
+        assertEquals("Receiver Name", nm103references.get(1).getTitle());
+
+        // Values of NM103 standard set in 837.xml
+        assertEquals("Name Last or Organization Name", nm103references.get(0).getReferencedType().getTitle());
+        assertEquals("Name Last or Organization Name", nm103references.get(1).getReferencedType().getTitle());
+    }
 }
diff --git a/src/test/java/io/xlate/edi/internal/schema/implementation/SegmentImplTest.java b/src/test/java/io/xlate/edi/internal/schema/implementation/SegmentImplTest.java
new file mode 100644
index 0000000..c26e88d
--- /dev/null
+++ b/src/test/java/io/xlate/edi/internal/schema/implementation/SegmentImplTest.java
@@ -0,0 +1,74 @@
+package io.xlate.edi.internal.schema.implementation;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import io.xlate.edi.schema.EDIReference;
+import io.xlate.edi.schema.EDISchemaException;
+import io.xlate.edi.schema.SchemaFactory;
+import io.xlate.edi.stream.EDIInputFactory;
+import io.xlate.edi.stream.EDIStreamException;
+import io.xlate.edi.stream.EDIStreamReader;
+import io.xlate.edi.stream.EDIStreamValidationError;
+
+class SegmentImplTest {
+
+    @Test
+    void testElementTypeDescriptionAccess() throws EDIStreamException, EDISchemaException {
+        EDIInputFactory factory = EDIInputFactory.newFactory();
+        ByteArrayInputStream stream = new ByteArrayInputStream((""
+                + "ISA*00*          *00*          *ZZ*ReceiverID     *ZZ*Sender         *200711*0100*^*00501*000000001*0*T*:~"
+                + "GS*HC*99999999999*888888888888*20111219*1340*1*X*005010X222~"
+                + "ST*837*0001*005010X222~"
+                + "BHT*0019*00*565743*20110523*154959*CH~"
+                + "NM1*41*2*SAMPLE INC*****46*496103~"
+                + "PER*IC*EDI DEPT*EM*FEEDBACK@example.com*TE*3305551212~"
+                + "NM1*40*2*PPO BLUE*****46*54771~"
+                + "HL*1**20~"
+                + "SE*6*0001~"
+                + "GE*1*1~"
+                + "IEA*1*000000001~").getBytes());
+
+        EDIStreamReader reader = factory.createEDIStreamReader(stream);
+        List<EDIStreamValidationError> errors = new ArrayList<>();
+        List<EDIReference> nm1references = new ArrayList<>();
+
+        while (reader.hasNext()) {
+            switch (reader.next()) {
+            case START_TRANSACTION:
+                reader.setTransactionSchema(SchemaFactory.newFactory()
+                                                         .createSchema(getClass().getResource("/x12/005010X222/837_loop1000_only.xml")));
+                break;
+            case START_SEGMENT:
+                if ("NM1".equals(reader.getReferenceCode())) {
+                    nm1references.add(reader.getSchemaTypeReference());
+                }
+                break;
+            case SEGMENT_ERROR:
+            case ELEMENT_OCCURRENCE_ERROR:
+            case ELEMENT_DATA_ERROR:
+                errors.add(reader.getErrorType());
+                System.out.println("Unexpected error: " + reader.getErrorType() + "; " + reader.getText() + "; " + reader.getLocation());
+                break;
+            default:
+                break;
+            }
+        }
+
+        assertEquals(0, errors.size(), () -> errors.toString());
+        assertEquals(2, nm1references.size());
+        // Values set in 837_loop1000_only.xml
+        assertEquals("Submitter Name", nm1references.get(0).getTitle());
+        assertEquals("Receiver Name", nm1references.get(1).getTitle());
+
+        // Values of NM1 standard set in 837.xml
+        assertEquals("Individual or Organizational Name", nm1references.get(0).getReferencedType().getTitle());
+        assertEquals("Individual or Organizational Name", nm1references.get(1).getReferencedType().getTitle());
+    }
+
+}
diff --git a/src/test/resources/x12/005010/837.xml b/src/test/resources/x12/005010/837.xml
index 0de3a7a..828fdbe 100644
--- a/src/test/resources/x12/005010/837.xml
+++ b/src/test/resources/x12/005010/837.xml
@@ -696,7 +696,7 @@
   <elementType name="N407" base="string" maxLength="3"/>
   <elementType name="NM101" base="string" minLength="2" maxLength="3"/>
   <elementType name="NM102" base="string"/>
-  <elementType name="NM103" base="string" maxLength="60"/>
+  <elementType name="NM103" base="string" maxLength="60" title="Name Last or Organization Name"/>
   <elementType name="NM104" base="string" maxLength="35"/>
   <elementType name="NM105" base="string" maxLength="25"/>
   <elementType name="NM106" base="string" maxLength="10"/>
@@ -1887,11 +1887,11 @@
       <element type="N407"/>
     </sequence>
   </segmentType>
-  <segmentType name="NM1">
+  <segmentType name="NM1" title="Individual or Organizational Name">
     <sequence>
       <element type="NM101" minOccurs="1"/>
       <element type="NM102" minOccurs="1"/>
-      <element type="NM103"/>
+      <element type="NM103" title="Name Last or Organization Name"/>
       <element type="NM104"/>
       <element type="NM105"/>
       <element type="NM106"/>
diff --git a/src/test/resources/x12/005010X222/837_loop1000_only.xml b/src/test/resources/x12/005010X222/837_loop1000_only.xml
new file mode 100644
index 0000000..ca37178
--- /dev/null
+++ b/src/test/resources/x12/005010X222/837_loop1000_only.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<schema xmlns="http://xlate.io/EDISchema/v4">
+
+  <include schemaLocation="file:./target/test-classes/x12/005010/837.xml"/>
+
+  <implementation>
+    <sequence>
+      <segment type="BHT"/>
+
+      <loop code="1000A" type="L0001" discriminator="1">
+        <sequence>
+          <segment type="NM1" title="Submitter Name">
+            <sequence>
+              <element position="1">
+                <enumeration>
+                  <value>41</value>
+                </enumeration>
+              </element>
+              <element position="2"/>
+              <element position="3" title="Submitter Last or Organization Name"/>
+              <element position="4"/>
+              <element position="5"/>
+              <element position="8"/>
+              <element position="9"/>
+            </sequence>
+          </segment>
+          <segment type="PER"/>
+        </sequence>
+      </loop>
+
+      <loop code="1000B" type="L0001" discriminator="1">
+        <sequence>
+          <segment type="NM1" title="Receiver Name">
+            <sequence>
+              <element position="1">
+                <enumeration>
+                  <value>40</value>
+                </enumeration>
+              </element>
+              <element position="2"/>
+              <element position="3" title="Receiver Name"/>
+              <element position="8"/>
+              <element position="9"/>
+            </sequence>
+          </segment>
+        </sequence>
+      </loop>
+
+      <loop code="2000A" type="L0002">
+        <sequence>
+          <segment type="HL" />
+        </sequence>
+      </loop>
+    </sequence>
+  </implementation>
+</schema>