Repository: xlate/staedi (java)

Issue:
Implementation elements before segment selection not available
Need to -

1. Set implementation element and composite in validator when implementation segment is determined
2. Update elements and composites with implementations in pending event list when implementation segment is determined 

Pull request:
Update event array element types upon implementation segment selection
Fixes #87 

- Clean up composite/element retrieval from `Validator`
- Re-factor handling of derived composites to allow proper selection of element types
- Allow `getText` call on `EDIStreamReader` for `ELEMENT_OCCURRENCE_ERROR` event

Patch:
diff --git a/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamReader.java b/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamReader.java
index 4f9d294..efb0136 100644
--- a/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamReader.java
+++ b/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamReader.java
@@ -333,6 +333,7 @@ public class StaEDIStreamReader implements EDIStreamReader {
         case END_SEGMENT:
         case ELEMENT_DATA:
         case ELEMENT_DATA_ERROR:
+        case ELEMENT_OCCURRENCE_ERROR:
         case SEGMENT_ERROR:
             break;
         default:
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 5027ded..4b0cde1 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
@@ -259,21 +259,21 @@ public class ProxyEventHandler implements EventHandler {
     public boolean compositeBegin(boolean isNil) {
         EDIReference typeReference = null;
         boolean eventsReady = true;
+        Validator validator = validator();
 
-        if (validator() != null && !isNil) {
-            boolean invalid = !validator().validCompositeOccurrences(dialect, location);
+        if (validator != null && !isNil) {
+            boolean invalid = !validator.validCompositeOccurrences(dialect, location);
+            typeReference = validator.getCompositeReference();
 
             if (invalid) {
-                typeReference = validator().getElementReference();
-                List<UsageError> errors = validator().getElementErrors();
+                List<UsageError> errors = validator.getElementErrors();
 
                 for (UsageError error : errors) {
                     enqueueEvent(error.getError().getCategory(), error.getError(), "", error.getTypeReference(), location);
                 }
-            } else {
-                typeReference = validator().getCompositeReference();
             }
-            eventsReady = !validator().isPendingDiscrimination();
+
+            eventsReady = !validator.isPendingDiscrimination();
         }
 
         enqueueEvent(EDIStreamEvent.START_COMPOSITE, EDIStreamValidationError.NONE, "", typeReference, location);
@@ -299,19 +299,31 @@ public class ProxyEventHandler implements EventHandler {
         boolean derivedComposite;
         EDIReference typeReference;
         boolean eventsReady = true;
+        final boolean compositeFromStream = location.getComponentPosition() > -1;
 
         elementHolder.set(text, start, length);
         dialect.elementData(elementHolder, location);
         Validator validator = validator();
+        boolean valid;
 
         if (validator != null) {
-            derivedComposite = validateElement(validator);
+            valid = validator.validateElement(dialect, location, elementHolder);
+            derivedComposite = !compositeFromStream && validator.isComposite();
             typeReference = validator.getElementReference();
+            enqueueElementOccurrenceErrors(validator, valid);
         } else {
+            valid = true;
             derivedComposite = false;
             typeReference = null;
         }
 
+        if (derivedComposite && elementHolder.getText() != null/* Not an empty composite */) {
+            this.compositeBegin(elementHolder.length() == 0);
+            location.incrementComponentPosition();
+        }
+
+        enqueueElementErrors(validator, valid);
+
         if (text != null && (!derivedComposite || length > 0) /* Not an inferred element */) {
             enqueueEvent(EDIStreamEvent.ELEMENT_DATA,
                          EDIStreamValidationError.NONE,
@@ -332,56 +344,51 @@ public class ProxyEventHandler implements EventHandler {
         return eventsReady;
     }
 
-    boolean validateElement(Validator validator) {
-        final boolean composite = location.getComponentPosition() > -1;
-        boolean valid = validator.validateElement(dialect, location, elementHolder);
-        boolean derivedComposite = !composite && validator.isComposite();
-
-        if (!valid) {
-            /*
-             * Process element-level errors before possibly starting a
-             * composite or reporting other data-related errors.
-             */
-            List<UsageError> errors = validator.getElementErrors();
-            Iterator<UsageError> cursor = errors.iterator();
-
-            while (cursor.hasNext()) {
-                UsageError error = cursor.next();
-
-                switch (error.getError()) {
-                case TOO_MANY_DATA_ELEMENTS:
-                case TOO_MANY_REPETITIONS:
-                    enqueueEvent(error.getError().getCategory(),
-                                 error.getError(),
-                                 elementHolder,
-                                 error.getTypeReference(),
-                                 location);
-                    cursor.remove();
-                    //$FALL-THROUGH$
-                default:
-                    continue;
-                }
-            }
+    void enqueueElementOccurrenceErrors(Validator validator, boolean valid) {
+        if (valid) {
+            return;
         }
 
-        if (derivedComposite && elementHolder.getText() != null/* Not an empty composite */) {
-            this.compositeBegin(elementHolder.length() == 0);
-            location.incrementComponentPosition();
-        }
+        /*
+         * Process element-level errors before possibly starting a
+         * composite or reporting other data-related errors.
+         */
+        List<UsageError> errors = validator.getElementErrors();
+        Iterator<UsageError> cursor = errors.iterator();
 
-        if (!valid) {
-            List<UsageError> errors = validator.getElementErrors();
+        while (cursor.hasNext()) {
+            UsageError error = cursor.next();
 
-            for (UsageError error : errors) {
+            switch (error.getError()) {
+            case TOO_MANY_DATA_ELEMENTS:
+            case TOO_MANY_REPETITIONS:
                 enqueueEvent(error.getError().getCategory(),
                              error.getError(),
                              elementHolder,
                              error.getTypeReference(),
                              location);
+                cursor.remove();
+                //$FALL-THROUGH$
+            default:
+                continue;
             }
         }
+    }
+
+    void enqueueElementErrors(Validator validator, boolean valid) {
+        if (valid) {
+            return;
+        }
+
+        List<UsageError> errors = validator.getElementErrors();
 
-        return derivedComposite;
+        for (UsageError error : errors) {
+            enqueueEvent(error.getError().getCategory(),
+                         error.getError(),
+                         elementHolder,
+                         error.getTypeReference(),
+                         location);
+        }
     }
 
     public boolean isBinaryElementLength() {
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 1e7bde9..837bc3e 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
@@ -210,11 +210,17 @@ public class Validator {
     }
 
     public EDIReference getCompositeReference() {
+        final EDIReference reference;
+
         if (implSegmentSelected && implComposite != null) {
-            return implComposite.getLink();
+            reference = implComposite.getLink();
+        } else if (composite != null) {
+            reference = composite.getLink();
+        } else {
+            reference = null;
         }
 
-        return composite != null ? composite.getLink() : null;
+        return reference;
     }
 
     public boolean isBinaryElementLength() {
@@ -231,25 +237,17 @@ 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();
-        }
+        final EDIReference reference;
 
-        if (element != null) {
-            return element.getLink();
+        if (implSegmentSelected && implElement != null) {
+            reference = implElement.getLink();
+        } else if (element != null) {
+            reference = element.getLink();
+        } else {
+            reference = null;
         }
 
-        return null;
+        return reference;
     }
 
     private static EDIReference referenceOf(EDIComplexType type, int minOccurs, int maxOccurs) {
@@ -712,12 +710,15 @@ public class Validator {
                 handleImplementationSelected(candidate, implSeg, handler);
 
                 if (implNode.isFirstChild()) {
-                    //start of loop, update the loop and segment references that were already reported
-                    updateEventReferences(events, index, count - 1, implType, implSeg.getLink());
+                    //start of loop, update the loop, segment, and element references that were already reported
+                    updateEventReferences(events, index, count, implType, implSeg.getLink());
 
                     // Replace the standard loop with the implementation on the stack
                     loopStack.pop();
                     loopStack.push(implNode.getParent());
+                } else {
+                    //update segment and element references that were already reported
+                    updateEventReferences(events, index, count, null, implSeg.getLink());
                 }
 
                 return true;
@@ -733,6 +734,16 @@ public class Validator {
         implNode = implSeg;
         implSegmentSelected = true;
 
+        // TODO: Implementation nodes must be incremented and validated (#88)
+        if (this.isComposite()) {
+            this.implComposite = implSeg.getChild(this.composite.getIndex());
+            this.implElement = this.implComposite.getChild(this.element.getIndex());
+        } else if (this.element != null) {
+            // Set implementation when standard element is already set (e.g. via discriminator)
+            this.implComposite = null;
+            this.implElement = implSeg.getChild(this.element.getIndex());
+        }
+
         if (candidate.isNodeType(Type.LOOP)) {
             candidate.incrementUsage();
             candidate.resetChildren();
@@ -790,18 +801,49 @@ public class Validator {
      */
     static void updateEventReferences(StreamEvent[] events, int index, int count, EDIReference implType, EDIReference implSeg) {
         for (int i = index; i < count; i++) {
-            setReferenceWhenMatched(events[i], EDIStreamEvent.START_LOOP, implType);
-            setReferenceWhenMatched(events[i], EDIStreamEvent.START_SEGMENT, implSeg);
+            switch (events[i].getType()) {
+            case START_LOOP:
+                // Assuming implType is not null if we find a START_LOOP event
+                Objects.requireNonNull(implType, "Unexpected loop event during implementation segment selection");
+                updateReferenceWhenMatched(events[i], implType);
+                break;
+            case START_SEGMENT:
+                updateReferenceWhenMatched(events[i], implSeg);
+                break;
+            case START_COMPOSITE:
+            case END_COMPOSITE:
+            case ELEMENT_DATA:
+                // TODO: Implementation nodes must be incremented and validated (#88)
+                updateReference(events[i], (SegmentImplementation) implSeg);
+                break;
+            default:
+                break;
+            }
         }
     }
 
-    static void setReferenceWhenMatched(StreamEvent event, EDIStreamEvent type, EDIReference override) {
+    static void updateReferenceWhenMatched(StreamEvent event, 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())) {
+        if (Objects.equals(event.getReferenceCode(), override.getReferencedType().getCode())) {
             event.setTypeReference(override);
         }
     }
 
+    static void updateReference(StreamEvent event, SegmentImplementation override) {
+        final List<EDITypeImplementation> implElements = override.getSequence();
+        final Location location = event.getLocation();
+        final int elementIndex = location.getElementPosition() - 1;
+        final int componentIndex = location.getComponentPosition() - 1;
+        EDITypeImplementation element = implElements.get(elementIndex);
+
+        if (componentIndex > -1) {
+            CompositeImplementation composite = (CompositeImplementation) implElements.get(elementIndex);
+            element = composite.getSequence().get(componentIndex);
+        }
+
+        event.setTypeReference(element);
+    }
+
     /* ********************************************************************** */
 
     public List<UsageError> getElementErrors() {


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 8dd08c4..493b1e3 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
@@ -3,6 +3,7 @@ package io.xlate.edi.internal.schema.implementation;
 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 static org.junit.jupiter.api.Assertions.assertNull;
 
 import java.io.ByteArrayInputStream;
 import java.util.ArrayList;
@@ -203,7 +204,7 @@ class ElementImplTest {
                 + "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~"
+                + "HL*1**20*1~"
                 + "SE*6*0001~"
                 + "GE*1*1~"
                 + "IEA*1*000000001~").getBytes());
@@ -245,4 +246,116 @@ class ElementImplTest {
         assertEquals("Name Last or Organization Name", nm103references.get(0).getReferencedType().getTitle());
         assertEquals("Name Last or Organization Name", nm103references.get(1).getReferencedType().getTitle());
     }
+
+    @Test
+    void testElementTypeDescriptionAccessOnDescriminator() 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*1~"
+                + "SE*6*0001~"
+                + "GE*1*1~"
+                + "IEA*1*000000001~").getBytes());
+
+        EDIStreamReader reader = factory.createEDIStreamReader(stream);
+        List<EDIStreamValidationError> errors = new ArrayList<>();
+        List<EDIReference> nm101references = 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 ("NM101".equals(reader.getReferenceCode())) {
+                    nm101references.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, nm101references.size());
+        assertEquals("Entity Identifier Code (Submitter)", nm101references.get(0).getTitle());
+        assertEquals("Entity Identifier Code (Receiver)", nm101references.get(1).getTitle());
+
+        // Values of NM101 standard set in 837.xml
+        assertEquals("Entity Identifier Code", nm101references.get(0).getReferencedType().getTitle());
+        assertEquals("Entity Identifier Code", nm101references.get(1).getReferencedType().getTitle());
+    }
+
+    @Test
+    void testElementTypeDescriptionAccessPriorToDescriminator() 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*1~"
+                + "SE*6*0001~"
+                + "GE*1*1~"
+                + "IEA*1*000000001~").getBytes());
+
+        EDIStreamReader reader = factory.createEDIStreamReader(stream);
+        List<EDIStreamValidationError> errors = new ArrayList<>();
+        List<EDIReference> hlReferences = 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 ("HL".equals(reader.getLocation().getSegmentTag())) {
+                    hlReferences.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, except for offset `1`
+        assertEquals(4, hlReferences.size());
+        assertEquals("Hierarchical ID Number (20)", hlReferences.get(0).getTitle());
+        assertNull(hlReferences.get(1).getTitle());
+        assertEquals("Hierarchical Level Code (20)", hlReferences.get(2).getTitle());
+        assertEquals("Hierarchical Child Code (20)", hlReferences.get(3).getTitle());
+
+        // Values of NM101 standard set in 837.xml
+        assertEquals("Hierarchical ID Number", hlReferences.get(0).getReferencedType().getTitle());
+        assertEquals("Hierarchical Parent ID Number", hlReferences.get(1).getReferencedType().getTitle());
+        assertEquals("Hierarchical Level Code", hlReferences.get(2).getReferencedType().getTitle());
+        assertEquals("Hierarchical Child Code", hlReferences.get(3).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
index c26e88d..fad81f0 100644
--- a/src/test/java/io/xlate/edi/internal/schema/implementation/SegmentImplTest.java
+++ b/src/test/java/io/xlate/edi/internal/schema/implementation/SegmentImplTest.java
@@ -29,7 +29,7 @@ class SegmentImplTest {
                 + "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~"
+                + "HL*1**20*1~"
                 + "SE*6*0001~"
                 + "GE*1*1~"
                 + "IEA*1*000000001~").getBytes());
diff --git a/src/test/java/io/xlate/edi/internal/stream/EventSequenceTest.java b/src/test/java/io/xlate/edi/internal/stream/EventSequenceTest.java
index 11631b4..512a4ed 100644
--- a/src/test/java/io/xlate/edi/internal/stream/EventSequenceTest.java
+++ b/src/test/java/io/xlate/edi/internal/stream/EventSequenceTest.java
@@ -22,10 +22,13 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
 
 import org.junit.jupiter.api.Test;
 
 import io.xlate.edi.internal.schema.SchemaUtils;
+import io.xlate.edi.schema.EDIReference;
 import io.xlate.edi.schema.EDISchemaException;
 import io.xlate.edi.schema.Schema;
 import io.xlate.edi.schema.SchemaFactory;
@@ -457,12 +460,14 @@ class EventSequenceTest {
         // GE02 - 2nd occurrence
         assertEquals(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR, reader.next());
         assertEquals(EDIStreamValidationError.TOO_MANY_REPETITIONS, reader.getErrorType());
+        assertEquals("0001", reader.getText());
         assertEquals(EDIStreamEvent.ELEMENT_DATA, reader.next());
         assertEquals("0001", reader.getText());
 
         // GE02 - 3rd occurrence plus data error
         assertEquals(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR, reader.next());
         assertEquals("28", reader.getReferenceCode());
+        assertEquals("AGAIN!", reader.getText()); // data association with error
         assertEquals(EDIStreamValidationError.TOO_MANY_REPETITIONS, reader.getErrorType());
         assertEquals(EDIStreamEvent.ELEMENT_DATA_ERROR, reader.next());
         assertEquals(EDIStreamValidationError.INVALID_CHARACTER_DATA, reader.getErrorType());
@@ -474,6 +479,7 @@ class EventSequenceTest {
         // GE03 too many elements
         assertEquals(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR, reader.next());
         assertEquals(EDIStreamValidationError.TOO_MANY_DATA_ELEMENTS, reader.getErrorType());
+        assertEquals("", reader.getText()); // data association with error
 
         //assertEquals(EDIStreamEvent.END_GROUP, reader.nextTag());
 
@@ -560,4 +566,77 @@ class EventSequenceTest {
 
         return schemaFactory.createSchema(schemaLocation);
     }
+
+    @Test
+    void testElementReturnedOnDerivedComposite() throws EDISchemaException, EDIStreamException {
+        SchemaFactory schemaFactory = SchemaFactory.newFactory();
+        Schema schema = schemaFactory.createSchema(new ByteArrayInputStream((""
+                + "<schema xmlns='http://xlate.io/EDISchema/v4'>"
+                + "<transaction>"
+                + "  <sequence>"
+                + "    <segment type='SG1'/>"
+                + "  </sequence>"
+                + "</transaction>"
+                + ""
+                + "<elementType name=\"E1\" base=\"string\" maxLength=\"5\" />"
+                + ""
+                + "<compositeType name=\"C1\">"
+                + "  <sequence>"
+                + "    <element type='E1'/>"
+                + "    <element type='E1'/>"
+                + "  </sequence>"
+                + "</compositeType>"
+                + ""
+                + "<segmentType name=\"SG1\">"
+                + "  <sequence>"
+                + "    <element type='E1'/>"
+                + "    <composite type='C1'/>"
+                + "    <element type='E1'/>"
+                + "  </sequence>"
+                + "</segmentType>"
+                + "</schema>").getBytes()));
+
+        InputStream stream = new ByteArrayInputStream((""
+                + "ISA*01*0000000000*01*0000000000*ZZ*ABCDEFGHIJKLMNO*ZZ*123456789012345*101127*1719*`*00402*000003438*0*P*>\n" +
+                "GS*HC*99999999999*888888888888*20111219*1340*1377*X*005010\n" +
+                "ST*999*0001\n" +
+                "SG1*11111*22222`22222*33333\n" +
+                "SE*3*0001\n" +
+                "GE*1*1377\n" +
+                "IEA*1*000003438\n" +
+                "").getBytes());
+
+        EDIInputFactory factory = EDIInputFactory.newFactory();
+        EDIStreamReader reader = factory.createEDIStreamReader(stream);
+        List<EDIReference> c1refs = new ArrayList<>();
+        List<EDIReference> e1refs = new ArrayList<>();
+
+        while (reader.hasNext()) {
+            switch (reader.next()) {
+            case START_TRANSACTION:
+                reader.setTransactionSchema(schema);
+                break;
+            case START_COMPOSITE:
+                if (reader.getLocation().getSegmentTag().equals("SG1") && reader.getLocation().getElementPosition() == 2) {
+                    c1refs.add(reader.getSchemaTypeReference());
+                }
+                break;
+            case ELEMENT_DATA:
+                if (reader.getLocation().getSegmentTag().equals("SG1") && reader.getLocation().getElementPosition() == 2) {
+                    e1refs.add(reader.getSchemaTypeReference());
+                }
+                break;
+            default:
+                break;
+            }
+        }
+
+        assertEquals(2, c1refs.size());
+        assertEquals("C1", c1refs.get(0).getReferencedType().getId());
+        assertEquals("C1", c1refs.get(1).getReferencedType().getId());
+
+        assertEquals(2, e1refs.size());
+        assertEquals("E1", e1refs.get(0).getReferencedType().getId());
+        assertEquals("E1", e1refs.get(1).getReferencedType().getId());
+    }
 }
diff --git a/src/test/resources/x12/005010/837.xml b/src/test/resources/x12/005010/837.xml
index 828fdbe..c522f2d 100644
--- a/src/test/resources/x12/005010/837.xml
+++ b/src/test/resources/x12/005010/837.xml
@@ -555,10 +555,10 @@
   <elementType name="HI1207" base="string" maxLength="30"/>
   <elementType name="HI1208" base="string" maxLength="30"/>
   <elementType name="HI1209" base="string"/>
-  <elementType name="HL01" base="string" maxLength="12"/>
-  <elementType name="HL02" base="string" maxLength="12"/>
-  <elementType name="HL03" base="string" maxLength="2"/>
-  <elementType name="HL04" base="string"/>
+  <elementType name="HL01" base="string" maxLength="12" title="Hierarchical ID Number"/>
+  <elementType name="HL02" base="string" maxLength="12" title="Hierarchical Parent ID Number"/>
+  <elementType name="HL03" base="string" maxLength="2" title="Hierarchical Level Code"/>
+  <elementType name="HL04" base="string" title="Hierarchical Child Code"/>
   <elementType name="HSD01" base="string" minLength="2" maxLength="2"/>
   <elementType name="HSD02" base="decimal" maxLength="15"/>
   <elementType name="HSD03" base="string" minLength="2" maxLength="2"/>
@@ -694,7 +694,7 @@
   <elementType name="N405" base="string" maxLength="2"/>
   <elementType name="N406" base="string" maxLength="30"/>
   <elementType name="N407" base="string" maxLength="3"/>
-  <elementType name="NM101" base="string" minLength="2" maxLength="3"/>
+  <elementType name="NM101" base="string" minLength="2" maxLength="3" title="Entity Identifier Code"/>
   <elementType name="NM102" base="string"/>
   <elementType name="NM103" base="string" maxLength="60" title="Name Last or Organization Name"/>
   <elementType name="NM104" base="string" maxLength="35"/>
diff --git a/src/test/resources/x12/005010X222/837_loop1000_only.xml b/src/test/resources/x12/005010X222/837_loop1000_only.xml
index ca37178..cd2b74c 100644
--- a/src/test/resources/x12/005010X222/837_loop1000_only.xml
+++ b/src/test/resources/x12/005010X222/837_loop1000_only.xml
@@ -11,7 +11,7 @@
         <sequence>
           <segment type="NM1" title="Submitter Name">
             <sequence>
-              <element position="1">
+              <element position="1" title="Entity Identifier Code (Submitter)">
                 <enumeration>
                   <value>41</value>
                 </enumeration>
@@ -32,7 +32,7 @@
         <sequence>
           <segment type="NM1" title="Receiver Name">
             <sequence>
-              <element position="1">
+              <element position="1" title="Entity Identifier Code (Receiver)">
                 <enumeration>
                   <value>40</value>
                 </enumeration>
@@ -46,9 +46,23 @@
         </sequence>
       </loop>
 
-      <loop code="2000A" type="L0002">
+      <loop code="2000A" type="L0002" discriminator="3" title="Billing Provider Level (2000A)" >
         <sequence>
-          <segment type="HL" />
+          <segment type="HL" title="Billing Provider Level (HL)">
+            <sequence>
+              <element position="1" title="Hierarchical ID Number (20)" />
+              <element position="3" title="Hierarchical Level Code (20)">
+                <enumeration>
+                  <value title="Information Source">20</value>
+                </enumeration>
+              </element>
+              <element position="4" minOccurs="1" title="Hierarchical Child Code (20)">
+                <enumeration>
+                  <value>1</value>
+                </enumeration>
+              </element>
+            </sequence>
+          </segment>
         </sequence>
       </loop>
     </sequence>