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 description:
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>