Skip to content

Commit

Permalink
Add method to reset Validator, call at transaction start (read+write)
Browse files Browse the repository at this point in the history
- Only set schema in reader and writer if different from existing value
- Add test with multiple transactions and multiple functional groups
  • Loading branch information
MikeEdgar committed May 27, 2020
1 parent e4fbaea commit 56123b9
Show file tree
Hide file tree
Showing 7 changed files with 3,023 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -284,6 +331,7 @@ UsageNode completeLoop(ValidationEventHandler handler, UsageNode node) {
}

public void validateSegment(ValidationEventHandler handler, CharSequence tag) {
initial = false;
segmentExpected = true;
implSegmentSelected = false;

Expand Down
193 changes: 193 additions & 0 deletions src/test/java/io/xlate/edi/internal/stream/StaEDIStreamWriterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String> elements = new ArrayList<>();
Map<String, Schema> 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();
Expand Down

0 comments on commit 56123b9

Please sign in to comment.