diff --git a/src/main/java/io/xlate/edi/internal/stream/StaEDIFilteredStreamReader.java b/src/main/java/io/xlate/edi/internal/stream/StaEDIFilteredStreamReader.java index b225bc45..2fca429e 100644 --- a/src/main/java/io/xlate/edi/internal/stream/StaEDIFilteredStreamReader.java +++ b/src/main/java/io/xlate/edi/internal/stream/StaEDIFilteredStreamReader.java @@ -211,4 +211,9 @@ public InputStream getBinaryData() { public EDIReference getSchemaTypeReference() { return delegate.getSchemaTypeReference(); } + + @Override + public boolean hasText() { + return delegate.hasText(); + } } 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 92cda8d6..12c240bc 100644 --- a/src/main/java/io/xlate/edi/internal/stream/StaEDIInputFactory.java +++ b/src/main/java/io/xlate/edi/internal/stream/StaEDIInputFactory.java @@ -35,11 +35,13 @@ public class StaEDIInputFactory extends EDIInputFactory { private EDIInputErrorReporter reporter; + @SuppressWarnings("deprecation") 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(EDI_ENABLE_LOOP_TEXT); supportedProperties.add(XML_DECLARE_TRANSACTION_XMLNS); supportedProperties.add(XML_WRAP_TRANSACTION_CONTENTS); 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 7ea912a7..f0affef7 100644 --- a/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamReader.java +++ b/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamReader.java @@ -47,6 +47,8 @@ public class StaEDIStreamReader implements EDIStreamReader, Configurable { private static final Logger LOGGER = Logger.getLogger(StaEDIStreamReader.class.getName()); + private static final CharBuffer GROUP_TEXT = CharBuffer.wrap(ProxyEventHandler.LOOP_CODE_GROUP); + private static final CharBuffer TRANSACTION_TEXT = CharBuffer.wrap(ProxyEventHandler.LOOP_CODE_TRANSACTION); private Schema controlSchema; private final Map properties; @@ -57,6 +59,7 @@ public class StaEDIStreamReader implements EDIStreamReader, Configurable { private boolean complete = false; private boolean closed = false; + private boolean deprecationLogged = false; public StaEDIStreamReader( InputStream stream, @@ -102,9 +105,38 @@ void requireEvent(String message, EDIStreamEvent... events) { throw new IllegalStateException(message); } + private void logDeprecation(EDIStreamEvent event) { + if (!deprecationLogged) { + deprecationLogged = true; + + LOGGER.warning(() -> "DEPRECATION - Retrieving text for event " + event + " will not be supported in a future release. " + + "Use `getReferenceCode` or `getSchemaTypeReference` to retrieve additional information for non-textual event types."); + } + } + private CharBuffer getBuffer() { checkTextState(); - return proxy.getCharacters(); + EDIStreamEvent event = getEventType(); + + switch (event) { + case START_GROUP: + case END_GROUP: + logDeprecation(event); + return GROUP_TEXT; + + case START_TRANSACTION: + case END_TRANSACTION: + logDeprecation(event); + return TRANSACTION_TEXT; + + case START_LOOP: + case END_LOOP: + logDeprecation(event); + return CharBuffer.wrap(proxy.getSchemaTypeReference().getReferencedType().getCode()); + + default: + return proxy.getCharacters(); + } } @Override @@ -187,6 +219,7 @@ private EDIStreamEvent nextEvent() throws EDIStreamException { try { this.setBinaryDataLength(Long.parseLong(getText())); } catch (NumberFormatException e) { + lexer.invalidate(); throw new EDIStreamException("Failed to parse binary element length", location, e); } } @@ -339,28 +372,36 @@ public EDIStreamValidationError getErrorType() { } private void checkTextState() { + if (!hasText()) { + throw new IllegalStateException("not a valid text state [" + getEventType() + ']'); + } + } + + @Override + public boolean hasText() { EDIStreamEvent event = getEventType(); if (event == null) { - throw new IllegalStateException("not a valid text state [" + event + ']'); + return false; } switch (event) { - case START_GROUP: - case START_TRANSACTION: - case START_LOOP: case START_SEGMENT: - case END_GROUP: - case END_TRANSACTION: - case END_LOOP: case END_SEGMENT: case ELEMENT_DATA: case ELEMENT_DATA_ERROR: case ELEMENT_OCCURRENCE_ERROR: case SEGMENT_ERROR: - break; + return true; + case START_GROUP: + case START_TRANSACTION: + case START_LOOP: + case END_GROUP: + case END_TRANSACTION: + case END_LOOP: + return enableLoopText(); default: - throw new IllegalStateException("not a valid text state [" + event + ']'); + return false; } } @@ -477,4 +518,10 @@ boolean ignoreExtraneousCharacters() { boolean nestHierarchicalLoops() { return getProperty(EDIInputFactory.EDI_NEST_HIERARCHICAL_LOOPS, Boolean::parseBoolean, true); } + + @SuppressWarnings("deprecation") + boolean enableLoopText() { + return getProperty(EDIInputFactory.EDI_ENABLE_LOOP_TEXT, Boolean::parseBoolean, true); + } + } diff --git a/src/main/java/io/xlate/edi/internal/stream/StaEDIXMLStreamReader.java b/src/main/java/io/xlate/edi/internal/stream/StaEDIXMLStreamReader.java index b634bc41..6a194666 100644 --- a/src/main/java/io/xlate/edi/internal/stream/StaEDIXMLStreamReader.java +++ b/src/main/java/io/xlate/edi/internal/stream/StaEDIXMLStreamReader.java @@ -35,6 +35,7 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; +import io.xlate.edi.internal.stream.tokenization.ProxyEventHandler; import io.xlate.edi.schema.EDIComplexType; import io.xlate.edi.schema.EDIReference; import io.xlate.edi.stream.EDIInputFactory; @@ -47,7 +48,7 @@ final class StaEDIXMLStreamReader implements XMLStreamReader { private static final Logger LOGGER = Logger.getLogger(StaEDIXMLStreamReader.class.getName()); private static final QName DUMMY_QNAME = new QName("DUMMY"); private static final QName INTERCHANGE = new QName(EDINamespaces.LOOPS, "INTERCHANGE", prefixOf(EDINamespaces.LOOPS)); - private static final QName TRANSACTION = new QName(EDINamespaces.LOOPS, "TRANSACTION", prefixOf(EDINamespaces.LOOPS)); + private static final QName TRANSACTION = new QName(EDINamespaces.LOOPS, ProxyEventHandler.LOOP_CODE_TRANSACTION, prefixOf(EDINamespaces.LOOPS)); private final EDIStreamReader ediReader; private final Map properties; @@ -206,15 +207,14 @@ private void enqueueEvent(EDIStreamEvent ediEvent) throws XMLStreamException { case START_TRANSACTION: withinTransaction = true; - readerText = ediReader.getText(); - name = buildName(elementStack.getFirst(), EDINamespaces.LOOPS, readerText); + name = buildName(elementStack.getFirst(), EDINamespaces.LOOPS, ediReader.getReferenceCode()); enqueueEvent(START_ELEMENT, name, true); determineTransactionSegments(); break; case START_GROUP: case START_LOOP: - name = buildName(elementStack.getFirst(), EDINamespaces.LOOPS, ediReader.getText()); + name = buildName(elementStack.getFirst(), EDINamespaces.LOOPS, ediReader.getReferenceCode()); enqueueEvent(START_ELEMENT, name, true); break; diff --git a/src/main/java/io/xlate/edi/internal/stream/json/StaEDIJsonParser.java b/src/main/java/io/xlate/edi/internal/stream/json/StaEDIJsonParser.java index f5a8661f..3bed2701 100644 --- a/src/main/java/io/xlate/edi/internal/stream/json/StaEDIJsonParser.java +++ b/src/main/java/io/xlate/edi/internal/stream/json/StaEDIJsonParser.java @@ -231,7 +231,7 @@ void enqueueEvent(EDIStreamEvent ediEvent) { case START_GROUP: case START_TRANSACTION: case START_LOOP: - enqueueStructureBegin("loop", ediReader.getText()); + enqueueStructureBegin("loop", ediReader.getReferenceCode()); break; case START_SEGMENT: enqueueStructureBegin("segment", ediReader.getText()); diff --git a/src/main/java/io/xlate/edi/internal/stream/tokenization/Lexer.java b/src/main/java/io/xlate/edi/internal/stream/tokenization/Lexer.java index c0f2b808..c13ef60c 100644 --- a/src/main/java/io/xlate/edi/internal/stream/tokenization/Lexer.java +++ b/src/main/java/io/xlate/edi/internal/stream/tokenization/Lexer.java @@ -145,6 +145,13 @@ public Dialect getDialect() { return dialect; } + public void invalidate() { + if (state != State.INVALID) { + previous = state; + state = State.INVALID; + } + } + public void setBinaryLength(long binaryLength) { this.binaryRemain = binaryLength; @@ -604,4 +611,14 @@ void popMode(Mode expected) throws EDIException { throw error(EDIException.INVALID_STATE); } } + + /* test */ + State currentState() { + return state; + } + + /* test */ + State previousState() { + return previous; + } } 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 fc42dea1..73c10b82 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 @@ -40,6 +40,9 @@ public class ProxyEventHandler implements EventHandler { + public static final String LOOP_CODE_GROUP = EDIType.Type.GROUP.toString(); + public static final String LOOP_CODE_TRANSACTION = EDIType.Type.TRANSACTION.toString(); + private final StaEDIStreamLocation location; private final boolean nestHierarchicalLoops; @@ -200,17 +203,17 @@ public void interchangeEnd() { public void loopBegin(EDIReference typeReference) { final String loopCode = typeReference.getReferencedType().getCode(); - if (EDIType.Type.TRANSACTION.toString().equals(loopCode)) { + if (LOOP_CODE_TRANSACTION.equals(loopCode)) { transaction = true; transactionSchemaAllowed = true; - enqueueEvent(EDIStreamEvent.START_TRANSACTION, EDIStreamValidationError.NONE, loopCode, typeReference, location); + enqueueEvent(EDIStreamEvent.START_TRANSACTION, EDIStreamValidationError.NONE, null, typeReference, location); if (transactionValidator != null) { transactionValidator.reset(); } - } else if (EDIType.Type.GROUP.toString().equals(loopCode)) { - enqueueEvent(EDIStreamEvent.START_GROUP, EDIStreamValidationError.NONE, loopCode, typeReference, location); + } else if (LOOP_CODE_GROUP.equals(loopCode)) { + enqueueEvent(EDIStreamEvent.START_GROUP, EDIStreamValidationError.NONE, null, typeReference, location); } else { - enqueueEvent(EDIStreamEvent.START_LOOP, EDIStreamValidationError.NONE, loopCode, typeReference, location); + enqueueEvent(EDIStreamEvent.START_LOOP, EDIStreamValidationError.NONE, null, typeReference, location); if (nestHierarchicalLoops && isHierarchicalLoop(typeReference.getReferencedType())) { EDILoopType loop = (EDILoopType) typeReference.getReferencedType(); @@ -229,17 +232,17 @@ public void loopEnd(EDIReference typeReference) { // Validator can not be null when a loopEnd event has been signaled. validator().validateLoopSyntax(this); - if (EDIType.Type.TRANSACTION.toString().equals(loopCode)) { + if (LOOP_CODE_TRANSACTION.equals(loopCode)) { transaction = false; dialect.transactionEnd(); - enqueueEvent(EDIStreamEvent.END_TRANSACTION, EDIStreamValidationError.NONE, loopCode, typeReference, location); - } else if (EDIType.Type.GROUP.toString().equals(loopCode)) { + enqueueEvent(EDIStreamEvent.END_TRANSACTION, EDIStreamValidationError.NONE, null, typeReference, location); + } else if (LOOP_CODE_GROUP.equals(loopCode)) { dialect.groupEnd(); - enqueueEvent(EDIStreamEvent.END_GROUP, EDIStreamValidationError.NONE, loopCode, typeReference, location); + enqueueEvent(EDIStreamEvent.END_GROUP, EDIStreamValidationError.NONE, null, typeReference, location); } else if (nestHierarchicalLoops && isHierarchicalLoop(typeReference.getReferencedType())) { levelCheckPending = true; } else { - enqueueEvent(EDIStreamEvent.END_LOOP, EDIStreamValidationError.NONE, loopCode, typeReference, location); + enqueueEvent(EDIStreamEvent.END_LOOP, EDIStreamValidationError.NONE, null, typeReference, location); } } diff --git a/src/main/java/io/xlate/edi/stream/EDIInputFactory.java b/src/main/java/io/xlate/edi/stream/EDIInputFactory.java index c219ca59..1f85f807 100644 --- a/src/main/java/io/xlate/edi/stream/EDIInputFactory.java +++ b/src/main/java/io/xlate/edi/stream/EDIInputFactory.java @@ -66,15 +66,33 @@ public abstract class EDIInputFactory extends PropertySupport { * 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, functional group, transaction, and loop start/end + * events will allow for {@link EDIStreamReader#getText()} to be called, + * which is the legacy behavior. + * + * The default value is `true` and this property is deprecated. In the next + * major release, the property's default value will be `false`. + * + * Default value: true + * + * @since 1.20 + * @deprecated use {@link EDIStreamReader#getReferenceCode()} and + * {@link EDIStreamReader#getSchemaTypeReference()} to retrieve + * additional information for non-textual event types. + */ + @Deprecated + public static final String EDI_ENABLE_LOOP_TEXT = "io.xlate.edi.stream.EDI_ENABLE_LOOP_TEXT"; //NOSONAR + /** * 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/java/io/xlate/edi/stream/EDIStreamReader.java b/src/main/java/io/xlate/edi/stream/EDIStreamReader.java index 96dd24f0..2f9aeb64 100644 --- a/src/main/java/io/xlate/edi/stream/EDIStreamReader.java +++ b/src/main/java/io/xlate/edi/stream/EDIStreamReader.java @@ -282,23 +282,37 @@ public interface EDIStreamReader extends Closeable, EDIStreamConstants { /** * Returns the current value of the parse event as a string. This returns - * the string value of an ELEMENT_DATA event, and the string value of a - * segment tag in a START_SEGMENT event. During an ELEMENT_ERROR event, this - * contains the invalid element (when available). * - * @return the current text or null + * + * + * @return the current text or an empty {@link java.lang.String String} * @throws IllegalStateException * if this state is not a valid text state */ String getText(); /** - * Returns an array which contains the characters from this event. This - * array should be treated as read-only and transient. I.e. the array will - * contain the text characters until the EDIStreamReader moves on to the - * next event. Attempts to hold onto the character array beyond that time or - * modify the contents of the array are breaches of the contract for this - * interface. + * Returns an array which contains the characters from this event (as + * specified by {@link #getText}). This array should be treated as read-only + * and transient. I.e. the array will contain the text characters until the + * EDIStreamReader moves on to the next event. Attempts to hold onto the + * character array beyond that time or modify the contents of the array are + * breaches of the contract for this interface. * * @return the current text or an empty array * @throws IllegalStateException @@ -307,16 +321,16 @@ public interface EDIStreamReader extends Closeable, EDIStreamConstants { char[] getTextCharacters(); /** - * Gets the the text associated with a ELEMENT_DATA, ELEMENT_ERROR, - * START_SEGMENT, or END_SEGMENT event. Text starting at "sourceStart" is - * copied into "target" starting at "targetStart". Up to "length" characters - * are copied. The number of characters actually copied is returned. The - * "sourceStart" argument must be greater or equal to 0 and less than or - * equal to the number of characters associated with the event. Usually, one - * requests text starting at a "sourceStart" of 0. If the number of - * characters actually copied is less than the "length", then there is no - * more text. Otherwise, subsequent calls need to be made until all text has - * been retrieved. + * Returns the the text associated with an event (as specified by + * {@link #getText}). Text starting at "sourceStart" is copied into "target" + * starting at "targetStart". Up to "length" characters are copied. The + * number of characters actually copied is returned. The "sourceStart" + * argument must be greater or equal to 0 and less than or equal to the + * number of characters associated with the event. Usually, one requests + * text starting at a "sourceStart" of 0. If the number of characters + * actually copied is less than the "length", then there is no more text. + * Otherwise, subsequent calls need to be made until all text has been + * retrieved. * * For example: * @@ -446,4 +460,21 @@ int getTextCharacters(int sourceStart, * @since 1.9 */ EDIReference getSchemaTypeReference(); + + /** + * Return true if the current event has text, false otherwise. The following + * events have text: + * + * + * @return true if the current event has text, false otherwise + * @since 1.20 + */ + boolean hasText(); } diff --git a/src/test/java/io/xlate/edi/internal/stream/SegmentValidationTest.java b/src/test/java/io/xlate/edi/internal/stream/SegmentValidationTest.java index dacd09ed..69c65907 100644 --- a/src/test/java/io/xlate/edi/internal/stream/SegmentValidationTest.java +++ b/src/test/java/io/xlate/edi/internal/stream/SegmentValidationTest.java @@ -475,9 +475,11 @@ void testImplementationValidSequence() throws EDISchemaException, EDIStreamExcep assertTrue(!reader.hasNext(), "Unexpected segment errors exist"); } + @SuppressWarnings("deprecation") @Test void testImplementationValidAlternateSequence() throws EDISchemaException, EDIStreamException { EDIInputFactory factory = EDIInputFactory.newFactory(); + factory.setProperty(EDIInputFactory.EDI_ENABLE_LOOP_TEXT, "false"); InputStream stream = new ByteArrayInputStream(("" + "ISA*00* *00* *ZZ*ReceiverID *ZZ*Sender *050812*1953*^*00501*508121953*0*P*:~" + "S01*X~" @@ -510,22 +512,22 @@ void testImplementationValidAlternateSequence() throws EDISchemaException, EDISt reader.setTransactionSchema(schemaFactory.createSchema(getClass().getResource("/x12/EDISchemaSegmentValidationImpl.xml"))); List expected = Arrays.asList( - StaEDITestEvent.forEvent(EDIStreamEvent.START_TRANSACTION, "TRANSACTION", "TRANSACTION"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_TRANSACTION, null, "TRANSACTION"), // Loop A - StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, "L0000", "0000A"), - StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, "L0000", "0000A"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, null, "0000A"), + StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, null, "0000A"), // Loop C - Occurrence 1 - StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, "L0000", "0000C"), - StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, "L0000", "0000C"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, null, "0000C"), + StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, null, "0000C"), // Loop B - StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, "L0000", "0000B"), - StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, "L0000", "0000B"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, null, "0000B"), + StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, null, "0000B"), // Loop D - StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, "L0000", "0000D"), - StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, "L0000", "0000D"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, null, "0000D"), + StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, null, "0000D"), // Loop C - Occurrence 2 - StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, "L0000", "0000C"), - StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, "L0000", "0000C"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, null, "0000C"), + StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, null, "0000C"), StaEDITestEvent.forError(EDIStreamValidationError.MANDATORY_SEGMENT_MISSING, "S20", "S20")); @@ -588,9 +590,11 @@ void testImplementationValidSequenceAllMissing() throws EDISchemaException, EDIS assertTrue(!reader.hasNext(), "Unexpected segment errors exist"); } + @SuppressWarnings("deprecation") @Test void testImplementationValidSequenceWithCompositeDiscr() throws EDISchemaException, EDIStreamException { EDIInputFactory factory = EDIInputFactory.newFactory(); + factory.setProperty(EDIInputFactory.EDI_ENABLE_LOOP_TEXT, "false"); SchemaFactory schemaFactory = SchemaFactory.newFactory(); InputStream stream = new ByteArrayInputStream(("" + "ISA*00* *00* *ZZ*ReceiverID *ZZ*Sender *050812*1953*^*00501*508121953*0*P*:~" @@ -622,18 +626,18 @@ void testImplementationValidSequenceWithCompositeDiscr() throws EDISchemaExcepti reader.setTransactionSchema(schemaFactory.createSchema(getClass().getResource("/x12/EDISchemaSegmentValidationImpl2.xml"))); List expected = Arrays.asList( - StaEDITestEvent.forEvent(EDIStreamEvent.START_TRANSACTION, "TRANSACTION", "TRANSACTION"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_TRANSACTION, null, "TRANSACTION"), // Loop A - StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, "L0000", "0000A"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, null, "0000A"), // Loop AXX - StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, "L0002", "0002AXX"), - StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, "L0002", "0002AXX"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, null, "0002AXX"), + StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, null, "0002AXX"), // Loop AYY - StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, "L0002", "0002AYY"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, null, "0002AYY"), StaEDITestEvent.forError(EDIStreamValidationError.SEGMENT_EXCEEDS_MAXIMUM_USE, "S31", "S31"), StaEDITestEvent.forError(EDIStreamValidationError.IMPLEMENTATION_SEGMENT_BELOW_MINIMUM_USE, "S31", "S31B"), - StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, "L0002", "0002AYY"), - StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, "L0000", "0000A")); + StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, null, "0002AYY"), + StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, null, "0000A")); List events = new ArrayList<>(); events.add(StaEDITestEvent.from(reader, false)); @@ -645,9 +649,11 @@ void testImplementationValidSequenceWithCompositeDiscr() throws EDISchemaExcepti assertEquals(expected, events); } + @SuppressWarnings("deprecation") @Test void testImplementation_Only_BHT_HL_Valid() throws EDISchemaException, EDIStreamException { EDIInputFactory factory = EDIInputFactory.newFactory(); + factory.setProperty(EDIInputFactory.EDI_ENABLE_LOOP_TEXT, "false"); SchemaFactory schemaFactory = SchemaFactory.newFactory(); InputStream stream = getClass().getResourceAsStream("/x12/sample837-small.edi"); @@ -664,24 +670,24 @@ void testImplementation_Only_BHT_HL_Valid() throws EDISchemaException, EDIStream reader.setTransactionSchema(schemaFactory.createSchema(getClass().getResource("/x12/005010X222/837.xml"))); List expected = Arrays.asList( - StaEDITestEvent.forEvent(EDIStreamEvent.START_TRANSACTION, "TRANSACTION", "TRANSACTION"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_TRANSACTION, null, "TRANSACTION"), // Occurrence 1 - StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, "L0001", "L0001"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, null, "L0001"), StaEDITestEvent.forError(EDIStreamValidationError.IMPLEMENTATION_UNUSED_SEGMENT_PRESENT, "NM1", "NM1"), StaEDITestEvent.forError(EDIStreamValidationError.IMPLEMENTATION_UNUSED_SEGMENT_PRESENT, "PER", "PER"), - StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, "L0001", "L0001"), + StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, null, "L0001"), // Occurrence 2 - StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, "L0001", "L0001"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, null, "L0001"), StaEDITestEvent.forError(EDIStreamValidationError.IMPLEMENTATION_UNUSED_SEGMENT_PRESENT, "NM1", "NM1"), - StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, "L0001", "L0001"), + StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, null, "L0001"), // Loop 2010A - StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, "L0002", "2010A"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, null, "2010A"), StaEDITestEvent.forError(EDIStreamValidationError.IMPLEMENTATION_UNUSED_SEGMENT_PRESENT, "PRV", "PRV"), - StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, "L0002", "2010A"), - StaEDITestEvent.forEvent(EDIStreamEvent.START_TRANSACTION, "TRANSACTION", "TRANSACTION"), + StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, null, "2010A"), + StaEDITestEvent.forEvent(EDIStreamEvent.START_TRANSACTION, null, "TRANSACTION"), // Loop 2010A - StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, "L0002", "2010A"), - StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, "L0002", "2010A")); + StaEDITestEvent.forEvent(EDIStreamEvent.START_LOOP, null, "2010A"), + StaEDITestEvent.forEvent(EDIStreamEvent.END_LOOP, null, "2010A")); List events = new ArrayList<>(); events.add(StaEDITestEvent.from(reader, false)); 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 52ab6340..2f191131 100644 --- a/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamReaderTest.java +++ b/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamReaderTest.java @@ -1215,7 +1215,6 @@ void testGetBinaryDataInvalid() throws Exception { EDIStreamReader reader = factory.createEDIStreamReader(stream); EDIStreamEvent event; - String tag = null; EDIStreamException thrown = null; try { @@ -1226,9 +1225,7 @@ void testGetBinaryDataInvalid() throws Exception { break; } - tag = reader.getText(); - - if ("BIN".equals(tag)) { + if (reader.getEventType() == EDIStreamEvent.START_SEGMENT && "BIN".equals(reader.getText())) { reader.next(); long binaryDataLength = Long.parseLong(reader.getText()); assertEquals(1839, binaryDataLength); @@ -1255,33 +1252,33 @@ void testGetBinaryDataInvalidLength() throws Exception { Schema schema = schemaFactory.createSchema(getClass().getResource("/x12/EDISchemaBinarySegment.xml")); EDIStreamEvent event; - String tag = null; - EDIStreamException thrown = null; + EDIStreamException bin01ParseException = null; + EDIStreamException inconsistentParser = null; try { while (reader.hasNext()) { try { - reader.nextTag(); + event = reader.nextTag(); } catch (NoSuchElementException e) { break; } - if (reader.getEventType() == EDIStreamEvent.START_TRANSACTION) { + if (event == EDIStreamEvent.START_TRANSACTION) { reader.setTransactionSchema(schema); assertThrows(IllegalStateException.class, () -> reader.setBinaryDataLength(1L)); - } else { - tag = reader.getText(); - - if ("BIN".equals(tag)) { - thrown = assertThrows(EDIStreamException.class, () -> reader.nextTag()); - break; - } + } else if (event == EDIStreamEvent.START_SEGMENT && "BIN".equals(reader.getText())) { + // BIN01 is non-numeric + bin01ParseException = assertThrows(EDIStreamException.class, () -> reader.nextTag()); + inconsistentParser = assertThrows(EDIStreamException.class, () -> reader.nextTag()); + break; } } } finally { reader.close(); } - assertNotNull(thrown); + + assertNotNull(bin01ParseException); + assertNotNull(inconsistentParser); } @Test @@ -1295,7 +1292,6 @@ void testGetBinaryDataValid() EDIStreamReader reader = factory.createEDIStreamReader(stream); EDIStreamEvent event; - String tag = null; try { while (reader.hasNext()) { @@ -1305,9 +1301,7 @@ void testGetBinaryDataValid() break; } - tag = reader.getText(); - - if ("BIN".equals(tag)) { + if (reader.getEventType() == EDIStreamEvent.START_SEGMENT && "BIN".equals(reader.getText())) { reader.next(); long binaryDataLength = Long.parseLong(reader.getText()); assertEquals(2768, binaryDataLength); diff --git a/src/test/java/io/xlate/edi/internal/stream/tokenization/LexerTest.java b/src/test/java/io/xlate/edi/internal/stream/tokenization/LexerTest.java index bfad343e..0c8ebb57 100644 --- a/src/test/java/io/xlate/edi/internal/stream/tokenization/LexerTest.java +++ b/src/test/java/io/xlate/edi/internal/stream/tokenization/LexerTest.java @@ -16,6 +16,7 @@ package io.xlate.edi.internal.stream.tokenization; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -392,4 +393,21 @@ void testUnmappabledCharacter() throws EDIException, IOException { MalformedInputException thrown = assertThrows(MalformedInputException.class, lexer::parse); assertEquals("Input length = 1", thrown.getMessage()); } + + @Test + void testPreviousStateRetainedWhenInvalidateInvoked() { + InputStream stream = new ByteArrayInputStream(new byte[0]); + TestLexerEventHandler eventHandler = new TestLexerEventHandler(); + final StaEDIStreamLocation location = new StaEDIStreamLocation(); + final Lexer lexer = new Lexer(stream, StandardCharsets.US_ASCII, eventHandler, location, false); + + assertEquals(State.INITIAL, lexer.currentState()); + assertNull(lexer.previousState()); + + for (int i = 0; i < 2; i++) { + lexer.invalidate(); + assertEquals(State.INVALID, lexer.currentState()); + assertEquals(State.INITIAL, lexer.previousState()); + } + } } diff --git a/src/test/java/io/xlate/edi/test/StaEDITestEvent.java b/src/test/java/io/xlate/edi/test/StaEDITestEvent.java index ab920de1..565b361f 100644 --- a/src/test/java/io/xlate/edi/test/StaEDITestEvent.java +++ b/src/test/java/io/xlate/edi/test/StaEDITestEvent.java @@ -41,7 +41,7 @@ public static StaEDITestEvent from(EDIStreamReader reader, boolean includeLocati return new StaEDITestEvent(reader.getEventType(), error ? reader.getErrorType() : null, - reader.getText(), + reader.hasText() ? reader.getText() : null, reader.getReferenceCode(), includeLocation ? reader.getLocation().copy() : null); }