diff --git a/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamWriter.java b/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamWriter.java index a8c9247b..ca56154c 100644 --- a/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamWriter.java +++ b/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamWriter.java @@ -27,6 +27,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -77,6 +78,7 @@ public class StaEDIStreamWriter implements EDIStreamWriter, ElementDataHandler, private Validator controlValidator; private boolean transactionSchemaAllowed = false; private boolean transaction = false; + private Schema transactionSchema; private Validator transactionValidator; private CharArraySequence dataHolder = new CharArraySequence(); private boolean atomicElementWrite = false; @@ -199,7 +201,10 @@ public void setControlSchema(Schema controlSchema) { @Override public void setTransactionSchema(Schema transactionSchema) { - transactionValidator = transactionSchema != null ? new Validator(transactionSchema, true, controlSchema) : null; + if (!Objects.equals(this.transactionSchema, transactionSchema)) { + this.transactionSchema = transactionSchema; + transactionValidator = transactionSchema != null ? new Validator(transactionSchema, true, controlSchema) : null; + } } @Override @@ -604,6 +609,9 @@ public void loopBegin(CharSequence id) { if (EDIType.Type.TRANSACTION.toString().equals(id)) { transaction = true; transactionSchemaAllowed = true; + if (transactionValidator != null) { + transactionValidator.reset(); + } } } 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 2b8cf8e4..367b626b 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 @@ -19,6 +19,7 @@ import java.nio.CharBuffer; import java.util.Iterator; import java.util.List; +import java.util.Objects; import io.xlate.edi.internal.stream.CharArraySequence; import io.xlate.edi.internal.stream.StaEDIStreamLocation; @@ -78,8 +79,10 @@ public Schema getTransactionSchema() { } public void setTransactionSchema(Schema transactionSchema) { - this.transactionSchema = transactionSchema; - transactionValidator = transactionSchema != null ? new Validator(transactionSchema, true, controlSchema) : null; + if (!Objects.equals(this.transactionSchema, transactionSchema)) { + this.transactionSchema = transactionSchema; + transactionValidator = transactionSchema != null ? new Validator(transactionSchema, true, controlSchema) : null; + } } public void resetEvents() { @@ -152,6 +155,9 @@ public void loopBegin(CharSequence id) { transaction = true; transactionSchemaAllowed = true; enqueueEvent(EDIStreamEvent.START_TRANSACTION, EDIStreamValidationError.NONE, id, null); + if (transactionValidator != null) { + transactionValidator.reset(); + } } else if (EDIType.Type.GROUP.toString().equals(id)) { enqueueEvent(EDIStreamEvent.START_GROUP, EDIStreamValidationError.NONE, id, null); } else { 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 bda8f35a..06a264ca 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 @@ -15,7 +15,23 @@ ******************************************************************************/ package io.xlate.edi.internal.stream.validation; -import static io.xlate.edi.stream.EDIStreamValidationError.*; +import static io.xlate.edi.stream.EDIStreamValidationError.IMPLEMENTATION_INVALID_CODE_VALUE; +import static io.xlate.edi.stream.EDIStreamValidationError.IMPLEMENTATION_LOOP_OCCURS_UNDER_MINIMUM_TIMES; +import static io.xlate.edi.stream.EDIStreamValidationError.IMPLEMENTATION_SEGMENT_BELOW_MINIMUM_USE; +import static io.xlate.edi.stream.EDIStreamValidationError.IMPLEMENTATION_TOO_FEW_REPETITIONS; +import static io.xlate.edi.stream.EDIStreamValidationError.IMPLEMENTATION_UNUSED_DATA_ELEMENT_PRESENT; +import static io.xlate.edi.stream.EDIStreamValidationError.IMPLEMENTATION_UNUSED_SEGMENT_PRESENT; +import static io.xlate.edi.stream.EDIStreamValidationError.INVALID_CODE_VALUE; +import static io.xlate.edi.stream.EDIStreamValidationError.LOOP_OCCURS_OVER_MAXIMUM_TIMES; +import static io.xlate.edi.stream.EDIStreamValidationError.MANDATORY_SEGMENT_MISSING; +import static io.xlate.edi.stream.EDIStreamValidationError.REQUIRED_DATA_ELEMENT_MISSING; +import static io.xlate.edi.stream.EDIStreamValidationError.SEGMENT_EXCEEDS_MAXIMUM_USE; +import static io.xlate.edi.stream.EDIStreamValidationError.SEGMENT_NOT_IN_DEFINED_TRANSACTION_SET; +import static io.xlate.edi.stream.EDIStreamValidationError.SEGMENT_NOT_IN_PROPER_SEQUENCE; +import static io.xlate.edi.stream.EDIStreamValidationError.TOO_MANY_COMPONENTS; +import static io.xlate.edi.stream.EDIStreamValidationError.TOO_MANY_DATA_ELEMENTS; +import static io.xlate.edi.stream.EDIStreamValidationError.TOO_MANY_REPETITIONS; +import static io.xlate.edi.stream.EDIStreamValidationError.UNEXPECTED_SEGMENT; import java.util.ArrayList; import java.util.Collections; @@ -53,6 +69,7 @@ public class Validator { private Schema containerSchema; private Schema schema; private final boolean validateCodeValues; + private boolean initial = true; private final UsageNode root; private final UsageNode implRoot; @@ -120,6 +137,36 @@ public Validator(Schema schema, boolean validateCodeValues, Schema containerSche } } + public void reset() { + if (initial) { + return; + } + + root.reset(); + correctSegment = segment = root.getFirstChild(); + + if (implRoot != null) { + implRoot.reset(); + implSegment = implRoot.getFirstChild(); + } + + cursor.reset(root, implRoot); + depth = 1; + + segmentExpected = false; + composite = null; + element = null; + + implSegmentSelected = false; + implComposite = null; + implElement = null; + + implSegmentCandidates.clear(); + useErrors.clear(); + elementErrors.clear(); + initial = true; + } + public boolean isPendingDiscrimination() { return !implSegmentCandidates.isEmpty(); } @@ -284,6 +331,7 @@ UsageNode completeLoop(ValidationEventHandler handler, UsageNode node) { } public void validateSegment(ValidationEventHandler handler, CharSequence tag) { + initial = false; segmentExpected = true; implSegmentSelected = false; diff --git a/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamWriterTest.java b/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamWriterTest.java index a4ee024c..12fe18b2 100644 --- a/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamWriterTest.java +++ b/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamWriterTest.java @@ -28,6 +28,9 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; @@ -1203,6 +1206,196 @@ public int read() throws IOException { assertEquals(expected.toString().trim(), result.toString().trim()); } + @Test + void testInputEquivalenceValidatedMultipleX12() throws Exception { + EDIInputFactory inputFactory = EDIInputFactory.newFactory(); + final ByteArrayOutputStream expected = new ByteArrayOutputStream(16384); + Schema control = SchemaUtils.getControlSchema("X12", new String[] { "00401" }); + + SchemaFactory schemaFactory = SchemaFactory.newFactory(); + final InputStream delegate = new BufferedInputStream(getClass().getResourceAsStream("/x12/invoice810_po850_dual.edi")); + + InputStream source = new InputStream() { + @Override + public int read() throws IOException { + int value = delegate.read(); + + if (value != -1) { + expected.write(value); + System.out.write(value); + System.out.flush(); + return value; + } + + return -1; + } + }; + EDIStreamReader reader = inputFactory.createEDIStreamReader(source); + + EDIOutputFactory outputFactory = EDIOutputFactory.newFactory(); + outputFactory.setProperty(EDIOutputFactory.PRETTY_PRINT, true); + ByteArrayOutputStream result = new ByteArrayOutputStream(16384); + EDIStreamWriter writer = outputFactory.createEDIStreamWriter(result); + writer.setControlSchema(control); + + EDIStreamEvent event; + String tag = null; + boolean composite = false; + int componentMod = 0; + int elementMod = 0; + boolean startTxSegment = false; + List elements = new ArrayList<>(); + Map schemas = new HashMap<>(); + + try { + while (reader.hasNext()) { + event = reader.next(); + + switch (event) { + case START_INTERCHANGE: + writer.startInterchange(); + break; + case END_INTERCHANGE: + writer.endInterchange(); + break; + case START_TRANSACTION: + startTxSegment = true; + elements.clear(); + // Continue the loop to avoid segment position comparison for this event type + continue; + case START_SEGMENT: + tag = reader.getText(); + writer.writeStartSegment(tag); + break; + case END_SEGMENT: + if (startTxSegment) { + Schema transaction; + if (!schemas.containsKey(elements.get(0))) { + transaction = schemaFactory.createSchema(getClass().getResource("/x12/EDISchema" + elements.get(0) + + ".xml")); + schemas.put(elements.get(0), transaction); + } else { + transaction = schemas.get(elements.get(0)); + } + + reader.setTransactionSchema(transaction); + writer.setTransactionSchema(transaction); + startTxSegment = false; + } + writer.writeEndSegment(); + break; + case START_COMPOSITE: + if (reader.getLocation().getElementOccurrence() > 1) { + writer.writeRepeatElement(); + } else { + writer.writeStartElement(); + } + composite = true; + break; + case END_COMPOSITE: + writer.endElement(); + composite = false; + break; + case ELEMENT_DATA: + String text = reader.getText(); + + if (startTxSegment) { + elements.add(text); + } + + if (composite) { + if (text == null || text.isEmpty()) { + writer.writeEmptyComponent(); + } else { + switch (++componentMod % 3) { + case 0: + writer.startComponent(); + writer.writeElementData(text); + writer.endComponent(); + break; + case 1: + writer.writeComponent(text); + break; + case 2: + writer.writeComponent(text.toCharArray(), 0, text.length()); + break; + } + } + } else { + if (reader.getLocation().getElementOccurrence() > 1) { + writer.writeRepeatElement(); + writer.writeElementData(text); + writer.endElement(); + } else { + if (text == null || text.isEmpty()) { + writer.writeEmptyElement(); + } else { + switch (++elementMod % 3) { + case 0: + writer.writeStartElement(); + writer.writeElementData(text); + writer.endElement(); + break; + case 1: + writer.writeElement(text); + break; + case 2: + writer.writeElement(text.toCharArray(), 0, text.length()); + break; + } + } + } + } + break; + case ELEMENT_DATA_BINARY: + writer.writeStartElementBinary(); + writer.writeBinaryData(reader.getBinaryData()); + writer.endElement(); + break; + case START_GROUP: + case START_LOOP: + case END_LOOP: + case END_TRANSACTION: + case END_GROUP: + // Ignore control loops + continue; + case SEGMENT_ERROR: + case ELEMENT_OCCURRENCE_ERROR: + case ELEMENT_DATA_ERROR: + System.out.println(event + "[" + reader.getErrorType() + "] error " + reader.getLocation().toString()); + break; + default: + break; + } + + assertEquals(reader.getLocation().getSegmentPosition(), + writer.getLocation().getSegmentPosition(), + () -> "Segment position mismatch writer: " + + writer.getLocation().toString() + + ";\nreader: " + + reader.getLocation().toString()); + assertEquals(reader.getLocation().getElementPosition(), + writer.getLocation().getElementPosition(), + () -> { + return "Element position mismatch writer: " + + writer.getLocation().toString() + + ";\nreader: " + + reader.getLocation().toString(); + }); + assertEquals(reader.getLocation().getElementOccurrence(), + writer.getLocation().getElementOccurrence(), + () -> "Element occurrence mismatch"); + assertEquals(reader.getLocation().getComponentPosition(), + writer.getLocation().getComponentPosition(), + () -> "Component position mismatch"); + } + } finally { + reader.close(); + } + + assertEquals(expected.toString().trim(), result.toString().trim()); + } + @Test void testGetStandardNullDialect() { EDIOutputFactory factory = EDIOutputFactory.newFactory(); diff --git a/src/test/resources/x12/EDISchema810.xml b/src/test/resources/x12/EDISchema810.xml new file mode 100644 index 00000000..cc4dfe0f --- /dev/null +++ b/src/test/resources/x12/EDISchema810.xml @@ -0,0 +1,1407 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/x12/EDISchema850.xml b/src/test/resources/x12/EDISchema850.xml new file mode 100644 index 00000000..6e75f247 --- /dev/null +++ b/src/test/resources/x12/EDISchema850.xml @@ -0,0 +1,1280 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/x12/invoice810_po850_dual.edi b/src/test/resources/x12/invoice810_po850_dual.edi new file mode 100644 index 00000000..9f43116f --- /dev/null +++ b/src/test/resources/x12/invoice810_po850_dual.edi @@ -0,0 +1,77 @@ +ISA*00* *00* *ZZ*SENDERISA *ZZ*RECEIVERISA *960807*1548*U*00401*000000020*0*T*>~ +GS*IN*SENDERDEPT*007326879*19960807*1548*1*X*004010~ +ST*810*000000001~ +BIG*19971211*00001**A99999-01~ +N1*ST*BUYSNACKS PORT*9*1223334445~ +N3*1000 N. SAMPLE HIGHWAY~ +N4*ATHENS*GA*30603~ +N1*BT*BUYSNACKS*9*1223334444~ +N3*P.O. BOX 0000~ +N4*TEMPLE*TX*76503~ +N1*RE*FOODSELLER*9*12345QQQQ~ +N3*P.O. BOX 222222~ +N4*DALLAS*TX*723224444~ +ITD*01*3*1.000**15**16*****1/15 NET 30~ +FOB*PP~ +IT1**16*CA*12.34**UA*002840022222~ +PID*F****CRUNCHY CHIPS LSS~ +IT1**13*CA*12.34**UA*002840033333~ +PID*F****NACHO CHIPS LSS~ +IT1**32*CA*12.34**UA*002840044444~ +PID*F****POTATO CHIPS~ +IT1**51*CA*12.34**UA*002840055555~ +PID*F****CORN CHIPS~ +IT1**9*CA*12.34**UA*002840066666~ +PID*F****BBQ CHIPS~ +IT1**85*CA*12.34**UA*002840077777~ +PID*F****GREAT BIG CHIPS LSS~ +IT1**1*CA*12.34**UA*002840088888~ +PID*F****MINI CHIPS LSS~ +TDS*255438~ +CAD*****FREEFORM~ +ISS*207*CA~ +CTT*7~ +SE*32*000000001~ +ST*810*000000002~ +BIG*19971215*00001**A99999-04~ +N1*ST*BUYER CHIPS*9*1223334445~ +N3*1234 N. BUYER HIGHWAY~ +N4*LOS ANGELES*CA*30603~ +N1*BT*BUYER CHIPS*9*1223334444~ +N3*P.O. BOX 123400~ +N4*CARSON*CA*76503~ +N1*RE*FOODSELLER*9*12345QQQQ~ +N3*P.O. BOX 222222~ +N4*DALLAS*TX*723224444~ +ITD*01*3*1.000**15**16*****1/15 NET 30~ +FOB*PP~ +IT1**50*CA*12.34**UA*002840022222~ +PID*F****CRUNCHY CHIPS LSS~ +IT1**100*CA*12.34**UA*002840066666~ +PID*F****BBQ CHIPS~ +TDS*255438~ +CAD*****FREEFORM~ +ISS*207*CA~ +CTT*2~ +SE*22*000000002~ +GE*2*1~ +GS*PO*9994935230*5566778899*020906*0809*165*X*003010~ +ST*850*000191240~ +BEG*00*NE*S115921858*1017760*040317~ +REF*PD*040209~ +REF*MU*0.3492~ +REF*WH*24~ +FOB*CC~ +TD5*****WORLDWIDE FREIGHT FLEET~ +N1*BY*SHIPPING GROUP, INC.*1*999999999~ +N1*ST*CARGO LIMITED #112*92~ +N3*3000 LONG BEACH DRIVE~ +N4*SAN PEDRO*CA*83308~ +PO1*1*150*EA***IN*02006~ +PO1*2*50*EA***IN*02008~ +PO1*3*25*EA***IN*01019~ +PO1*4*25*EA***IN*01220~ +CTT*4*250~ +SE*17*000191240~ +GE*1*165~ +IEA*2*000000020~ \ No newline at end of file