diff --git a/pom.xml b/pom.xml index 9b59ed19..68149219 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.xlate staedi - 1.17.1-SNAPSHOT + 1.18.0-SNAPSHOT StAEDI : Streaming API for EDI for Java Streaming API for EDI for Java diff --git a/src/main/java/io/xlate/edi/internal/schema/ElementPosition.java b/src/main/java/io/xlate/edi/internal/schema/ElementPosition.java new file mode 100644 index 00000000..d8c6ed13 --- /dev/null +++ b/src/main/java/io/xlate/edi/internal/schema/ElementPosition.java @@ -0,0 +1,56 @@ +package io.xlate.edi.internal.schema; + +import java.util.Objects; + +import io.xlate.edi.schema.EDIElementPosition; + +public class ElementPosition implements EDIElementPosition { + + private static final String TOSTRING_FORMAT = "%d.%d"; + + final int elementIndex; + final int componentIndex; + + public ElementPosition(int elementPosition, int componentPosition) { + super(); + this.elementIndex = elementPosition; + this.componentIndex = componentPosition; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!getClass().isInstance(o)) { + return false; + } + + ElementPosition other = (ElementPosition) o; + + return Objects.equals(elementIndex, other.elementIndex) && + Objects.equals(componentIndex, other.componentIndex); + } + + @Override + public int hashCode() { + return Objects.hash(elementIndex, componentIndex); + } + + @Override + public String toString() { + return String.format(TOSTRING_FORMAT, elementIndex, componentIndex); + } + + @Override + public int getElementPosition() { + return elementIndex; + } + + @Override + public int getComponentPosition() { + return componentIndex > 0 ? componentIndex : -1; + } + +} diff --git a/src/main/java/io/xlate/edi/internal/schema/LoopType.java b/src/main/java/io/xlate/edi/internal/schema/LoopType.java new file mode 100644 index 00000000..9cc632e9 --- /dev/null +++ b/src/main/java/io/xlate/edi/internal/schema/LoopType.java @@ -0,0 +1,40 @@ +package io.xlate.edi.internal.schema; + +import java.util.List; + +import io.xlate.edi.schema.EDIElementPosition; +import io.xlate.edi.schema.EDILoopType; +import io.xlate.edi.schema.EDIReference; +import io.xlate.edi.schema.EDISyntaxRule; +import io.xlate.edi.schema.EDIType; + +@SuppressWarnings("java:S2160") // Intentionally inherit 'equals' from superclass +class LoopType extends StructureType implements EDILoopType { + + private final EDIElementPosition levelIdPosition; + private final EDIElementPosition parentIdPosition; + + LoopType(String code, + List references, + List syntaxRules, + EDIElementPosition levelIdPosition, + EDIElementPosition parentIdPosition, + String title, + String description) { + + super(code, EDIType.Type.LOOP, code, references, syntaxRules, title, description); + this.levelIdPosition = levelIdPosition; + this.parentIdPosition = parentIdPosition; + } + + @Override + public EDIElementPosition getLevelIdPosition() { + return levelIdPosition; + } + + @Override + public EDIElementPosition getParentIdPosition() { + return parentIdPosition; + } + +} diff --git a/src/main/java/io/xlate/edi/internal/schema/SchemaReaderBase.java b/src/main/java/io/xlate/edi/internal/schema/SchemaReaderBase.java index ef4432a4..e0bd4dbc 100644 --- a/src/main/java/io/xlate/edi/internal/schema/SchemaReaderBase.java +++ b/src/main/java/io/xlate/edi/internal/schema/SchemaReaderBase.java @@ -5,6 +5,7 @@ import static io.xlate.edi.internal.schema.StaEDISchemaFactory.unexpectedEvent; import static java.util.stream.Collectors.toList; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -25,6 +26,7 @@ import javax.xml.stream.XMLStreamReader; import io.xlate.edi.schema.EDIComplexType; +import io.xlate.edi.schema.EDIElementPosition; import io.xlate.edi.schema.EDIReference; import io.xlate.edi.schema.EDISchemaException; import io.xlate.edi.schema.EDISimpleType; @@ -45,6 +47,8 @@ abstract class SchemaReaderBase implements SchemaReader { static final String ATTR_MIN_OCCURS = "minOccurs"; static final String ATTR_MAX_OCCURS = "maxOccurs"; static final String ATTR_TITLE = "title"; + static final String ATTR_LEVEL_ID_POSITION = "levelIdPosition"; + static final String ATTR_PARENT_ID_POSITION = "parentIdPosition"; static final EDIReference ANY_ELEMENT_REF_OPT = new Reference(StaEDISchema.ANY_ELEMENT_ID, LOCALNAME_ELEMENT, 0, 1, null, null); static final EDIReference ANY_COMPOSITE_REF_OPT = new Reference(StaEDISchema.ANY_COMPOSITE_ID, LOCALNAME_COMPOSITE, 0, 99, null, null); @@ -388,11 +392,15 @@ StructureType readComplexType(XMLStreamReader reader, final EDIType.Type type = complex.get(complexType); final String id; String code = parseAttribute(reader, "code", String::valueOf, null); + EDIElementPosition levelIdPosition = null; + EDIElementPosition parentIdPosition = null; if (qnTransaction.equals(complexType)) { id = StaEDISchema.TRANSACTION_ID; } else if (qnLoop.equals(complexType)) { id = code; + levelIdPosition = parseElementPosition(reader, ATTR_LEVEL_ID_POSITION); + parentIdPosition = parseElementPosition(reader, ATTR_PARENT_ID_POSITION); } else { id = parseAttribute(reader, "name", String::valueOf); @@ -423,7 +431,15 @@ StructureType readComplexType(XMLStreamReader reader, event = reader.getEventType(); if (event == XMLStreamConstants.END_ELEMENT) { - return new StructureType(id, type, code, refs, rules, title, descr); + StructureType structure; + + if (qnLoop.equals(complexType)) { + structure = new LoopType(code, refs, rules, levelIdPosition, parentIdPosition, title, descr); + } else { + structure = new StructureType(id, type, code, refs, rules, title, descr); + } + + return structure; } else { throw unexpectedEvent(reader); } @@ -806,6 +822,21 @@ void logUnusedTypes(Map types, Level level) { } } + EDIElementPosition parseElementPosition(XMLStreamReader reader, String attrName) { + BigDecimal positionAttr = parseAttribute(reader, attrName, BigDecimal::new, null); + + if (positionAttr == null) { + return null; + } + + int elementPosition = positionAttr.intValue(); + int componentPosition = positionAttr.remainder(BigDecimal.ONE) + .movePointRight(positionAttr.scale()) + .intValue(); + + return new ElementPosition(elementPosition, componentPosition); + } + protected abstract String readReferencedId(XMLStreamReader reader); protected abstract void readInclude(XMLStreamReader reader, Map types) throws EDISchemaException; 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 241f5149..3f1858d2 100644 --- a/src/main/java/io/xlate/edi/internal/schema/SchemaReaderV3.java +++ b/src/main/java/io/xlate/edi/internal/schema/SchemaReaderV3.java @@ -3,7 +3,6 @@ import static io.xlate.edi.internal.schema.StaEDISchemaFactory.schemaException; import static io.xlate.edi.internal.schema.StaEDISchemaFactory.unexpectedElement; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.Deque; @@ -34,6 +33,7 @@ import io.xlate.edi.internal.schema.implementation.SegmentImpl; import io.xlate.edi.internal.schema.implementation.TransactionImpl; import io.xlate.edi.schema.EDIComplexType; +import io.xlate.edi.schema.EDIElementPosition; import io.xlate.edi.schema.EDIReference; import io.xlate.edi.schema.EDISchemaException; import io.xlate.edi.schema.EDIType; @@ -215,7 +215,7 @@ LoopImplementation readLoopImplementation(XMLStreamReader reader, QName complexT String typeId; int minOccurs = 0; int maxOccurs = 0; - BigDecimal discriminatorAttr = null; + EDIElementPosition discriminatorPos = null; String title = null; if (transactionLoop) { @@ -226,7 +226,7 @@ LoopImplementation readLoopImplementation(XMLStreamReader reader, QName complexT typeId = parseAttribute(reader, "type", String::valueOf); minOccurs = parseAttribute(reader, ATTR_MIN_OCCURS, Integer::parseInt, -1); maxOccurs = parseAttribute(reader, ATTR_MAX_OCCURS, Integer::parseInt, -1); - discriminatorAttr = parseAttribute(reader, ATTR_DISCRIMINATOR, BigDecimal::new, null); + discriminatorPos = parseElementPosition(reader, ATTR_DISCRIMINATOR); title = parseAttribute(reader, ATTR_TITLE, String::valueOf, null); } @@ -237,9 +237,9 @@ LoopImplementation readLoopImplementation(XMLStreamReader reader, QName complexT Discriminator disc = null; - if (discriminatorAttr != null) { + if (discriminatorPos != null) { SegmentImpl segImpl = (SegmentImpl) sequence.get(0); - disc = buildDiscriminator(discriminatorAttr, segImpl.getSequence()); + disc = buildDiscriminator(discriminatorPos, segImpl.getSequence()); } return new LoopImpl(minOccurs, maxOccurs, id, typeId, disc, sequence, title, descr); @@ -266,13 +266,13 @@ SegmentImpl readSegmentImplementation() { String code = parseAttribute(reader, "code", String::valueOf, typeId); int minOccurs = parseAttribute(reader, ATTR_MIN_OCCURS, Integer::parseInt, -1); int maxOccurs = parseAttribute(reader, ATTR_MAX_OCCURS, Integer::parseInt, -1); - BigDecimal discriminatorAttr = parseAttribute(reader, ATTR_DISCRIMINATOR, BigDecimal::new, null); + EDIElementPosition discriminatorPos = parseElementPosition(reader, ATTR_DISCRIMINATOR); 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(discriminatorAttr, sequence); + Discriminator disc = buildDiscriminator(discriminatorPos, sequence); SegmentImpl segment = new SegmentImpl(minOccurs, maxOccurs, typeId, @@ -312,20 +312,19 @@ void readPositionedSequenceEntry(QName entryName, List se } } - Discriminator buildDiscriminator(BigDecimal discriminatorAttr, + Discriminator buildDiscriminator(EDIElementPosition discriminatorPos, List sequence) { Discriminator disc = null; - if (discriminatorAttr != null) { - int elementPosition = discriminatorAttr.intValue(); - int componentPosition = discriminatorAttr.remainder(BigDecimal.ONE) - .movePointRight(discriminatorAttr.scale()).intValue(); + if (discriminatorPos != null) { + final int elementPosition = discriminatorPos.getElementPosition(); + final int componentPosition = discriminatorPos.getComponentPosition(); - EDITypeImplementation eleImpl = getDiscriminatorElement(discriminatorAttr, elementPosition, sequence, "element"); + EDITypeImplementation eleImpl = getDiscriminatorElement(discriminatorPos, elementPosition, sequence, "element"); if (eleImpl instanceof CompositeImpl) { sequence = ((CompositeImpl) eleImpl).getSequence(); - eleImpl = getDiscriminatorElement(discriminatorAttr, componentPosition, sequence, "component"); + eleImpl = getDiscriminatorElement(discriminatorPos, componentPosition, sequence, "component"); } Set discValues; @@ -333,24 +332,24 @@ Discriminator buildDiscriminator(BigDecimal discriminatorAttr, if (eleImpl != null) { discValues = ((ElementImpl) eleImpl).getValueSet(); } else { - throw schemaException("Discriminator position is unused (not specified): " + discriminatorAttr, reader); + throw schemaException("Discriminator position is unused (not specified): " + discriminatorPos, reader); } if (!discValues.isEmpty()) { - disc = new DiscriminatorImpl(elementPosition, componentPosition, discValues); + disc = new DiscriminatorImpl(discriminatorPos, discValues); } else { - throw schemaException("Discriminator element does not specify value enumeration: " + discriminatorAttr, reader); + throw schemaException("Discriminator element does not specify value enumeration: " + discriminatorPos, reader); } } return disc; } - EDITypeImplementation getDiscriminatorElement(BigDecimal attr, 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 { - throw schemaException("Discriminator " + type + " position invalid: " + attr, reader); + throw schemaException("Discriminator " + type + " position invalid: " + discriminatorPos, reader); } } diff --git a/src/main/java/io/xlate/edi/internal/schema/implementation/DiscriminatorImpl.java b/src/main/java/io/xlate/edi/internal/schema/implementation/DiscriminatorImpl.java index 6ae85ebc..a1f9da63 100644 --- a/src/main/java/io/xlate/edi/internal/schema/implementation/DiscriminatorImpl.java +++ b/src/main/java/io/xlate/edi/internal/schema/implementation/DiscriminatorImpl.java @@ -3,19 +3,17 @@ import java.util.Objects; import java.util.Set; +import io.xlate.edi.schema.EDIElementPosition; import io.xlate.edi.schema.implementation.Discriminator; public class DiscriminatorImpl implements Discriminator { - private static final String TOSTRING_FORMAT = "position: [%d, %d], values: %s"; - private final int elementPosition; - private final int componentPosition; - private final Set valueSet; + private static final String TOSTRING_FORMAT = "position: %s, values: %s"; + final EDIElementPosition position; + final Set valueSet; - public DiscriminatorImpl(int elementPosition, int componentPosition, Set valueSet) { - super(); - this.elementPosition = elementPosition; - this.componentPosition = componentPosition; + public DiscriminatorImpl(EDIElementPosition position, Set valueSet) { + this.position = position; this.valueSet = valueSet; } @@ -30,30 +28,28 @@ public boolean equals(Object o) { } DiscriminatorImpl other = (DiscriminatorImpl) o; - - return Objects.equals(elementPosition, other.elementPosition) && - Objects.equals(componentPosition, other.componentPosition) && + return Objects.equals(position, other.position) && Objects.equals(valueSet, other.valueSet); } @Override public int hashCode() { - return Objects.hash(elementPosition, componentPosition, valueSet); + return Objects.hash(position, valueSet); } @Override public String toString() { - return String.format(TOSTRING_FORMAT, elementPosition, componentPosition, valueSet); + return String.format(TOSTRING_FORMAT, position, valueSet); } @Override public int getElementPosition() { - return elementPosition; + return position.getElementPosition(); } @Override public int getComponentPosition() { - return componentPosition; + return position.getComponentPosition(); } @Override diff --git a/src/main/java/io/xlate/edi/internal/stream/CharArraySequence.java b/src/main/java/io/xlate/edi/internal/stream/CharArraySequence.java index 39806be7..6e431e75 100644 --- a/src/main/java/io/xlate/edi/internal/stream/CharArraySequence.java +++ b/src/main/java/io/xlate/edi/internal/stream/CharArraySequence.java @@ -45,10 +45,6 @@ public int length() { return length; } - public char[] getText() { - return text; - } - @Override public char charAt(int index) { if (index < 0 || index >= length) { diff --git a/src/main/java/io/xlate/edi/internal/stream/StaEDIInputFactory.java b/src/main/java/io/xlate/edi/internal/stream/StaEDIInputFactory.java index b5e491e2..92cda8d6 100644 --- a/src/main/java/io/xlate/edi/internal/stream/StaEDIInputFactory.java +++ b/src/main/java/io/xlate/edi/internal/stream/StaEDIInputFactory.java @@ -38,9 +38,11 @@ public class StaEDIInputFactory extends EDIInputFactory { public StaEDIInputFactory() { supportedProperties.add(EDI_VALIDATE_CONTROL_STRUCTURE); supportedProperties.add(EDI_VALIDATE_CONTROL_CODE_VALUES); + supportedProperties.add(EDI_IGNORE_EXTRANEOUS_CHARACTERS); + supportedProperties.add(EDI_NEST_HIERARCHICAL_LOOPS); + supportedProperties.add(XML_DECLARE_TRANSACTION_XMLNS); supportedProperties.add(XML_WRAP_TRANSACTION_CONTENTS); - supportedProperties.add(EDI_IGNORE_EXTRANEOUS_CHARACTERS); supportedProperties.add(JSON_NULL_EMPTY_ELEMENTS); supportedProperties.add(JSON_OBJECT_ELEMENTS); 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 392b6e92..7ea912a7 100644 --- a/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamReader.java +++ b/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamReader.java @@ -68,7 +68,7 @@ public StaEDIStreamReader( this.controlSchema = schema; this.properties = new HashMap<>(properties); this.reporter = reporter; - this.proxy = new ProxyEventHandler(location, this.controlSchema); + this.proxy = new ProxyEventHandler(location, this.controlSchema, nestHierarchicalLoops()); this.lexer = new Lexer(stream, charset, proxy, location, ignoreExtraneousCharacters()); } @@ -103,6 +103,7 @@ void requireEvent(String message, EDIStreamEvent... events) { } private CharBuffer getBuffer() { + checkTextState(); return proxy.getCharacters(); } @@ -365,19 +366,13 @@ private void checkTextState() { @Override public String getText() { - ensureOpen(); - checkTextState(); final CharBuffer buffer = getBuffer(); - return buffer.toString(); } @Override public char[] getTextCharacters() { - ensureOpen(); - checkTextState(); final CharBuffer buffer = getBuffer(); - return Arrays.copyOf(buffer.array(), buffer.length()); } @@ -387,9 +382,6 @@ public int getTextCharacters(int sourceStart, int targetStart, int length) { - ensureOpen(); - checkTextState(); - if (target == null) { throw new NullPointerException("Null target array"); } @@ -426,19 +418,13 @@ public int getTextCharacters(int sourceStart, @Override public int getTextStart() { - ensureOpen(); - checkTextState(); final CharBuffer buffer = getBuffer(); - return buffer.position(); } @Override public int getTextLength() { - ensureOpen(); - checkTextState(); final CharBuffer buffer = getBuffer(); - return buffer.limit(); } @@ -487,4 +473,8 @@ boolean useInternalControlSchema() { boolean ignoreExtraneousCharacters() { return getProperty(EDIInputFactory.EDI_IGNORE_EXTRANEOUS_CHARACTERS, Boolean::parseBoolean, false); } + + boolean nestHierarchicalLoops() { + return getProperty(EDIInputFactory.EDI_NEST_HIERARCHICAL_LOOPS, Boolean::parseBoolean, true); + } } diff --git a/src/main/java/io/xlate/edi/internal/stream/tokenization/Dialect.java b/src/main/java/io/xlate/edi/internal/stream/tokenization/Dialect.java index 5cdae9c5..f0eb95e5 100644 --- a/src/main/java/io/xlate/edi/internal/stream/tokenization/Dialect.java +++ b/src/main/java/io/xlate/edi/internal/stream/tokenization/Dialect.java @@ -176,4 +176,5 @@ public String[] getTransactionVersion() { public String getTransactionVersionString() { return transactionVersionString; } + } 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 55ea9b46..da16e570 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 @@ -17,14 +17,20 @@ import java.io.InputStream; import java.nio.CharBuffer; +import java.util.Deque; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Objects; +import java.util.Queue; +import java.util.function.Function; import io.xlate.edi.internal.stream.CharArraySequence; import io.xlate.edi.internal.stream.StaEDIStreamLocation; import io.xlate.edi.internal.stream.validation.UsageError; import io.xlate.edi.internal.stream.validation.Validator; +import io.xlate.edi.schema.EDIElementPosition; +import io.xlate.edi.schema.EDILoopType; import io.xlate.edi.schema.EDIReference; import io.xlate.edi.schema.EDIType; import io.xlate.edi.schema.Schema; @@ -35,6 +41,7 @@ public class ProxyEventHandler implements EventHandler { private final StaEDIStreamLocation location; + private final boolean nestHierarchicalLoops; private Schema controlSchema; private Validator controlValidator; @@ -49,17 +56,41 @@ public class ProxyEventHandler implements EventHandler { private String segmentTag; private CharArraySequence elementHolder = new CharArraySequence(); - private StreamEvent[] events = new StreamEvent[99]; - private int eventCount = 0; - private int eventIndex = 0; + private final Queue eventPool = new LinkedList<>(); + // Use implementation to access as both Deque & List + private final LinkedList eventQueue = new LinkedList<>(); + + private final Deque openLevels = new LinkedList<>(); + + static class HierarchicalLevel { + final String id; + final StreamEvent event; + + HierarchicalLevel(String id, StreamEvent event) { + this.id = id; + this.event = event; + } + + boolean isParentOf(String parentId) { + return !parentId.isEmpty() && parentId.equals(id); + } + } + + private boolean levelCheckPending; + private StreamEvent currentSegmentBegin; + private StreamEvent startedLevel; + private EDIElementPosition levelIdPosition; + private String startedLevelId; + private EDIElementPosition parentIdPosition; + private String startedLevelParentId; + private Dialect dialect; - public ProxyEventHandler(StaEDIStreamLocation location, Schema controlSchema) { + public ProxyEventHandler(StaEDIStreamLocation location, Schema controlSchema, boolean nestHierarchicalLoops) { this.location = location; + this.nestHierarchicalLoops = nestHierarchicalLoops; + setControlSchema(controlSchema, true); - for (int i = 0; i < 99; i++) { - events[i] = new StreamEvent(); - } } public void setControlSchema(Schema controlSchema, boolean validateCodeValues) { @@ -87,48 +118,53 @@ public void setTransactionSchema(Schema transactionSchema) { } public void resetEvents() { - eventCount = 0; - eventIndex = 0; + eventPool.addAll(eventQueue); + eventQueue.clear(); } public EDIStreamEvent getEvent() { - if (hasEvents()) { - return events[eventIndex].type; - } - return null; + return current(StreamEvent::getType, null); } public CharBuffer getCharacters() { - if (hasEvents()) { - return events[eventIndex].getData(); - } - throw new IllegalStateException(); - } - - public boolean hasEvents() { - return eventIndex < eventCount; + return current(StreamEvent::getData, null); } public boolean nextEvent() { - if (eventCount < 1) { + if (eventQueue.isEmpty()) { return false; } - return ++eventIndex < eventCount; + + eventPool.add(eventQueue.removeFirst()); + return !eventQueue.isEmpty(); } public EDIStreamValidationError getErrorType() { - return events[eventIndex].errorType; + return current(StreamEvent::getErrorType, null); } public String getReferenceCode() { - return hasEvents() ? events[eventIndex].getReferenceCode() : null; + return current(StreamEvent::getReferenceCode, null); } public Location getLocation() { - if (hasEvents() && events[eventIndex].location != null) { - return events[eventIndex].location; + return current(StreamEvent::getLocation, this.location); + } + + T current(Function mapper, T defaultValue) { + final T value; + + if (eventQueue.isEmpty()) { + value = defaultValue; + } else { + value = mapper.apply(eventQueue.getFirst()); } - return location; + + return value; + } + + StreamEvent getPooledEvent() { + return eventPool.isEmpty() ? new StreamEvent() : eventPool.remove(); } public InputStream getBinary() { @@ -140,7 +176,7 @@ public void setBinary(InputStream binary) { } public EDIReference getSchemaTypeReference() { - return hasEvents() ? events[eventIndex].getTypeReference() : null; + return current(StreamEvent::getTypeReference, null); } @Override @@ -175,6 +211,14 @@ public void loopBegin(EDIReference typeReference) { enqueueEvent(EDIStreamEvent.START_GROUP, EDIStreamValidationError.NONE, loopCode, typeReference, location); } else { enqueueEvent(EDIStreamEvent.START_LOOP, EDIStreamValidationError.NONE, loopCode, typeReference, location); + + if (nestHierarchicalLoops && isHierarchicalLoop(typeReference.getReferencedType())) { + EDILoopType loop = (EDILoopType) typeReference.getReferencedType(); + startedLevel = eventQueue.getLast(); + levelIdPosition = loop.getLevelIdPosition(); + parentIdPosition = loop.getParentIdPosition(); + levelCheckPending = true; + } } } @@ -192,6 +236,8 @@ public void loopEnd(EDIReference typeReference) { } else if (EDIType.Type.GROUP.toString().equals(loopCode)) { dialect.groupEnd(); enqueueEvent(EDIStreamEvent.END_GROUP, EDIStreamValidationError.NONE, loopCode, typeReference, location); + } else if (nestHierarchicalLoops && isHierarchicalLoop(typeReference.getReferencedType())) { + levelCheckPending = true; } else { enqueueEvent(EDIStreamEvent.END_LOOP, EDIStreamValidationError.NONE, loopCode, typeReference, location); } @@ -209,6 +255,7 @@ public boolean segmentBegin(String segmentTag) { Validator validator = validator(); boolean eventsReady = true; EDIReference typeReference = null; + clearLevelCheck(); if (validator != null && !dialect.isServiceAdviceSegment(segmentTag)) { validator.validateSegment(this, segmentTag); @@ -231,11 +278,14 @@ public boolean segmentBegin(String segmentTag) { } enqueueEvent(EDIStreamEvent.START_SEGMENT, EDIStreamValidationError.NONE, segmentTag, typeReference, location); - return eventsReady; + currentSegmentBegin = eventQueue.getLast(); + return !levelCheckPending && eventsReady; } boolean exitTransaction(CharSequence tag) { - return transaction && !transactionSchemaAllowed && controlSchema != null + return transaction + && !transactionSchemaAllowed + && controlSchema != null && controlSchema.containsSegment(tag.toString()); } @@ -250,6 +300,10 @@ public boolean segmentEnd() { typeReference = validator.getSegmentReference(); } + if (levelCheckPending) { + performLevelCheck(); + } + location.clearSegmentLocations(); enqueueEvent(EDIStreamEvent.END_SEGMENT, EDIStreamValidationError.NONE, segmentTag, typeReference, location); return true; @@ -277,7 +331,7 @@ public boolean compositeBegin(boolean isNil) { } enqueueEvent(EDIStreamEvent.START_COMPOSITE, EDIStreamValidationError.NONE, "", typeReference, location); - return eventsReady; + return !levelCheckPending && eventsReady; } @Override @@ -291,7 +345,7 @@ public boolean compositeEnd(boolean isNil) { location.clearComponentPosition(); enqueueEvent(EDIStreamEvent.END_COMPOSITE, EDIStreamValidationError.NONE, "", null, location); - return eventsReady; + return !levelCheckPending && eventsReady; } @Override @@ -306,6 +360,10 @@ public boolean elementData(char[] text, int start, int length) { Validator validator = validator(); boolean valid; + if (levelCheckPending && startedLevel != null) { + setLevelIdentifiers(); + } + if (validator != null) { valid = validator.validateElement(dialect, location, elementHolder, null); derivedComposite = !compositeFromStream && validator.isComposite(); @@ -317,7 +375,14 @@ public boolean elementData(char[] text, int start, int length) { typeReference = null; } - if (derivedComposite && elementHolder.getText() != null/* Not an empty composite */) { + /* + * The first component of a composite was the only element received + * for the composite. It was found to be a composite via the schema + * and the composite begin/end events must be generated. + **/ + final boolean componentReceivedAsSimple = derivedComposite && text != null; + + if (componentReceivedAsSimple) { this.compositeBegin(elementHolder.length() == 0); location.incrementComponentPosition(); } @@ -332,16 +397,79 @@ public boolean elementData(char[] text, int start, int length) { location); if (validator != null && validator.isPendingDiscrimination()) { - eventsReady = validator.selectImplementation(events, eventIndex, eventCount, this); + eventsReady = validator.selectImplementation(eventQueue, this); } } - if (derivedComposite && text != null /* Not an empty composite */) { + if (componentReceivedAsSimple) { this.compositeEnd(length == 0); location.clearComponentPosition(); } - return eventsReady; + return !levelCheckPending && eventsReady; + } + + void clearLevelCheck() { + levelCheckPending = false; + currentSegmentBegin = null; + startedLevel = null; + + levelIdPosition = null; + startedLevelId = ""; + + parentIdPosition = null; + startedLevelParentId = ""; + } + + boolean isHierarchicalLoop(EDIType type) { + EDILoopType loop = (EDILoopType) type; + + return loop.getLevelIdPosition() != null && + loop.getParentIdPosition() != null; + } + + void setLevelIdentifiers() { + if (levelIdPosition.matchesLocation(location)) { + startedLevelId = elementHolder.toString(); + } + + if (parentIdPosition.matchesLocation(location)) { + startedLevelParentId = elementHolder.toString(); + } + } + + void performLevelCheck() { + if (startedLevel != null) { + completeLevel(startedLevel, startedLevelParentId); + + StreamEvent openLevel = getPooledEvent(); + openLevel.type = EDIStreamEvent.END_LOOP; + openLevel.errorType = startedLevel.errorType; + openLevel.setData(startedLevel.data); + openLevel.setTypeReference(startedLevel.typeReference); + openLevel.setLocation(startedLevel.location); + + /* + * startedLevelId will not be null due to Validator#validateSyntax. + * Although the client never sees the generated element event, here + * it will be an empty string. + */ + openLevels.addLast(new HierarchicalLevel(startedLevelId, openLevel)); + } else { + completeLevel(currentSegmentBegin, ""); + } + + clearLevelCheck(); + } + + void completeLevel(StreamEvent successor, String parentId) { + while (!openLevels.isEmpty() && !openLevels.getLast().isParentOf(parentId)) { + HierarchicalLevel completed = openLevels.removeLast(); + completed.event.location.set(location); + completed.event.location.clearSegmentLocations(); + + eventQueue.add(eventQueue.indexOf(successor), completed.event); + } } void enqueueElementOccurrenceErrors(Validator validator, boolean valid) { @@ -435,28 +563,35 @@ private void enqueueEvent(EDIStreamEvent event, EDIReference typeReference, Location location) { - final int index = eventCount; - StreamEvent target = events[index]; - EDIStreamEvent associatedEvent = (index > 0) ? getAssociatedEvent(error) : null; + StreamEvent target = getPooledEvent(); + EDIStreamEvent associatedEvent = eventQueue.isEmpty() ? null : getAssociatedEvent(error); - if (eventExists(associatedEvent, index)) { + if (eventExists(associatedEvent)) { /* * Ensure segment errors occur before other event types * when the array has other events already present. */ - int offset = index; + int offset = eventQueue.size(); boolean complete = false; while (!complete) { - if (events[offset - 1].type == associatedEvent) { + StreamEvent enqueuedEvent = eventQueue.get(offset - 1); + + if (enqueuedEvent.type == associatedEvent) { complete = true; } else { - events[offset] = events[offset - 1]; + if (eventQueue.size() == offset) { + eventQueue.add(offset, enqueuedEvent); + } else { + eventQueue.set(offset, enqueuedEvent); + } offset--; } } - events[offset] = target; + eventQueue.set(offset, target); + } else { + eventQueue.add(target); } target.type = event; @@ -464,15 +599,13 @@ private void enqueueEvent(EDIStreamEvent event, target.setData(data); target.setTypeReference(typeReference); target.setLocation(location); - - eventCount++; } - private boolean eventExists(EDIStreamEvent associatedEvent, int index) { - int offset = index; + private boolean eventExists(EDIStreamEvent associatedEvent) { + int offset = eventQueue.size(); while (associatedEvent != null && offset > 0) { - if (events[offset - 1].type == associatedEvent) { + if (eventQueue.get(offset - 1).type == associatedEvent) { return true; } offset--; diff --git a/src/main/java/io/xlate/edi/internal/stream/tokenization/StreamEvent.java b/src/main/java/io/xlate/edi/internal/stream/tokenization/StreamEvent.java index fdcb5b72..812b7fb3 100644 --- a/src/main/java/io/xlate/edi/internal/stream/tokenization/StreamEvent.java +++ b/src/main/java/io/xlate/edi/internal/stream/tokenization/StreamEvent.java @@ -33,6 +33,10 @@ public EDIStreamEvent getType() { return type; } + public EDIStreamValidationError getErrorType() { + return errorType; + } + public CharBuffer getData() { return dataNull ? null : data; } 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 11800766..281b36ef 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 @@ -746,8 +746,8 @@ private void handleMissingMandatory(ValidationEventHandler handler, int depth) { } } - public boolean selectImplementation(StreamEvent[] events, int index, int count, ValidationEventHandler handler) { - StreamEvent currentEvent = events[index + count - 1]; + public boolean selectImplementation(Deque eventQueue, ValidationEventHandler handler) { + StreamEvent currentEvent = eventQueue.getLast(); if (currentEvent.getType() != EDIStreamEvent.ELEMENT_DATA) { return false; @@ -763,14 +763,14 @@ public boolean selectImplementation(StreamEvent[] events, int index, int count, if (implNode.isFirstChild()) { //start of loop, update the loop, segment, and element references that were already reported - updateEventReferences(events, index, count, implType, implSeg.getLink()); + updateEventReferences(eventQueue, 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()); + updateEventReferences(eventQueue, null, implSeg.getLink()); } return true; @@ -864,17 +864,8 @@ static boolean isMatch(PolymorphicImplementation implType, StreamEvent currentEv return true; } - if (discr.getValueSet().contains(currentEvent.getData().toString())) { - int eleLoc = discr.getElementPosition(); - int comLoc = discr.getComponentPosition() == 0 ? -1 : discr.getComponentPosition(); - Location location = currentEvent.getLocation(); - - if (eleLoc == location.getElementPosition() && comLoc == location.getComponentPosition()) { - return true; - } - } - - return false; + return discr.matchesLocation(currentEvent.getLocation()) + && discr.getValueSet().contains(currentEvent.getData().toString()); } /** @@ -886,21 +877,21 @@ static boolean isMatch(PolymorphicImplementation implType, StreamEvent currentEv * @param count * @param implType */ - static void updateEventReferences(StreamEvent[] events, int index, int count, EDIReference implType, EDIReference implSeg) { - for (int i = index; i < count; i++) { - switch (events[i].getType()) { + static void updateEventReferences(Deque eventQueue, EDIReference implType, EDIReference implSeg) { + for (StreamEvent event : eventQueue) { + switch (event.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); + updateReferenceWhenMatched(event, implType); break; case START_SEGMENT: - updateReferenceWhenMatched(events[i], implSeg); + updateReferenceWhenMatched(event, implSeg); break; case START_COMPOSITE: case END_COMPOSITE: case ELEMENT_DATA: - updateReference(events[i], (SegmentImplementation) implSeg); + updateReference(event, (SegmentImplementation) implSeg); break; default: break; diff --git a/src/main/java/io/xlate/edi/schema/EDIElementPosition.java b/src/main/java/io/xlate/edi/schema/EDIElementPosition.java new file mode 100644 index 00000000..fd5933ae --- /dev/null +++ b/src/main/java/io/xlate/edi/schema/EDIElementPosition.java @@ -0,0 +1,44 @@ +package io.xlate.edi.schema; + +import io.xlate.edi.stream.Location; + +/** + * Specification of the position of an element. Provides information about the + * element position within a segment and (optionally) a component element + * position within a composite element. + * + * @since 1.18 + * @see io.xlate.edi.schema.implementation.Discriminator + */ +public interface EDIElementPosition { + + /** + * Get the element position within a segment. + * + * @return the element position within a segment + */ + int getElementPosition(); + + /** + * Get the component position within a composite element. Returns -1 if not + * available. + * + * @return the component position with a composite element or -1 + */ + int getComponentPosition(); + + /** + * Determine if the given location's element and component matches this + * element position specification. + * + * @param location + * the location to match against this position + * @return true if the element and component positions match, otherwise + * false + */ + default boolean matchesLocation(Location location) { + return location.getElementPosition() == getElementPosition() + && location.getComponentPosition() == getComponentPosition(); + } + +} diff --git a/src/main/java/io/xlate/edi/schema/EDILoopType.java b/src/main/java/io/xlate/edi/schema/EDILoopType.java new file mode 100644 index 00000000..5ae2d318 --- /dev/null +++ b/src/main/java/io/xlate/edi/schema/EDILoopType.java @@ -0,0 +1,28 @@ +package io.xlate.edi.schema; + +/** + * Provides access to attributes specified for a loop complex type. + * + * @since 1.18 + */ +public interface EDILoopType extends EDIComplexType { + + /** + * For hierarchical loops, get the position of the element within the first + * segment that identifies the loop. If not a hierarchical loop, null is + * returned. + * + * @return the position of the element holding the loop's level ID + */ + EDIElementPosition getLevelIdPosition(); + + /** + * For hierarchical loops, get the position of the element within the first + * segment that identifies the parent of the loop. If not a hierarchical + * loop, null is returned. + * + * @return the position of the element holding the loop's parent level ID + */ + EDIElementPosition getParentIdPosition(); + +} diff --git a/src/main/java/io/xlate/edi/schema/implementation/Discriminator.java b/src/main/java/io/xlate/edi/schema/implementation/Discriminator.java index c8e4b78d..452de98d 100644 --- a/src/main/java/io/xlate/edi/schema/implementation/Discriminator.java +++ b/src/main/java/io/xlate/edi/schema/implementation/Discriminator.java @@ -2,11 +2,9 @@ import java.util.Set; -public interface Discriminator { +import io.xlate.edi.schema.EDIElementPosition; - int getElementPosition(); - - int getComponentPosition(); +public interface Discriminator extends EDIElementPosition { Set getValueSet(); diff --git a/src/main/java/io/xlate/edi/stream/EDIInputFactory.java b/src/main/java/io/xlate/edi/stream/EDIInputFactory.java index b0e0e0f9..c219ca59 100644 --- a/src/main/java/io/xlate/edi/stream/EDIInputFactory.java +++ b/src/main/java/io/xlate/edi/stream/EDIInputFactory.java @@ -62,6 +62,19 @@ public abstract class EDIInputFactory extends PropertySupport { */ public static final String EDI_IGNORE_EXTRANEOUS_CHARACTERS = "io.xlate.edi.stream.EDI_IGNORE_EXTRANEOUS_CHARACTERS"; + /** + * When set to true, hierarchical loops will be nested in the EDI input + * stream. The nesting structure is determined by the linkage specified by + * the EDI data itself using pointers given in the EDI schema for a loop. + * + * For example, the hierarchical information given by the X12 HL segment. + * + * Default value: true + * + * @since 1.18 + */ + public static final String EDI_NEST_HIERARCHICAL_LOOPS = "io.xlate.edi.stream.EDI_NEST_HIERARCHICAL_LOOPS"; + /** * When set to true, simple data elements not containing data will be * represented via the JSON parsers as a null value. diff --git a/src/main/resources/schema/EDISchema-v4.xsd b/src/main/resources/schema/EDISchema-v4.xsd index f7d7a910..45ec868c 100644 --- a/src/main/resources/schema/EDISchema-v4.xsd +++ b/src/main/resources/schema/EDISchema-v4.xsd @@ -84,7 +84,7 @@ - + @@ -92,6 +92,10 @@ + + + + @@ -439,6 +443,26 @@ + + + + + The element position in the loop's first segment used to identify a + hierarchical level in the context of hierarchical nested loops. + When used, attribute parentId must also be specified. + + + + + + + + The element position in the loop's first segment used to identify a + hierarchical parent level in the context of hierarchical nested loops. + When used, attribute levelId must also be specified. + + + diff --git a/src/test/java/io/xlate/edi/internal/schema/implementation/DiscriminatorImplTest.java b/src/test/java/io/xlate/edi/internal/schema/implementation/DiscriminatorImplTest.java index 1d51f33c..c95a0f81 100644 --- a/src/test/java/io/xlate/edi/internal/schema/implementation/DiscriminatorImplTest.java +++ b/src/test/java/io/xlate/edi/internal/schema/implementation/DiscriminatorImplTest.java @@ -9,7 +9,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import io.xlate.edi.schema.implementation.Discriminator; +import io.xlate.edi.internal.schema.ElementPosition; +import io.xlate.edi.schema.EDIElementPosition; class DiscriminatorImplTest { @@ -17,23 +18,29 @@ class DiscriminatorImplTest { @BeforeEach void setUp() throws Exception { - target = new DiscriminatorImpl(1, 2, Collections.singleton("50")); + target = new DiscriminatorImpl(position(1, 2), Collections.singleton("50")); + } + + ElementPosition position(int e, int c) { + return new ElementPosition(e, c); } @Test void testHashCode() { - int expected = new DiscriminatorImpl(1, 2, Collections.singleton("50")).hashCode(); + int expected = new DiscriminatorImpl(position(1, 2), Collections.singleton("50")).hashCode(); assertEquals(expected, target.hashCode()); } @Test void testEquals_Same() { + assertEquals(target.position, target.position); assertEquals(target, target); } @Test void testEquals_Identical() { - Discriminator identical = new DiscriminatorImpl(1, 2, Collections.singleton("50")); + DiscriminatorImpl identical = new DiscriminatorImpl(position(1, 2), Collections.singleton("50")); + assertEquals(target.position, identical.position); assertEquals(target, identical); } @@ -44,16 +51,33 @@ void testEquals_NotInstance() { } } + @Test + void testEquals_PositionNotInstance() { + DiscriminatorImpl actual = new DiscriminatorImpl(new EDIElementPosition() { + @Override + public int getComponentPosition() { + return 1; + } + + @Override + public int getElementPosition() { + return 2; + } + }, Collections.singleton("50")); + + assertNotEquals(target, actual); + } + @Test void testEquals_Different() { - assertNotEquals(new DiscriminatorImpl(1, 2, Collections.singleton("60")), target); - assertNotEquals(new DiscriminatorImpl(2, 2, Collections.singleton("50")), target); - assertNotEquals(new DiscriminatorImpl(1, 3, Collections.singleton("50")), target); + assertNotEquals(new DiscriminatorImpl(position(1, 2), Collections.singleton("60")), target); + assertNotEquals(new DiscriminatorImpl(position(2, 2), Collections.singleton("50")), target); + assertNotEquals(new DiscriminatorImpl(position(1, 3), Collections.singleton("50")), target); } @Test void testToString() { - String expected = new DiscriminatorImpl(1, 2, Collections.singleton("50")).toString(); + String expected = new DiscriminatorImpl(position(1, 2), Collections.singleton("50")).toString(); assertEquals(expected, target.toString()); } diff --git a/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamReaderTest.java b/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamReaderTest.java index 62597c3e..52ab6340 100644 --- a/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamReaderTest.java +++ b/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamReaderTest.java @@ -24,11 +24,14 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.math.BigDecimal; import java.math.BigInteger; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -38,6 +41,7 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -2032,4 +2036,90 @@ void testTRADACOMS_IncorrectSegmentTagDelimiterIsInvalid() throws Exception { EDIStreamException thrown = assertThrows(EDIStreamException.class, () -> reader.next()); assertTrue(thrown.getMessage().contains("Expected TRADACOMS segment tag delimiter")); } + + @Test + void testHierarchicalLoopsNested() throws IOException { + EDIInputFactory factory = EDIInputFactory.newFactory(); + EDIStreamReader reader = factory.createEDIStreamReader(getClass().getResourceAsStream("/x12/sample837-HL-nested.edi")); + StringBuilder actual = new StringBuilder(); + List unexpected = new ArrayList<>(); + int depth = 0; + + try { + while (reader.hasNext()) { + switch (reader.next()) { + case SEGMENT_ERROR: + case ELEMENT_OCCURRENCE_ERROR: + case ELEMENT_DATA_ERROR: + unexpected.add(reader.getErrorType()); + break; + case START_TRANSACTION: + SchemaFactory schemaFactory = SchemaFactory.newFactory(); + URL schemaLocation = getClass().getResource("/x12/005010/837-hierarchical-level-enabled.xml"); + Schema schema = schemaFactory.createSchema(schemaLocation); + reader.setTransactionSchema(schema); + // Fall through... + case START_GROUP: + case START_LOOP: + actual.append(new String(new char[depth]).replace("\0", " ")); + actual.append(reader.getText()); + actual.append('\n'); + depth++; + break; + case END_LOOP: + case END_TRANSACTION: + case END_GROUP: + depth--; + break; + case START_SEGMENT: + actual.append(new String(new char[depth]).replace("\0", " ")); + actual.append(reader.getText()); + if ("HL".equals(reader.getLocation().getSegmentTag())) { + actual.append(":"); + } + break; + case END_SEGMENT: + actual.append('\n'); + break; + case ELEMENT_DATA: + Location loc = reader.getLocation(); + if ("HL".equals(loc.getSegmentTag())) { + if (loc.getElementPosition() == 1) { + actual.append(" id: "); + actual.append(reader.getText()); + } else if (loc.getElementPosition() == 2) { + if (reader.getText().isEmpty()) { + actual.append("; no parent"); + } else { + actual.append("; parent: "); + actual.append(reader.getText()); + } + } + } + break; + default: + break; + } + } + } catch (Exception e) { + unexpected.add(e); + } finally { + reader.close(); + } + + System.out.println(actual.toString()); + + // Expected 4 required elements missing from 2 empty HL segments + assertEquals(4, unexpected.size(), () -> unexpected.toString()); + EDIStreamValidationError expectedErr = EDIStreamValidationError.REQUIRED_DATA_ELEMENT_MISSING; + assertEquals(Arrays.asList(expectedErr, expectedErr, expectedErr, expectedErr), unexpected); + + String expected; + + try (InputStream stream = getClass().getResourceAsStream("/x12/sample837-HL-nested-expected.txt")) { + expected = new BufferedReader(new InputStreamReader(stream)).lines().collect(Collectors.joining("\n")); + } + + assertEquals(expected, actual.toString()); + } } diff --git a/src/test/java/io/xlate/edi/internal/stream/json/StaEDIJsonParserTest.java b/src/test/java/io/xlate/edi/internal/stream/json/StaEDIJsonParserTest.java index cee3c15b..8687b98e 100644 --- a/src/test/java/io/xlate/edi/internal/stream/json/StaEDIJsonParserTest.java +++ b/src/test/java/io/xlate/edi/internal/stream/json/StaEDIJsonParserTest.java @@ -138,35 +138,47 @@ void testInvalidParserType() { } @ParameterizedTest - @ValueSource( - classes = { - jakarta.json.stream.JsonParser.class, - javax.json.stream.JsonParser.class - }) - void testNullElementsAsArray(Class parserInterface) throws Throwable { + @CsvSource({ + "jakarta.json.stream.JsonParser, /x12/005010/837.xml, false, /x12/sample837-original.json", + "jakarta.json.stream.JsonParser, /x12/005010/837.xml, true, /x12/sample837-original.json", + "jakarta.json.stream.JsonParser, /x12/005010/837-hierarchical-level-enabled.xml, true, /x12/sample837-original-nestHL.json", + "jakarta.json.stream.JsonParser, /x12/005010/837-hierarchical-level-enabled.xml, false, /x12/sample837-original.json", + "javax.json.stream.JsonParser, /x12/005010/837.xml, false, /x12/sample837-original.json", + "javax.json.stream.JsonParser, /x12/005010/837.xml, true, /x12/sample837-original.json", + "javax.json.stream.JsonParser, /x12/005010/837-hierarchical-level-enabled.xml, true, /x12/sample837-original-nestHL.json", + "javax.json.stream.JsonParser, /x12/005010/837-hierarchical-level-enabled.xml, false, /x12/sample837-original.json" + }) + void testNullElementsAsArray(Class parserInterface, String schemaPath, boolean nestHL, String expectedResource) throws Throwable { ediReaderConfig.put(EDIInputFactory.JSON_NULL_EMPTY_ELEMENTS, true); - setupReader(factory, "/x12/sample837-original.edi", "/x12/005010/837.xml"); + ediReaderConfig.put(EDIInputFactory.EDI_NEST_HIERARCHICAL_LOOPS, nestHL); + setupReader(factory, "/x12/sample837-original.edi", schemaPath); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); Object jsonParser = factory.createJsonParser(ediReader, parserInterface); copyParserToGenerator(ediReader, jsonParser, buffer); - List expected = Files.readAllLines(Paths.get(getClass().getResource("/x12/sample837-original.json").toURI())); + List expected = Files.readAllLines(Paths.get(getClass().getResource(expectedResource).toURI())); System.out.println(buffer.toString()); JSONAssert.assertEquals(String.join("", expected), buffer.toString(), true); } @ParameterizedTest - @ValueSource( - classes = { - jakarta.json.stream.JsonParser.class, - javax.json.stream.JsonParser.class - }) - void testElementsAsObjects(Class parserInterface) throws Throwable { + @CsvSource({ + "jakarta.json.stream.JsonParser, /x12/005010/837.xml, false, /x12/sample837-original-object-elements.json", + "jakarta.json.stream.JsonParser, /x12/005010/837.xml, true, /x12/sample837-original-object-elements.json", + "jakarta.json.stream.JsonParser, /x12/005010/837-hierarchical-level-enabled.xml, true, /x12/sample837-original-object-elements-nestHL.json", + "jakarta.json.stream.JsonParser, /x12/005010/837-hierarchical-level-enabled.xml, false, /x12/sample837-original-object-elements.json", + "javax.json.stream.JsonParser, /x12/005010/837.xml, false, /x12/sample837-original-object-elements.json", + "javax.json.stream.JsonParser, /x12/005010/837.xml, true, /x12/sample837-original-object-elements.json", + "javax.json.stream.JsonParser, /x12/005010/837-hierarchical-level-enabled.xml, true, /x12/sample837-original-object-elements-nestHL.json", + "javax.json.stream.JsonParser, /x12/005010/837-hierarchical-level-enabled.xml, false, /x12/sample837-original-object-elements.json" + }) + void testElementsAsObjects(Class parserInterface, String schemaPath, boolean nestHL, String expectedResource) throws Throwable { ediReaderConfig.put(EDIInputFactory.JSON_OBJECT_ELEMENTS, true); - setupReader(factory, "/x12/sample837-original.edi", "/x12/005010/837.xml"); + ediReaderConfig.put(EDIInputFactory.EDI_NEST_HIERARCHICAL_LOOPS, nestHL); + setupReader(factory, "/x12/sample837-original.edi", schemaPath); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); Object jsonParser = JsonParserFactory.createJsonParser(ediReader, parserInterface, ediReaderConfig); copyParserToGenerator(ediReader, jsonParser, buffer); - List expected = Files.readAllLines(Paths.get(getClass().getResource("/x12/sample837-original-object-elements.json").toURI())); + List expected = Files.readAllLines(Paths.get(getClass().getResource(expectedResource).toURI())); System.out.println(buffer.toString()); JSONAssert.assertEquals(String.join("", expected), buffer.toString(), true); } diff --git a/src/test/resources/x12/005010/837-hierarchical-level-enabled.xml b/src/test/resources/x12/005010/837-hierarchical-level-enabled.xml new file mode 100644 index 00000000..e9b2679d --- /dev/null +++ b/src/test/resources/x12/005010/837-hierarchical-level-enabled.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/x12/005010/837-types.xml b/src/test/resources/x12/005010/837-types.xml new file mode 100644 index 00000000..9e1b74bc --- /dev/null +++ b/src/test/resources/x12/005010/837-types.xml @@ -0,0 +1,1970 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/x12/005010/837.xml b/src/test/resources/x12/005010/837.xml index c522f2d4..7cd4a9ac 100644 --- a/src/test/resources/x12/005010/837.xml +++ b/src/test/resources/x12/005010/837.xml @@ -1,5 +1,8 @@ + + + @@ -169,1970 +172,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/test/resources/x12/sample837-HL-nested-expected.txt b/src/test/resources/x12/sample837-HL-nested-expected.txt new file mode 100644 index 00000000..22edd3e8 --- /dev/null +++ b/src/test/resources/x12/sample837-HL-nested-expected.txt @@ -0,0 +1,74 @@ +ISA +GROUP + GS + TRANSACTION + ST + BHT + L0001 + NM1 + L0001 + NM1 + L0002 + HL: + L0002 + HL: + L0002 + HL: id: 1; no parent + L0002 + HL: id: 2; parent: 1 + L0002 + HL: id: 3; parent: 2 + L0002 + HL: id: 4; parent: 2 + L0002 + HL: id: 5; parent: 2 + L0002 + HL: id: 6; parent: 1 + L0002 + HL: id: 7; parent: 1 + L0002 + HL: id: 8; parent: 7 + L0002 + HL: id: 9; parent: 7 + L0002 + HL: id: 10; no parent + L0002 + HL: id: 11; no parent + L0002 + HL: id: 12; no parent + L0002 + HL: id: 13; parent: 12 + L0002 + HL: id: 14; parent: 12 + L0002 + HL: id: 15; parent: 14 + L0002 + HL: id: 16; parent: 14 + SE + TRANSACTION + ST + BHT + L0002 + HL: id: 1; no parent + L0002 + HL: id: 2; no parent + L0002 + HL: id: 3; no parent + L0002 + HL: id: 4; no parent + L0002 + HL: id: 5; parent: 4 + L0002 + HL: id: 6; parent: 4 + L0002 + HL: id: 7; parent: 6 + L0002 + HL: id: 8; parent: 6 + L0002 + HL: id: 9; no parent + L0002 + HL: id: 10; parent: 9 + SE + GE +IEA + diff --git a/src/test/resources/x12/sample837-HL-nested.edi b/src/test/resources/x12/sample837-HL-nested.edi new file mode 100644 index 00000000..451b3223 --- /dev/null +++ b/src/test/resources/x12/sample837-HL-nested.edi @@ -0,0 +1,40 @@ +ISA*01*0000000000*01*0000000000*ZZ*ABCDEFGHIJKLMNO*ZZ*123456789012345*210701*1230*`*00501*000000001*0*P*> +GS*HC*99999999999*888888888888*20210701*1229*1*X*005010 +ST*837*0001 +BHT*0019*00*565743*20210701*1225*CH +NM1*41*2*SENDER +NM1*40*2*RECEIVER +HL +HL +HL*1**20*1 + HL*2*1*22*1 + HL*3*2*23*0 + HL*4*2*23*0 + HL*5*2*23*0 + HL*6*1*22*0 + HL*7*1*22*1 + HL*8*7*23*0 + HL*9*7*23*0 +HL*10**20 +HL*11**20 +HL*12**20*1 + HL*13*12*22*0 + HL*14*12*22*1 + HL*15*14*23*0 + HL*16*14*23*0 +SE*8*0001 +ST*837*0002 +BHT*0019*00*565743*20210701*1225*CH +HL*1**20 +HL*2**20 +HL*3**20 +HL*4**20*1 + HL*5*4*22*0 + HL*6*4*22*1 + HL*7*6*23*0 + HL*8*6*23*0 +HL*9**20*1 + HL*10*9*22*0 +SE*4*0002 +GE*2*1 +IEA*1*000000001 diff --git a/src/test/resources/x12/sample837-original-nestHL.json b/src/test/resources/x12/sample837-original-nestHL.json new file mode 100644 index 00000000..41b77670 --- /dev/null +++ b/src/test/resources/x12/sample837-original-nestHL.json @@ -0,0 +1,579 @@ +{ + "name": "INTERCHANGE", + "type": "loop", + "data": [ + { + "name": "ISA", + "type": "segment", + "data": [ + "01", + "0000000000", + "01", + "0000000000", + "ZZ", + "ABCDEFGHIJKLMNO", + "ZZ", + "123456789012345", + "101127", + "1719", + "`", + "00402", + 3438, + "0", + "P", + ">" + ] + }, + { + "name": "GROUP", + "type": "loop", + "data": [ + { + "name": "GS", + "type": "segment", + "data": [ + "HC", + "99999999999", + "888888888888", + "20111219", + "1340", + 1377, + "X", + "005010X222" + ] + }, + { + "name": "TRANSACTION", + "type": "loop", + "data": [ + { + "name": "ST", + "type": "segment", + "data": [ + "837", + "0001", + "005010X222" + ] + }, + { + "name": "BHT", + "type": "segment", + "data": [ + "0019", + "00", + "565743", + "20110523", + "154959", + "CH" + ] + }, + { + "name": "L0001", + "type": "loop", + "data": [ + { + "name": "NM1", + "type": "segment", + "data": [ + "41", + "2", + "SAMPLE INC", + null, + null, + null, + null, + "46", + "496103" + ] + }, + { + "name": "PER", + "type": "segment", + "data": [ + "IC", + "EDI DEPT", + "EM", + "FEEDBACK@example.com", + "TE", + "3305551212" + ] + } + ] + }, + { + "name": "L0001", + "type": "loop", + "data": [ + { + "name": "NM1", + "type": "segment", + "data": [ + "40", + "2", + "PPO BLUE", + null, + null, + null, + null, + "46", + "54771" + ] + } + ] + }, + { + "name": "L0002", + "type": "loop", + "data": [ + { + "name": "HL", + "type": "segment", + "data": [ + "1", + null, + "20", + "1" + ] + }, + { + "name": "PRV", + "type": "segment", + "data": [ + "BI", + "PXC", + "333600000X" + ] + }, + { + "name": "L0003", + "type": "loop", + "data": [ + { + "name": "NM1", + "type": "segment", + "data": [ + "85", + "2", + "EDI SPECIALTY SAMPLE", + null, + null, + null, + null, + "XX", + "123456789" + ] + }, + { + "name": "N3", + "type": "segment", + "data": [ + "1212 DEPOT DRIVE" + ] + }, + { + "name": "N4", + "type": "segment", + "data": [ + "CHICAGO", + "IL", + "606930159" + ] + }, + { + "name": "REF", + "type": "segment", + "data": [ + "EI", + "300123456" + ] + } + ] + }, + { + "name": "L0002", + "type": "loop", + "data": [ + { + "name": "HL", + "type": "segment", + "data": [ + "2", + "1", + "22", + "1" + ] + }, + { + "name": "SBR", + "type": "segment", + "data": [ + "P", + null, + null, + null, + null, + null, + null, + null, + "BL" + ] + }, + { + "name": "L0003", + "type": "loop", + "data": [ + { + "name": "NM1", + "type": "segment", + "data": [ + "IL", + "1", + "CUSTOMER", + "KAREN", + null, + null, + null, + "MI", + "YYX123456789" + ] + }, + { + "name": "N3", + "type": "segment", + "data": [ + "228 PINEAPPLE CIRCLE" + ] + }, + { + "name": "N4", + "type": "segment", + "data": [ + "CORA", + "PA", + "15108" + ] + }, + { + "name": "DMG", + "type": "segment", + "data": [ + "D8", + "19630625", + "M" + ] + } + ] + }, + { + "name": "L0003", + "type": "loop", + "data": [ + { + "name": "NM1", + "type": "segment", + "data": [ + "PR", + "2", + "PPO BLUE", + null, + null, + null, + null, + "PI", + "54771" + ] + }, + { + "name": "N3", + "type": "segment", + "data": [ + "PO BOX 12345" + ] + }, + { + "name": "N4", + "type": "segment", + "data": [ + "CAMP HILL", + "PA", + "17089" + ] + } + ] + }, + { + "name": "L0002", + "type": "loop", + "data": [ + { + "name": "HL", + "type": "segment", + "data": [ + "3", + "2", + "23", + "0" + ] + }, + { + "name": "PAT", + "type": "segment", + "data": [ + "19" + ] + }, + { + "name": "L0003", + "type": "loop", + "data": [ + { + "name": "NM1", + "type": "segment", + "data": [ + "QC", + "1", + "CUSTOMER", + "COLE" + ] + }, + { + "name": "N3", + "type": "segment", + "data": [ + "228 PINEAPPLE CIRCLE" + ] + }, + { + "name": "N4", + "type": "segment", + "data": [ + "CORA", + "PA", + "15108" + ] + }, + { + "name": "DMG", + "type": "segment", + "data": [ + "D8", + "19940921", + "M" + ] + } + ] + }, + { + "name": "L0004", + "type": "loop", + "data": [ + { + "name": "CLM", + "type": "segment", + "data": [ + "945405", + 5332.54, + null, + null, + { + "name": "CLM05", + "type": "composite", + "data": [ + "12", + "B", + "1" + ] + }, + "Y", + "A", + "Y", + "Y", + "P" + ] + }, + { + "name": "HI", + "type": "segment", + "data": [ + { + "name": "HI01", + "type": "composite", + "data": [ + "BK", + "2533" + ] + } + ] + }, + { + "name": "L0009", + "type": "loop", + "data": [ + { + "name": "LX", + "type": "segment", + "data": [ + 1 + ] + }, + { + "name": "SV1", + "type": "segment", + "data": [ + { + "name": "SV101", + "type": "composite", + "data": [ + "HC", + "J2941" + ] + }, + 5332.54, + "UN", + 84, + null, + null, + { + "name": "SV107", + "type": "composite", + "data": [ + 1 + ] + } + ] + }, + { + "name": "DTP", + "type": "segment", + "data": [ + "472", + "RD8", + "20110511-20110511" + ] + }, + { + "name": "REF", + "type": "segment", + "data": [ + "6R", + "1099999731" + ] + }, + { + "name": "NTE", + "type": "segment", + "data": [ + "ADD", + "GENERIC 12MG CARTRIDGE" + ] + }, + { + "name": "L0010", + "type": "loop", + "data": [ + { + "name": "LIN", + "type": "segment", + "data": [ + null, + "N4", + "00013264681" + ] + }, + { + "name": "CTP", + "type": "segment", + "data": [ + null, + null, + null, + 7, + { + "name": "CTP05", + "type": "composite", + "data": [ + "UN" + ] + } + ] + } + ] + }, + { + "name": "L0011", + "type": "loop", + "data": [ + { + "name": "NM1", + "type": "segment", + "data": [ + "DK", + "1", + "PATIENT", + "DEBORAH", + null, + null, + null, + "XX", + "12345679030" + ] + }, + { + "name": "N3", + "type": "segment", + "data": [ + "123 MAIN ST", + "APT B" + ] + }, + { + "name": "N4", + "type": "segment", + "data": [ + "PITTSBURGH", + "PA", + "152181871" + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "name": "SE", + "type": "segment", + "data": [ + 39, + "0001" + ] + } + ] + }, + { + "name": "GE", + "type": "segment", + "data": [ + 1, + 1377 + ] + } + ] + }, + { + "name": "IEA", + "type": "segment", + "data": [ + 1, + 3438 + ] + } + ] +} \ No newline at end of file diff --git a/src/test/resources/x12/sample837-original-object-elements-nestHL.json b/src/test/resources/x12/sample837-original-object-elements-nestHL.json new file mode 100644 index 00000000..14895a28 --- /dev/null +++ b/src/test/resources/x12/sample837-original-object-elements-nestHL.json @@ -0,0 +1,1164 @@ +{ + "name": "INTERCHANGE", + "type": "loop", + "data": [ + { + "name": "ISA", + "type": "segment", + "data": [ + { + "type": "element", + "data": "01" + }, + { + "type": "element", + "data": "0000000000" + }, + { + "type": "element", + "data": "01" + }, + { + "type": "element", + "data": "0000000000" + }, + { + "type": "element", + "data": "ZZ" + }, + { + "type": "element", + "data": "ABCDEFGHIJKLMNO" + }, + { + "type": "element", + "data": "ZZ" + }, + { + "type": "element", + "data": "123456789012345" + }, + { + "type": "element", + "data": "101127" + }, + { + "type": "element", + "data": "1719" + }, + { + "type": "element", + "data": "`" + }, + { + "type": "element", + "data": "00402" + }, + { + "type": "element", + "data": 3438 + }, + { + "type": "element", + "data": "0" + }, + { + "type": "element", + "data": "P" + }, + { + "type": "element", + "data": ">" + } + ] + }, + { + "name": "GROUP", + "type": "loop", + "data": [ + { + "name": "GS", + "type": "segment", + "data": [ + { + "type": "element", + "data": "HC" + }, + { + "type": "element", + "data": "99999999999" + }, + { + "type": "element", + "data": "888888888888" + }, + { + "type": "element", + "data": "20111219" + }, + { + "type": "element", + "data": "1340" + }, + { + "type": "element", + "data": 1377 + }, + { + "type": "element", + "data": "X" + }, + { + "type": "element", + "data": "005010X222" + } + ] + }, + { + "name": "TRANSACTION", + "type": "loop", + "data": [ + { + "name": "ST", + "type": "segment", + "data": [ + { + "type": "element", + "data": "837" + }, + { + "type": "element", + "data": "0001" + }, + { + "type": "element", + "data": "005010X222" + } + ] + }, + { + "name": "BHT", + "type": "segment", + "data": [ + { + "type": "element", + "data": "0019" + }, + { + "type": "element", + "data": "00" + }, + { + "type": "element", + "data": "565743" + }, + { + "type": "element", + "data": "20110523" + }, + { + "type": "element", + "data": "154959" + }, + { + "type": "element", + "data": "CH" + } + ] + }, + { + "name": "L0001", + "type": "loop", + "data": [ + { + "name": "NM1", + "type": "segment", + "data": [ + { + "type": "element", + "data": "41" + }, + { + "type": "element", + "data": "2" + }, + { + "type": "element", + "data": "SAMPLE INC" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "46" + }, + { + "type": "element", + "data": "496103" + } + ] + }, + { + "name": "PER", + "type": "segment", + "data": [ + { + "type": "element", + "data": "IC" + }, + { + "type": "element", + "data": "EDI DEPT" + }, + { + "type": "element", + "data": "EM" + }, + { + "type": "element", + "data": "FEEDBACK@example.com" + }, + { + "type": "element", + "data": "TE" + }, + { + "type": "element", + "data": "3305551212" + } + ] + } + ] + }, + { + "name": "L0001", + "type": "loop", + "data": [ + { + "name": "NM1", + "type": "segment", + "data": [ + { + "type": "element", + "data": "40" + }, + { + "type": "element", + "data": "2" + }, + { + "type": "element", + "data": "PPO BLUE" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "46" + }, + { + "type": "element", + "data": "54771" + } + ] + } + ] + }, + { + "name": "L0002", + "type": "loop", + "data": [ + { + "name": "HL", + "type": "segment", + "data": [ + { + "type": "element", + "data": "1" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "20" + }, + { + "type": "element", + "data": "1" + } + ] + }, + { + "name": "PRV", + "type": "segment", + "data": [ + { + "type": "element", + "data": "BI" + }, + { + "type": "element", + "data": "PXC" + }, + { + "type": "element", + "data": "333600000X" + } + ] + }, + { + "name": "L0003", + "type": "loop", + "data": [ + { + "name": "NM1", + "type": "segment", + "data": [ + { + "type": "element", + "data": "85" + }, + { + "type": "element", + "data": "2" + }, + { + "type": "element", + "data": "EDI SPECIALTY SAMPLE" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "XX" + }, + { + "type": "element", + "data": "123456789" + } + ] + }, + { + "name": "N3", + "type": "segment", + "data": [ + { + "type": "element", + "data": "1212 DEPOT DRIVE" + } + ] + }, + { + "name": "N4", + "type": "segment", + "data": [ + { + "type": "element", + "data": "CHICAGO" + }, + { + "type": "element", + "data": "IL" + }, + { + "type": "element", + "data": "606930159" + } + ] + }, + { + "name": "REF", + "type": "segment", + "data": [ + { + "type": "element", + "data": "EI" + }, + { + "type": "element", + "data": "300123456" + } + ] + } + ] + }, + { + "name": "L0002", + "type": "loop", + "data": [ + { + "name": "HL", + "type": "segment", + "data": [ + { + "type": "element", + "data": "2" + }, + { + "type": "element", + "data": "1" + }, + { + "type": "element", + "data": "22" + }, + { + "type": "element", + "data": "1" + } + ] + }, + { + "name": "SBR", + "type": "segment", + "data": [ + { + "type": "element", + "data": "P" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "BL" + } + ] + }, + { + "name": "L0003", + "type": "loop", + "data": [ + { + "name": "NM1", + "type": "segment", + "data": [ + { + "type": "element", + "data": "IL" + }, + { + "type": "element", + "data": "1" + }, + { + "type": "element", + "data": "CUSTOMER" + }, + { + "type": "element", + "data": "KAREN" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "MI" + }, + { + "type": "element", + "data": "YYX123456789" + } + ] + }, + { + "name": "N3", + "type": "segment", + "data": [ + { + "type": "element", + "data": "228 PINEAPPLE CIRCLE" + } + ] + }, + { + "name": "N4", + "type": "segment", + "data": [ + { + "type": "element", + "data": "CORA" + }, + { + "type": "element", + "data": "PA" + }, + { + "type": "element", + "data": "15108" + } + ] + }, + { + "name": "DMG", + "type": "segment", + "data": [ + { + "type": "element", + "data": "D8" + }, + { + "type": "element", + "data": "19630625" + }, + { + "type": "element", + "data": "M" + } + ] + } + ] + }, + { + "name": "L0003", + "type": "loop", + "data": [ + { + "name": "NM1", + "type": "segment", + "data": [ + { + "type": "element", + "data": "PR" + }, + { + "type": "element", + "data": "2" + }, + { + "type": "element", + "data": "PPO BLUE" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "PI" + }, + { + "type": "element", + "data": "54771" + } + ] + }, + { + "name": "N3", + "type": "segment", + "data": [ + { + "type": "element", + "data": "PO BOX 12345" + } + ] + }, + { + "name": "N4", + "type": "segment", + "data": [ + { + "type": "element", + "data": "CAMP HILL" + }, + { + "type": "element", + "data": "PA" + }, + { + "type": "element", + "data": "17089" + } + ] + } + ] + }, + { + "name": "L0002", + "type": "loop", + "data": [ + { + "name": "HL", + "type": "segment", + "data": [ + { + "type": "element", + "data": "3" + }, + { + "type": "element", + "data": "2" + }, + { + "type": "element", + "data": "23" + }, + { + "type": "element", + "data": "0" + } + ] + }, + { + "name": "PAT", + "type": "segment", + "data": [ + { + "type": "element", + "data": "19" + } + ] + }, + { + "name": "L0003", + "type": "loop", + "data": [ + { + "name": "NM1", + "type": "segment", + "data": [ + { + "type": "element", + "data": "QC" + }, + { + "type": "element", + "data": "1" + }, + { + "type": "element", + "data": "CUSTOMER" + }, + { + "type": "element", + "data": "COLE" + } + ] + }, + { + "name": "N3", + "type": "segment", + "data": [ + { + "type": "element", + "data": "228 PINEAPPLE CIRCLE" + } + ] + }, + { + "name": "N4", + "type": "segment", + "data": [ + { + "type": "element", + "data": "CORA" + }, + { + "type": "element", + "data": "PA" + }, + { + "type": "element", + "data": "15108" + } + ] + }, + { + "name": "DMG", + "type": "segment", + "data": [ + { + "type": "element", + "data": "D8" + }, + { + "type": "element", + "data": "19940921" + }, + { + "type": "element", + "data": "M" + } + ] + } + ] + }, + { + "name": "L0004", + "type": "loop", + "data": [ + { + "name": "CLM", + "type": "segment", + "data": [ + { + "type": "element", + "data": "945405" + }, + { + "type": "element", + "data": 5332.54 + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "name": "CLM05", + "type": "composite", + "data": [ + { + "type": "element", + "data": "12" + }, + { + "type": "element", + "data": "B" + }, + { + "type": "element", + "data": "1" + } + ] + }, + { + "type": "element", + "data": "Y" + }, + { + "type": "element", + "data": "A" + }, + { + "type": "element", + "data": "Y" + }, + { + "type": "element", + "data": "Y" + }, + { + "type": "element", + "data": "P" + } + ] + }, + { + "name": "HI", + "type": "segment", + "data": [ + { + "name": "HI01", + "type": "composite", + "data": [ + { + "type": "element", + "data": "BK" + }, + { + "type": "element", + "data": "2533" + } + ] + } + ] + }, + { + "name": "L0009", + "type": "loop", + "data": [ + { + "name": "LX", + "type": "segment", + "data": [ + { + "type": "element", + "data": 1 + } + ] + }, + { + "name": "SV1", + "type": "segment", + "data": [ + { + "name": "SV101", + "type": "composite", + "data": [ + { + "type": "element", + "data": "HC" + }, + { + "type": "element", + "data": "J2941" + } + ] + }, + { + "type": "element", + "data": 5332.54 + }, + { + "type": "element", + "data": "UN" + }, + { + "type": "element", + "data": 84 + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "name": "SV107", + "type": "composite", + "data": [ + { + "type": "element", + "data": 1 + } + ] + } + ] + }, + { + "name": "DTP", + "type": "segment", + "data": [ + { + "type": "element", + "data": "472" + }, + { + "type": "element", + "data": "RD8" + }, + { + "type": "element", + "data": "20110511-20110511" + } + ] + }, + { + "name": "REF", + "type": "segment", + "data": [ + { + "type": "element", + "data": "6R" + }, + { + "type": "element", + "data": "1099999731" + } + ] + }, + { + "name": "NTE", + "type": "segment", + "data": [ + { + "type": "element", + "data": "ADD" + }, + { + "type": "element", + "data": "GENERIC 12MG CARTRIDGE" + } + ] + }, + { + "name": "L0010", + "type": "loop", + "data": [ + { + "name": "LIN", + "type": "segment", + "data": [ + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "N4" + }, + { + "type": "element", + "data": "00013264681" + } + ] + }, + { + "name": "CTP", + "type": "segment", + "data": [ + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": 7 + }, + { + "name": "CTP05", + "type": "composite", + "data": [ + { + "type": "element", + "data": "UN" + } + ] + } + ] + } + ] + }, + { + "name": "L0011", + "type": "loop", + "data": [ + { + "name": "NM1", + "type": "segment", + "data": [ + { + "type": "element", + "data": "DK" + }, + { + "type": "element", + "data": "1" + }, + { + "type": "element", + "data": "PATIENT" + }, + { + "type": "element", + "data": "DEBORAH" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "" + }, + { + "type": "element", + "data": "XX" + }, + { + "type": "element", + "data": "12345679030" + } + ] + }, + { + "name": "N3", + "type": "segment", + "data": [ + { + "type": "element", + "data": "123 MAIN ST" + }, + { + "type": "element", + "data": "APT B" + } + ] + }, + { + "name": "N4", + "type": "segment", + "data": [ + { + "type": "element", + "data": "PITTSBURGH" + }, + { + "type": "element", + "data": "PA" + }, + { + "type": "element", + "data": "152181871" + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "name": "SE", + "type": "segment", + "data": [ + { + "type": "element", + "data": 39 + }, + { + "type": "element", + "data": "0001" + } + ] + } + ] + }, + { + "name": "GE", + "type": "segment", + "data": [ + { + "type": "element", + "data": 1 + }, + { + "type": "element", + "data": 1377 + } + ] + } + ] + }, + { + "name": "IEA", + "type": "segment", + "data": [ + { + "type": "element", + "data": 1 + }, + { + "type": "element", + "data": 3438 + } + ] + } + ] +} diff --git a/src/test/resources/x12/sample837-original.edi b/src/test/resources/x12/sample837-original.edi index 2a9c0cd9..ce3a8261 100644 --- a/src/test/resources/x12/sample837-original.edi +++ b/src/test/resources/x12/sample837-original.edi @@ -1,43 +1,43 @@ ISA*01*0000000000*01*0000000000*ZZ*ABCDEFGHIJKLMNO*ZZ*123456789012345*101127*1719*`*00402*000003438*0*P*> -GS*HC*99999999999*888888888888*20111219*1340*1377*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 -PRV*BI*PXC*333600000X -NM1*85*2*EDI SPECIALTY SAMPLE*****XX*123456789 -N3*1212 DEPOT DRIVE -N4*CHICAGO*IL*606930159 -REF*EI*300123456 -HL*2*1*22*1 -SBR*P********BL -NM1*IL*1*CUSTOMER*KAREN****MI*YYX123456789 -N3*228 PINEAPPLE CIRCLE -N4*CORA*PA*15108 -DMG*D8*19630625*M -NM1*PR*2*PPO BLUE*****PI*54771 -N3*PO BOX 12345 -N4*CAMP HILL*PA*17089 -HL*3*2*23*0 -PAT*19 -NM1*QC*1*CUSTOMER*COLE -N3*228 PINEAPPLE CIRCLE -N4*CORA*PA*15108 -DMG*D8*19940921*M -CLM*945405*5332.54***12>B>1*Y*A*Y*Y*P -HI*BK>2533 -LX*1 -SV1*HC>J2941*5332.54*UN*84***1 -DTP*472*RD8*20110511-20110511 -REF*6R*1099999731 -NTE*ADD*GENERIC 12MG CARTRIDGE -LIN**N4*00013264681 -CTP****7*UN -NM1*DK*1*PATIENT*DEBORAH****XX*12345679030 -N3*123 MAIN ST*APT B -N4*PITTSBURGH*PA*152181871 -SE*39*0001 -GE*1*1377 + GS*HC*99999999999*888888888888*20111219*1340*1377*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 + PRV*BI*PXC*333600000X + NM1*85*2*EDI SPECIALTY SAMPLE*****XX*123456789 + N3*1212 DEPOT DRIVE + N4*CHICAGO*IL*606930159 + REF*EI*300123456 + HL*2*1*22*1 + SBR*P********BL + NM1*IL*1*CUSTOMER*KAREN****MI*YYX123456789 + N3*228 PINEAPPLE CIRCLE + N4*CORA*PA*15108 + DMG*D8*19630625*M + NM1*PR*2*PPO BLUE*****PI*54771 + N3*PO BOX 12345 + N4*CAMP HILL*PA*17089 + HL*3*2*23*0 + PAT*19 + NM1*QC*1*CUSTOMER*COLE + N3*228 PINEAPPLE CIRCLE + N4*CORA*PA*15108 + DMG*D8*19940921*M + CLM*945405*5332.54***12>B>1*Y*A*Y*Y*P + HI*BK>2533 + LX*1 + SV1*HC>J2941*5332.54*UN*84***1 + DTP*472*RD8*20110511-20110511 + REF*6R*1099999731 + NTE*ADD*GENERIC 12MG CARTRIDGE + LIN**N4*00013264681 + CTP****7*UN + NM1*DK*1*PATIENT*DEBORAH****XX*12345679030 + N3*123 MAIN ST*APT B + N4*PITTSBURGH*PA*152181871 + SE*39*0001 + GE*1*1377 IEA*1*000003438