diff --git a/src/main/java/io/xlate/edi/internal/schema/SchemaReaderV3.java b/src/main/java/io/xlate/edi/internal/schema/SchemaReaderV3.java index 3f1858d2..9f98d366 100644 --- a/src/main/java/io/xlate/edi/internal/schema/SchemaReaderV3.java +++ b/src/main/java/io/xlate/edi/internal/schema/SchemaReaderV3.java @@ -119,8 +119,8 @@ void setReferences(Map types) { super.setReferences(types); StreamSupport.stream(Spliterators.spliteratorUnknownSize(implementedTypes.descendingIterator(), - Spliterator.ORDERED), - false) + Spliterator.ORDERED), + false) .filter(type -> type.getType() != Type.ELEMENT) .map(BaseComplexImpl.class::cast) .forEach(type -> setReferences(type, types)); @@ -191,6 +191,10 @@ EDITypeImplementation getDefaultImplementation(EDIReference standardReference, i return new ElementImpl(standardReference, position); case COMPOSITE: return new CompositeImpl(standardReference, position, getDefaultSequence(((EDIComplexType) std).getReferences())); + case SEGMENT: + return new SegmentImpl(standardReference, getDefaultSequence(((EDIComplexType) std).getReferences())); + case LOOP: + return new LoopImpl(standardReference, getDefaultSequence(((EDIComplexType) std).getReferences())); default: throw schemaException("Implementation of " + std.getId() + " must not be empty"); } @@ -213,36 +217,37 @@ LoopImplementation readLoopImplementation(XMLStreamReader reader, QName complexT List sequence = new ArrayList<>(); String id; String typeId; - int minOccurs = 0; - int maxOccurs = 0; - EDIElementPosition discriminatorPos = null; - String title = null; + int minOccurs; + int maxOccurs; + EDIElementPosition discriminatorPos; + String title; if (transactionLoop) { id = StaEDISchema.IMPLEMENTATION_ID; typeId = null; + minOccurs = 0; + maxOccurs = 0; + discriminatorPos = null; + title = null; } else { id = parseAttribute(reader, "code", String::valueOf); typeId = parseAttribute(reader, "type", String::valueOf); minOccurs = parseAttribute(reader, ATTR_MIN_OCCURS, Integer::parseInt, -1); maxOccurs = parseAttribute(reader, ATTR_MAX_OCCURS, Integer::parseInt, -1); - discriminatorPos = parseElementPosition(reader, ATTR_DISCRIMINATOR); + discriminatorPos = parseElementPosition(reader, ATTR_DISCRIMINATOR); title = parseAttribute(reader, ATTR_TITLE, String::valueOf, null); } - String descr = readDescription(reader); - readSequence(reader, e -> readLoopSequenceEntry(e, sequence)); - nextTag(reader, "reading to end of " + complexType); - requireElement(complexType, reader); - - Discriminator disc = null; - - if (discriminatorPos != null) { - SegmentImpl segImpl = (SegmentImpl) sequence.get(0); - disc = buildDiscriminator(discriminatorPos, segImpl.getSequence()); - } - - return new LoopImpl(minOccurs, maxOccurs, id, typeId, disc, sequence, title, descr); + return readTypeImplementation(reader, + () -> readSequence(reader, e -> readLoopSequenceEntry(e, sequence)), + descr -> whenExpected(reader, complexType, () -> { + Discriminator disc = null; + if (discriminatorPos != null) { + SegmentImpl segImpl = (SegmentImpl) sequence.get(0); + disc = buildDiscriminator(discriminatorPos, segImpl.getSequence()); + } + return new LoopImpl(minOccurs, maxOccurs, id, typeId, disc, sequence, title, descr); + })); } void readLoopSequenceEntry(QName entryName, List sequence) { @@ -270,20 +275,20 @@ SegmentImpl readSegmentImplementation() { String title = parseAttribute(reader, ATTR_TITLE, String::valueOf, null); return readTypeImplementation(reader, - () -> readSequence(reader, e -> readPositionedSequenceEntry(e, sequence, true)), - descr -> whenExpected(reader, qnSegment, () -> { - Discriminator disc = buildDiscriminator(discriminatorPos, sequence); - SegmentImpl segment = new SegmentImpl(minOccurs, - maxOccurs, - typeId, - code, - disc, - sequence, - title, - descr); - implementedTypes.add(segment); - return segment; - })); + () -> readSequence(reader, e -> readPositionedSequenceEntry(e, sequence, true)), + descr -> whenExpected(reader, qnSegment, () -> { + Discriminator disc = buildDiscriminator(discriminatorPos, sequence); + SegmentImpl segment = new SegmentImpl(minOccurs, + maxOccurs, + typeId, + code, + disc, + sequence, + title, + descr); + implementedTypes.add(segment); + return segment; + })); } void readPositionedSequenceEntry(QName entryName, List sequence, boolean composites) { @@ -345,7 +350,10 @@ Discriminator buildDiscriminator(EDIElementPosition discriminatorPos, return disc; } - EDITypeImplementation getDiscriminatorElement(EDIElementPosition discriminatorPos, int position, List sequence, String type) { + EDITypeImplementation getDiscriminatorElement(EDIElementPosition discriminatorPos, + int position, + List sequence, + String type) { if (position > 0 && position <= sequence.size()) { return sequence.get(position - 1); } else { @@ -361,16 +369,16 @@ CompositeImpl readCompositeImplementation(XMLStreamReader reader) { String title = parseAttribute(reader, ATTR_TITLE, String::valueOf, null); return readTypeImplementation(reader, - () -> readSequence(reader, e -> readPositionedSequenceEntry(e, sequence, false)), - descr -> whenExpected(reader, - qnComposite, - () -> new CompositeImpl(minOccurs, - maxOccurs, - null, - position, - sequence, - title, - descr))); + () -> readSequence(reader, e -> readPositionedSequenceEntry(e, sequence, false)), + descr -> whenExpected(reader, + qnComposite, + () -> new CompositeImpl(minOccurs, + maxOccurs, + null, + position, + sequence, + title, + descr))); } void readSequence(XMLStreamReader reader, Consumer startHandler) { @@ -391,16 +399,16 @@ ElementImpl readElementImplementation(XMLStreamReader reader) { String title = parseAttribute(reader, ATTR_TITLE, String::valueOf, null); return readTypeImplementation(reader, - () -> valueSet.set(super.readEnumerationValues(reader)), - descr -> whenExpected(reader, - qnElement, - () -> new ElementImpl(minOccurs, - maxOccurs, - (String) null, - position, - valueSet.get(), - title, - descr))); + () -> valueSet.set(super.readEnumerationValues(reader)), + descr -> whenExpected(reader, + qnElement, + () -> new ElementImpl(minOccurs, + maxOccurs, + (String) null, + position, + valueSet.get(), + title, + descr))); } T whenExpected(XMLStreamReader reader, QName expected, Supplier supplier) { diff --git a/src/main/java/io/xlate/edi/internal/schema/implementation/CompositeImpl.java b/src/main/java/io/xlate/edi/internal/schema/implementation/CompositeImpl.java index 074c27dc..d89f4d7b 100644 --- a/src/main/java/io/xlate/edi/internal/schema/implementation/CompositeImpl.java +++ b/src/main/java/io/xlate/edi/internal/schema/implementation/CompositeImpl.java @@ -30,7 +30,6 @@ public CompositeImpl(int minOccurs, public CompositeImpl(EDIReference standardReference, int position, List sequence) { super(sequence, null, null); this.setStandardReference(standardReference); - super.typeId = standard.getId(); this.position = position; } diff --git a/src/main/java/io/xlate/edi/internal/schema/implementation/LoopImpl.java b/src/main/java/io/xlate/edi/internal/schema/implementation/LoopImpl.java index 6699c79b..b694c4d6 100644 --- a/src/main/java/io/xlate/edi/internal/schema/implementation/LoopImpl.java +++ b/src/main/java/io/xlate/edi/internal/schema/implementation/LoopImpl.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.Objects; +import io.xlate.edi.schema.EDIReference; import io.xlate.edi.schema.implementation.Discriminator; import io.xlate.edi.schema.implementation.EDITypeImplementation; import io.xlate.edi.schema.implementation.LoopImplementation; @@ -30,6 +31,13 @@ public LoopImpl(int minOccurs, this.discriminator = discriminator; } + public LoopImpl(EDIReference standardReference, List sequence) { + super(sequence, null, null); + this.setStandardReference(standardReference); + this.code = standard.getCode(); + this.discriminator = null; + } + @Override public boolean equals(Object o) { return super.equals(o) && diff --git a/src/main/java/io/xlate/edi/internal/schema/implementation/SegmentImpl.java b/src/main/java/io/xlate/edi/internal/schema/implementation/SegmentImpl.java index d9e61e1a..870f9af5 100644 --- a/src/main/java/io/xlate/edi/internal/schema/implementation/SegmentImpl.java +++ b/src/main/java/io/xlate/edi/internal/schema/implementation/SegmentImpl.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.Objects; +import io.xlate.edi.schema.EDIReference; import io.xlate.edi.schema.implementation.Discriminator; import io.xlate.edi.schema.implementation.EDITypeImplementation; import io.xlate.edi.schema.implementation.SegmentImplementation; @@ -30,6 +31,13 @@ public SegmentImpl(int minOccurs, this.discriminator = discriminator; } + public SegmentImpl(EDIReference standardReference, List sequence) { + super(sequence, null, null); + this.setStandardReference(standardReference); + this.code = standard.getCode(); + this.discriminator = null; + } + @Override public boolean equals(Object o) { return super.equals(o) && 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 4d47b4ea..fc42dea1 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 @@ -295,6 +295,7 @@ public boolean segmentEnd() { EDIReference typeReference = null; if (validator != null) { + validator.clearImplementationCandidates(this); validator.validateSyntax(dialect, this, this, location, false); validator.validateVersionConstraints(dialect, this, null); typeReference = validator.getSegmentReference(); 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 b05a4249..8609eccd 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 @@ -147,7 +147,6 @@ static class RevalidationNode { final StaEDIStreamLocation location; public RevalidationNode(UsageNode standard, UsageNode impl, CharSequence data, StaEDIStreamLocation location) { - super(); this.standard = standard; this.impl = impl; this.data = CharBuffer.allocate(data.length()); @@ -155,6 +154,14 @@ public RevalidationNode(UsageNode standard, UsageNode impl, CharSequence data, S this.data.flip(); this.location = location.copy(); } + + public RevalidationNode(UsageNode standard, UsageNode impl, StaEDIStreamLocation location) { + this.standard = standard; + this.impl = impl; + this.data = null; + this.location = location.copy(); + } + } public static Validator forSchema(Schema schema, Schema containerSchema, boolean validateCodeValues, boolean formatElements) { @@ -525,7 +532,7 @@ boolean handleSegment(CharSequence tag, UsageNode current, UsageNode currentImpl handler.segmentError(current.getId(), current.getLink(), IMPLEMENTATION_UNUSED_SEGMENT_PRESENT); // Save the currentImpl so that the search is resumed from the correct location implNode = currentImpl; - } else if (implSegmentCandidates.size() == 1) { + } else if (isSingleSegmentWithoutDescriminator(implSegmentCandidates)) { currentImpl.incrementUsage(); currentImpl.resetChildren(); @@ -542,6 +549,15 @@ boolean handleSegment(CharSequence tag, UsageNode current, UsageNode currentImpl return true; } + boolean isSingleSegmentWithoutDescriminator(List candidates) { + if (candidates.size() != 1) { + return false; + } + + PolymorphicImplementation candidate = (PolymorphicImplementation) candidates.get(0).getLink(); + return candidate.getDiscriminator() == null; + } + static UsageNode toSegment(UsageNode node) { UsageNode segmentNode; @@ -747,6 +763,13 @@ private void handleMissingMandatory(ValidationEventHandler handler, int depth) { } } + public void clearImplementationCandidates(ValidationEventHandler handler) { + if (!implSegmentCandidates.isEmpty()) { + handler.segmentError(segment.getId(), segment.getLink(), IMPLEMENTATION_UNUSED_SEGMENT_PRESENT); + implSegmentCandidates.clear(); + } + } + public boolean selectImplementation(Deque eventQueue, ValidationEventHandler handler) { StreamEvent currentEvent = eventQueue.getLast(); @@ -839,7 +862,7 @@ void checkPreviousSiblings(UsageNode implSeg, ValidationEventHandler handler) { if (std.isUsed()) { validateImplUnusedElementBlank(std, impl, true); } else { - validateDataElementRequirement(null, std, impl, entry.data, entry.location); + validateDataElementRequirement(null, std, impl, entry.location); } handleRevalidatedElementErrors(entry, elementErrors, handler); @@ -1060,7 +1083,7 @@ public boolean validateElement(Dialect dialect, StaEDIStreamLocation position, C if (valueReceived) { validateElementValue(dialect, position, value, formattedValue); } else { - validateDataElementRequirement(version, this.element, this.implElement, value, position); + validateDataElementRequirement(version, this.element, this.implElement, position); } return elementErrors.isEmpty(); @@ -1121,7 +1144,11 @@ void validateElementValue(Dialect dialect, StaEDIStreamLocation position, CharSe public void validateVersionConstraints(Dialect dialect, ValidationEventHandler validationHandler, StringBuilder formattedValue) { for (RevalidationNode entry : revalidationQueue) { - validateElementValue(dialect, entry.standard, entry.impl, entry.data, formattedValue); + if (entry.data != null) { + validateElementValue(dialect, entry.standard, entry.impl, entry.data, formattedValue); + } else { + validateDataElementRequirement(dialect.getTransactionVersionString(), entry.standard, entry.impl, entry.location); + } handleRevalidatedElementErrors(entry, elementErrors, validationHandler); } @@ -1257,12 +1284,12 @@ boolean validateImplUnusedElementBlank(UsageNode node, UsageNode implNode, boole return true; } - void validateDataElementRequirement(String version, UsageNode element, UsageNode implElement, CharSequence value, StaEDIStreamLocation position) { + void validateDataElementRequirement(String version, UsageNode element, UsageNode implElement, StaEDIStreamLocation position) { if (!UsageNode.hasMinimumUsage(version, element) || !UsageNode.hasMinimumUsage(version, implElement)) { elementErrors.add(new UsageError(element, REQUIRED_DATA_ELEMENT_MISSING)); } else if (isPendingDiscrimination()) { // This element requirement can not be validated until the correct implementation is determined - revalidationQueue.add(new RevalidationNode(this.element, this.implElement, value, position)); + revalidationQueue.add(new RevalidationNode(this.element, this.implElement, position)); } } diff --git a/src/main/resources/schema/EDISchema-v3.xsd b/src/main/resources/schema/EDISchema-v3.xsd index f5f07d4b..013cce6c 100644 --- a/src/main/resources/schema/EDISchema-v3.xsd +++ b/src/main/resources/schema/EDISchema-v3.xsd @@ -329,7 +329,7 @@ - + The ordered list of segments and sub-loops contained in this diff --git a/src/main/resources/schema/EDISchema-v4.xsd b/src/main/resources/schema/EDISchema-v4.xsd index 45ec868c..e58ba9b7 100644 --- a/src/main/resources/schema/EDISchema-v4.xsd +++ b/src/main/resources/schema/EDISchema-v4.xsd @@ -471,7 +471,7 @@ - + The ordered list of segments and sub-loops contained in this diff --git a/src/test/java/io/xlate/edi/internal/schema/StaEDISchemaFactoryTest.java b/src/test/java/io/xlate/edi/internal/schema/StaEDISchemaFactoryTest.java index 7ff358e5..6bfc31f4 100644 --- a/src/test/java/io/xlate/edi/internal/schema/StaEDISchemaFactoryTest.java +++ b/src/test/java/io/xlate/edi/internal/schema/StaEDISchemaFactoryTest.java @@ -30,6 +30,7 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -48,6 +49,7 @@ import io.xlate.edi.schema.EDIType; import io.xlate.edi.schema.Schema; import io.xlate.edi.schema.SchemaFactory; +import io.xlate.edi.schema.implementation.LoopImplementation; import io.xlate.edi.stream.EDIStreamConstants.Standards; @SuppressWarnings("resource") @@ -651,4 +653,30 @@ void testInvalidElementTypeBase() throws EDISchemaException { assertTrue(thrown.getCause().getCause() instanceof IllegalArgumentException); } + @Test + void testEmptyLoopImplementationCopiesStandard() throws EDISchemaException { + SchemaFactory factory = SchemaFactory.newFactory(); + InputStream stream = new ByteArrayInputStream(("" + + "" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + "").getBytes()); + + Schema schema = factory.createSchema(stream); + for (String segment : Arrays.asList("AK2", "AK3", "AK4", "AK5")) { + assertTrue(schema.containsSegment(segment)); + } + LoopImplementation impl = schema.getImplementation(); + LoopImplementation loop2000 = (LoopImplementation) impl.getSequence().get(1); + assertEquals("AK2", loop2000.getSequence().get(0).getCode()); + assertEquals("2100", loop2000.getSequence().get(1).getCode()); + assertEquals("AK5", loop2000.getSequence().get(2).getCode()); + } + } diff --git a/src/test/java/io/xlate/edi/internal/stream/SegmentValidationTest.java b/src/test/java/io/xlate/edi/internal/stream/SegmentValidationTest.java index 587f8360..dacd09ed 100644 --- a/src/test/java/io/xlate/edi/internal/stream/SegmentValidationTest.java +++ b/src/test/java/io/xlate/edi/internal/stream/SegmentValidationTest.java @@ -692,4 +692,39 @@ void testImplementation_Only_BHT_HL_Valid() throws EDISchemaException, EDIStream assertEquals(expected, events); } + + @Test + void testImplUnusedSegmentErrorWhenNotMatched() throws EDISchemaException, EDIStreamException { + EDIInputFactory factory = EDIInputFactory.newFactory(); + SchemaFactory schemaFactory = SchemaFactory.newFactory(); + InputStream stream = getClass().getResourceAsStream("/x12/issue229/837-header-ref-only.edi"); + + EDIStreamReader reader = factory.createEDIStreamReader(stream); + reader = StaEDITestUtil.filterEvents( + factory, + reader, + EDIStreamEvent.START_TRANSACTION, + EDIStreamEvent.SEGMENT_ERROR, + EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR, + EDIStreamEvent.START_LOOP, + EDIStreamEvent.END_LOOP); + + assertEvent(reader, EDIStreamEvent.START_TRANSACTION); + reader.setTransactionSchema(schemaFactory.createSchema(getClass().getResource("/x12/005010X222/837_REF_impls.xml"))); + + List expected = Arrays.asList( + StaEDITestEvent.forEvent(EDIStreamEvent.START_TRANSACTION, "TRANSACTION", "TRANSACTION"), + StaEDITestEvent.forError(EDIStreamValidationError.REQUIRED_DATA_ELEMENT_MISSING, "", "REF0402"), + StaEDITestEvent.forError(EDIStreamValidationError.IMPLEMENTATION_UNUSED_SEGMENT_PRESENT, "REF", "REF"), + StaEDITestEvent.forError(EDIStreamValidationError.MANDATORY_SEGMENT_MISSING, "HL", "HL")); + + List events = new ArrayList<>(); + events.add(StaEDITestEvent.from(reader, false)); + + while (reader.hasNext()) { + events.add(StaEDITestEvent.from(reader, false)); + } + + assertEquals(expected, events); + } } diff --git a/src/test/java/io/xlate/edi/test/StaEDITestUtil.java b/src/test/java/io/xlate/edi/test/StaEDITestUtil.java index f4762249..fd8a95d2 100644 --- a/src/test/java/io/xlate/edi/test/StaEDITestUtil.java +++ b/src/test/java/io/xlate/edi/test/StaEDITestUtil.java @@ -2,10 +2,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.InputStream; +import java.net.URL; import java.util.Arrays; import java.util.HashSet; import java.util.Set; +import io.xlate.edi.schema.Schema; +import io.xlate.edi.schema.SchemaFactory; import io.xlate.edi.stream.EDIInputFactory; import io.xlate.edi.stream.EDIStreamEvent; import io.xlate.edi.stream.EDIStreamException; @@ -113,4 +117,145 @@ public static String toString(EDIStreamReader reader) { return buffer.append(')').toString(); } + + public static void printTransaction(String resourcePath, String schemaPath) throws Exception { + EDIInputFactory factory = EDIInputFactory.newFactory(); + + // Any InputStream can be used to create an `EDIStreamReader` + try (InputStream stream = StaEDITestUtil.class.getResourceAsStream(resourcePath); + EDIStreamReader reader = factory.createEDIStreamReader(stream)) { + EDIStreamEvent event; + boolean transactionBeginSegment = false; + String comp = null; + int depth = 0; + StringBuilder buffer = new StringBuilder(); + + while (reader.hasNext()) { + event = reader.next(); + + switch (event) { + case START_INTERCHANGE: + System.out.println(repeat(' ', depth) + ""); + depth++; + break; + + case END_INTERCHANGE: + depth--; + System.out.println(repeat(' ', depth) + ""); + break; + + case START_GROUP: + System.out.println(repeat(' ', depth) + ""); + depth++; + break; + + case END_GROUP: + depth--; + System.out.println(repeat(' ', depth) + ""); + break; + + case START_TRANSACTION: + transactionBeginSegment = true; + System.out.println(repeat(' ', depth) + ""); + depth++; + break; + + case END_TRANSACTION: + depth--; + System.out.println(repeat(' ', depth) + ""); + break; + + case START_LOOP: + System.out.println(repeat(' ', depth) + "<" + reader.getReferenceCode() + ">"); + depth++; + break; + case END_LOOP: + depth--; + System.out.println(repeat(' ', depth) + ""); + break; + + case START_SEGMENT: + buffer.setLength(0); + buffer.append(repeat(' ', depth)); + buffer.append(""); + + System.out.println(buffer.toString()); + depth++; + break; + + case END_SEGMENT: + if (transactionBeginSegment) { + SchemaFactory schemaFactory = SchemaFactory.newFactory(); + URL schemaUrl = StaEDITestUtil.class.getResource(schemaPath); + //schemaFactory.setProperty(SchemaFactory.SCHEMA_LOCATION_URL_CONTEXT, schemaUrl); + Schema schema = schemaFactory.createSchema(schemaUrl); + reader.setTransactionSchema(schema); + } + transactionBeginSegment = false; + depth--; + System.out.println(repeat(' ', depth) + ""); + break; + + case START_COMPOSITE: + System.out.println(repeat(' ', depth) + "<" + reader.getReferenceCode() + ">"); + comp = reader.getReferenceCode(); + depth++; + break; + case END_COMPOSITE: + depth--; + System.out.println(repeat(' ', depth) + ""); + comp = null; + break; + + case ELEMENT_DATA: + String name = reader.getReferenceCode(); + if (name != null && !name.contains("Element")) { + name = "Element-" + name; + } + if (null != reader.getText() && !"".equals(reader.getText())) { + System.out.println(repeat(' ', depth) + "<" + name + ">" + reader.getText() + ""); + if ("null".equals(reader.getReferenceCode())) { + System.out.println(repeat(' ', depth) + "<" + name + ">" + reader.getText() + ""); + } + } + + break; + + case SEGMENT_ERROR: + // Handle a segment error + EDIStreamValidationError segmentErrorType = reader.getErrorType(); + System.out.println( + "*" + repeat(' ', depth) + "** Segment error: " + segmentErrorType.name() + " " + reader.getLocation()); + break; + + case ELEMENT_OCCURRENCE_ERROR: + case ELEMENT_DATA_ERROR: + // Handle a segment error + EDIStreamValidationError elementErrorType = reader.getErrorType(); + System.out.println( + "*" + repeat(' ', depth) + "** Element error:" + elementErrorType.name() + ", " + reader.getReferenceCode()); + break; + + default: + break; + } + } + } + } + + static String repeat(char value, int times) { + StringBuilder buffer = new StringBuilder(times); + for (int i = 0; i < times; i++) { + buffer.append(" "); + } + return buffer.toString(); + } + } diff --git a/src/test/resources/x12/005010X222/837_REF_impls.xml b/src/test/resources/x12/005010X222/837_REF_impls.xml new file mode 100644 index 00000000..d27d6579 --- /dev/null +++ b/src/test/resources/x12/005010X222/837_REF_impls.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + AA + + + + + + + + + + + + + diff --git a/src/test/resources/x12/issue229/837-header-ref-only.edi b/src/test/resources/x12/issue229/837-header-ref-only.edi new file mode 100644 index 00000000..7491cb69 --- /dev/null +++ b/src/test/resources/x12/issue229/837-header-ref-only.edi @@ -0,0 +1,8 @@ +ISA*00* *00* *ZZ*DATA *ZZ*BRT01 *211108*0515*^*00501*000000001*0*P*:~ +GS*HC*DATA*BRT01*20211108*1030*1*X*005010X222A1~ +ST*837*1001*005010X222A1~ +BHT*DATA*00*DATA*20211108*1030*CH~ +REF*BB*DATA**00~ +SE*4*1001~ +GE*1*1~ +IEA*1*000000001~