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 7a8ff80c..65c12936 100644 --- a/src/main/java/io/xlate/edi/internal/schema/SchemaReaderBase.java +++ b/src/main/java/io/xlate/edi/internal/schema/SchemaReaderBase.java @@ -3,6 +3,7 @@ import static io.xlate.edi.internal.schema.StaEDISchemaFactory.schemaException; import static io.xlate.edi.internal.schema.StaEDISchemaFactory.unexpectedElement; import static io.xlate.edi.internal.schema.StaEDISchemaFactory.unexpectedEvent; +import static java.util.stream.Collectors.toList; import java.util.ArrayList; import java.util.Arrays; @@ -15,23 +16,47 @@ import java.util.Objects; import java.util.Set; import java.util.function.Function; +import java.util.stream.IntStream; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; +import io.xlate.edi.schema.EDIComplexType; import io.xlate.edi.schema.EDIReference; import io.xlate.edi.schema.EDISchemaException; import io.xlate.edi.schema.EDISimpleType; +import io.xlate.edi.schema.EDISimpleType.Base; import io.xlate.edi.schema.EDISyntaxRule; import io.xlate.edi.schema.EDIType; +import io.xlate.edi.schema.EDIType.Type; abstract class SchemaReaderBase implements SchemaReader { static final String REFERR_UNDECLARED = "Type %s references undeclared %s with ref='%s'"; static final String REFERR_ILLEGAL = "Type '%s' must not be referenced as '%s' in definition of type '%s'"; + static final EDIReference ANY_ELEMENT_REF_OPT = new Reference(StaEDISchema.ANY_ELEMENT_ID, "element", 0, 1); + static final EDIReference ANY_COMPOSITE_REF_OPT = new Reference(StaEDISchema.ANY_COMPOSITE_ID, "composite", 0, 99); + + static final EDIReference ANY_ELEMENT_REF_REQ = new Reference(StaEDISchema.ANY_ELEMENT_ID, "element", 1, 1); + static final EDIReference ANY_COMPOSITE_REF_REQ = new Reference(StaEDISchema.ANY_COMPOSITE_ID, "composite", 1, 99); + + static final EDISimpleType ANY_ELEMENT = new ElementType(StaEDISchema.ANY_ELEMENT_ID, + Base.STRING, + 0, + 0, + 99_999, + Collections.emptySet()); + + static final EDIComplexType ANY_COMPOSITE = new StructureType(StaEDISchema.ANY_COMPOSITE_ID, + Type.COMPOSITE, + "ANY", + IntStream.rangeClosed(0, 99).mapToObj(i -> ANY_ELEMENT_REF_OPT) + .collect(toList()), + Collections.emptyList()); + final String xmlns; final QName qnSchema; @@ -48,6 +73,7 @@ abstract class SchemaReaderBase implements SchemaReader { final QName qnSequence; final QName qnEnumeration; final QName qnValue; + final QName qnAny; final QName qnCompositeType; final QName qnElementType; @@ -75,6 +101,7 @@ public SchemaReaderBase(String xmlns, XMLStreamReader reader) { qnSequence = new QName(xmlns, "sequence"); qnEnumeration = new QName(xmlns, "enumeration"); qnValue = new QName(xmlns, "value"); + qnAny = new QName(xmlns, "any"); qnCompositeType = new QName(xmlns, "compositeType"); qnElementType = new QName(xmlns, "elementType"); @@ -115,6 +142,9 @@ public String getTransactionName() { public Map readTypes() throws EDISchemaException { Map types = new HashMap<>(100); + types.put(StaEDISchema.ANY_ELEMENT_ID, ANY_ELEMENT); + types.put(StaEDISchema.ANY_COMPOSITE_ID, ANY_COMPOSITE); + try { if (isInterchangeSchema(reader)) { readInterchange(reader, types); @@ -181,7 +211,7 @@ void readInterchange(XMLStreamReader reader, Map types) throws refs.add(headerRef); while (qnSegment.equals(element)) { - refs.add(readReference(reader, types)); + addReferences(reader, EDIType.Type.SEGMENT, refs, readReference(reader, types)); reader.nextTag(); // Advance to end element reader.nextTag(); // Advance to next start element element = reader.getName(); @@ -203,10 +233,10 @@ void readInterchange(XMLStreamReader reader, Map types) throws refs.add(trailerRef); StructureType interchange = new StructureType(qnInterchange.toString(), - EDIType.Type.INTERCHANGE, - "INTERCHANGE", - refs, - Collections.emptyList()); + EDIType.Type.INTERCHANGE, + "INTERCHANGE", + refs, + Collections.emptyList()); types.put(interchange.getId(), interchange); } @@ -214,7 +244,8 @@ void readInterchange(XMLStreamReader reader, Map types) throws Reference readControlStructure(XMLStreamReader reader, QName element, QName subelement, - Map types) throws XMLStreamException { + Map types) + throws XMLStreamException { int minOccurs = 0; int maxOccurs = 99999; String use = parseAttribute(reader, "use", String::valueOf, "optional"); @@ -250,10 +281,10 @@ Reference readControlStructure(XMLStreamReader reader, refs.add(trailerRef); StructureType struct = new StructureType(element.toString(), - complex.get(element), - complex.get(element).toString(), - refs, - Collections.emptyList()); + complex.get(element), + complex.get(element).toString(), + refs, + Collections.emptyList()); types.put(struct.getId(), struct); @@ -326,8 +357,9 @@ void nameCheck(String name, Map types, XMLStreamReader reader) } StructureType readComplexType(XMLStreamReader reader, - QName complexType, - Map types) throws XMLStreamException { + QName complexType, + Map types) + throws XMLStreamException { final EDIType.Type type = complex.get(complexType); final String id; @@ -354,7 +386,7 @@ StructureType readComplexType(XMLStreamReader reader, readDescription(reader); requireElementStart(qnSequence, reader); - readReferences(reader, types, refs); + readReferences(reader, types, type, refs); int event = reader.nextTag(); @@ -374,14 +406,15 @@ StructureType readComplexType(XMLStreamReader reader, } void readReferences(XMLStreamReader reader, - Map types, - List refs) { + Map types, + EDIType.Type parentType, + List refs) { try { while (reader.hasNext()) { switch (reader.next()) { case XMLStreamConstants.START_ELEMENT: - refs.add(readReference(reader, types)); + addReferences(reader, parentType, refs, readReference(reader, types)); break; case XMLStreamConstants.END_ELEMENT: @@ -400,11 +433,42 @@ void readReferences(XMLStreamReader reader, } } + void addReferences(XMLStreamReader reader, EDIType.Type parentType, List refs, Reference reference) { + if ("ANY".equals(reference.getRefId())) { + final EDIReference optRef; + final EDIReference reqRef; + + switch (parentType) { + case SEGMENT: + optRef = ANY_COMPOSITE_REF_OPT; + reqRef = ANY_COMPOSITE_REF_REQ; + break; + case COMPOSITE: + optRef = ANY_ELEMENT_REF_OPT; + reqRef = ANY_ELEMENT_REF_REQ; + break; + default: + throw schemaException("Element " + qnAny + " may only be present for segmentType and compositeType", reader); + } + + final int min = reference.getMinOccurs(); + final int max = reference.getMaxOccurs(); + + for (int i = 0; i < max; i++) { + refs.add(i < min ? reqRef : optRef); + } + } else { + refs.add(reference); + } + } + Reference readReference(XMLStreamReader reader, Map types) { QName element = reader.getName(); String refId = null; - if (references.contains(element)) { + if (qnAny.equals(element)) { + refId = "ANY"; + } else if (references.contains(element)) { refId = readReferencedId(reader); Objects.requireNonNull(refId); } else if (qnLoop.equals(element)) { diff --git a/src/main/java/io/xlate/edi/internal/schema/StaEDISchema.java b/src/main/java/io/xlate/edi/internal/schema/StaEDISchema.java index 69a0ca5e..b3765cc3 100644 --- a/src/main/java/io/xlate/edi/internal/schema/StaEDISchema.java +++ b/src/main/java/io/xlate/edi/internal/schema/StaEDISchema.java @@ -25,7 +25,10 @@ import io.xlate.edi.schema.Schema; import io.xlate.edi.schema.implementation.LoopImplementation; -class StaEDISchema implements Schema { +public class StaEDISchema implements Schema { + + public static final String ANY_ELEMENT_ID = "io.xlate.edi.internal.schema.ANY_ELEMENT"; + public static final String ANY_COMPOSITE_ID = "io.xlate.edi.internal.schema.ANY_COMPOSITE"; final String interchangeName; final String transactionStandardName; 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 58daa70c..bda8f35a 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 @@ -23,6 +23,7 @@ import java.util.List; import java.util.logging.Logger; +import io.xlate.edi.internal.schema.StaEDISchema; import io.xlate.edi.internal.stream.StaEDIStreamLocation; import io.xlate.edi.internal.stream.tokenization.Dialect; import io.xlate.edi.internal.stream.tokenization.ElementDataHandler; @@ -765,7 +766,7 @@ public boolean validCompositeOccurrences(Location position) { } public boolean isComposite() { - return composite != null; + return composite != null && !StaEDISchema.ANY_COMPOSITE_ID.equals(composite.getId()); } public boolean validateElement(Dialect dialect, StaEDIStreamLocation position, CharSequence value) { diff --git a/src/main/resources/schema/EDISchema-v3.xsd b/src/main/resources/schema/EDISchema-v3.xsd index 9e378446..47c12739 100644 --- a/src/main/resources/schema/EDISchema-v3.xsd +++ b/src/main/resources/schema/EDISchema-v3.xsd @@ -77,6 +77,19 @@ + + + + The minimum number of times an undefined element may occur at the declared location in the EDI structure. + + + + + The maximum number of times an undefined element may occur at the declared location in the EDI structure. + + + + @@ -511,9 +524,18 @@ - + - + + + + May be used to declare that any component element may occur in this composite type up + to the maximum number given by maxOccurs. The value of minOccurs defines the number of + undefined component elements that MUST occur in the composite. + + + + @@ -543,6 +565,16 @@ + + + + May be used to declare that any element or composite may occur in this segment type up + to the maximum number given by maxOccurs. The value of minOccurs defines the number of + undefined elements that MUST occur in the segment at this position. Elements that repeat + may occur up to 99 times and are not affected by the value of the maxOccurs attribute. + + + diff --git a/src/test/java/io/xlate/edi/internal/schema/StaEDISchemaFactoryTest.java b/src/test/java/io/xlate/edi/internal/schema/StaEDISchemaFactoryTest.java index d2182357..557104f9 100644 --- a/src/test/java/io/xlate/edi/internal/schema/StaEDISchemaFactoryTest.java +++ b/src/test/java/io/xlate/edi/internal/schema/StaEDISchemaFactoryTest.java @@ -36,12 +36,12 @@ @SuppressWarnings("resource") public class StaEDISchemaFactoryTest { - final String INTERCHANGE_V2 = "{http://xlate.io/EDISchema/v2}interchange"; - final String TRANSACTION_V2 = "{http://xlate.io/EDISchema/v2}transaction"; + final String INTERCHANGE_V2 = '{' + StaEDISchemaFactory.XMLNS_V2 + "}interchange"; + final String TRANSACTION_V2 = '{' + StaEDISchemaFactory.XMLNS_V2 + "}transaction"; - final String INTERCHANGE_V3 = "{http://xlate.io/EDISchema/v3}interchange"; - final String STANDARD_V3 = "{http://xlate.io/EDISchema/v3}transaction"; - final String IMPL_V3 = "{http://xlate.io/EDISchema/v3}implementation"; + final String INTERCHANGE_V3 = '{' + StaEDISchemaFactory.XMLNS_V3 + "}interchange"; + final String STANDARD_V3 = '{' + StaEDISchemaFactory.XMLNS_V3 + "}transaction"; + final String IMPL_V3 = '{' + StaEDISchemaFactory.XMLNS_V3 + "}implementation"; @Test public void testCreateSchemaByURL() throws EDISchemaException { @@ -409,4 +409,86 @@ public void testInvalidSegmentName() { assertEquals("Invalid segment name [sg1]", thrown.getOriginalMessage()); } + + @Test + public void testAnyCompositeType() throws EDISchemaException { + SchemaFactory factory = SchemaFactory.newFactory(); + InputStream stream = new ByteArrayInputStream(("" + + "" + + "" + + "" + + " " + + " " + + " " + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").getBytes()); + Schema schema = factory.createSchema(stream); + EDIComplexType segmentSG1 = (EDIComplexType) schema.getType("SG1"); + assertEquals(3, segmentSG1.getReferences().size()); + // Two "ANY" references refer to the same object + assertTrue(segmentSG1.getReferences().get(1) == segmentSG1.getReferences().get(2)); + } + + @Test + public void testAnyElementType() throws EDISchemaException { + SchemaFactory factory = SchemaFactory.newFactory(); + InputStream stream = new ByteArrayInputStream(("" + + "" + + "" + + "" + + " " + + " " + + " " + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").getBytes()); + Schema schema = factory.createSchema(stream); + EDIComplexType segmentSG6 = (EDIComplexType) schema.getType("SG6"); + assertEquals(1, segmentSG6.getReferences().size()); + // Two "ANY" references refer to the same object + assertEquals("C001", segmentSG6.getReferences().get(0).getReferencedType().getId()); + assertEquals(5, ((EDIComplexType) segmentSG6.getReferences().get(0).getReferencedType()).getReferences().size()); + } + + @Test + public void testAnySegmentTypeInvalid() throws EDISchemaException { + SchemaFactory factory = SchemaFactory.newFactory(); + InputStream stream = new ByteArrayInputStream(("" + + "" + + "" + + "" + + " " + + " " + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").getBytes()); + EDISchemaException thrown = assertThrows(EDISchemaException.class, () -> factory.createSchema(stream)); + assertEquals("Element {" + StaEDISchemaFactory.XMLNS_V3 + "}any may only be present for segmentType and compositeType", + thrown.getOriginalMessage()); + } } diff --git a/src/test/java/io/xlate/edi/internal/stream/ErrorEventsTest.java b/src/test/java/io/xlate/edi/internal/stream/ErrorEventsTest.java index cbdc9cef..fa3dbd1d 100644 --- a/src/test/java/io/xlate/edi/internal/stream/ErrorEventsTest.java +++ b/src/test/java/io/xlate/edi/internal/stream/ErrorEventsTest.java @@ -373,4 +373,244 @@ public void testEmptySegmentSchemaWithData() throws EDISchemaException, EDIStrea assertTrue(reader.hasNext(), "Expected error missing"); assertEquals(EDIStreamValidationError.TOO_MANY_DATA_ELEMENTS, reader.getErrorType()); } + + @Test + public void testExtraAnyElementAllowedInAK1() throws EDISchemaException, EDIStreamException { + EDIInputFactory factory = EDIInputFactory.newFactory(); + InputStream stream = new ByteArrayInputStream(("" + + "ISA*00* *00* *ZZ*ReceiverID *ZZ*Sender *050812*1953*^*00501*508121953*0*P*:~" + + "GS*FA*ReceiverDept*SenderDept*20050812*195335*000005*X*005010X230~" + + "ST*997*0001~" + + "AK1*HC*000001**ANY1*ANY2~" + + "AK9*A*1*1*1~" + + "SE*4*0001~" + + "GE*1*000005~" + + "IEA*1*508121953~").getBytes()); + + EDIStreamReader reader = factory.createEDIStreamReader(stream); + reader = factory.createFilteredReader(reader, (r) -> { + switch (r.getEventType()) { + case SEGMENT_ERROR: + case ELEMENT_DATA_ERROR: + case ELEMENT_OCCURRENCE_ERROR: + case START_TRANSACTION: // To set the schema + case START_COMPOSITE: // To ensure no composites signaled for "any" elements + return true; + default: + break; + } + return false; + }); + + assertEquals(EDIStreamEvent.START_TRANSACTION, reader.next(), "Expecting start of transaction"); + SchemaFactory schemaFactory = SchemaFactory.newFactory(); + URL schemaLocation = getClass().getResource("/x12/EDISchema997_support_any_elements.xml"); + Schema schema = schemaFactory.createSchema(schemaLocation); + reader.setTransactionSchema(schema); + assertFalse(reader.hasNext(), "Unexpected errors in transaction"); + } + + @Test + public void testTooManyExtraAnyElementAllowedInAK1() throws EDISchemaException, EDIStreamException { + EDIInputFactory factory = EDIInputFactory.newFactory(); + InputStream stream = new ByteArrayInputStream(("" + + "ISA*00* *00* *ZZ*ReceiverID *ZZ*Sender *050812*1953*^*00501*508121953*0*P*:~" + + "GS*FA*ReceiverDept*SenderDept*20050812*195335*000005*X*005010X230~" + + "ST*997*0001~" + + "AK1*HC*000001**ANY1*ANY2*ANY3~" + + "AK9*A*1*1*1~" + + "SE*4*0001~" + + "GE*1*000005~" + + "IEA*1*508121953~").getBytes()); + + EDIStreamReader reader = factory.createEDIStreamReader(stream); + reader = factory.createFilteredReader(reader, (r) -> { + switch (r.getEventType()) { + case SEGMENT_ERROR: + case ELEMENT_DATA_ERROR: + case ELEMENT_OCCURRENCE_ERROR: + case START_TRANSACTION: // To set the schema + case START_COMPOSITE: // To ensure no composites signaled for "any" elements + return true; + default: + break; + } + return false; + }); + + assertEquals(EDIStreamEvent.START_TRANSACTION, reader.next(), "Expecting start of transaction"); + SchemaFactory schemaFactory = SchemaFactory.newFactory(); + URL schemaLocation = getClass().getResource("/x12/EDISchema997_support_any_elements.xml"); + Schema schema = schemaFactory.createSchema(schemaLocation); + reader.setTransactionSchema(schema); + + assertTrue(reader.hasNext(), "Expected error missing"); + assertEquals(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR, reader.getEventType()); + assertEquals(EDIStreamValidationError.TOO_MANY_DATA_ELEMENTS, reader.getErrorType()); + assertEquals("AK1", reader.getLocation().getSegmentTag()); + assertEquals(6, reader.getLocation().getElementPosition()); + assertEquals(1, reader.getLocation().getElementOccurrence()); + assertEquals(-1, reader.getLocation().getComponentPosition()); + } + + @Test + public void testNoExtraAnyElementInAK1() throws EDISchemaException, EDIStreamException { + EDIInputFactory factory = EDIInputFactory.newFactory(); + InputStream stream = new ByteArrayInputStream(("" + + "ISA*00* *00* *ZZ*ReceiverID *ZZ*Sender *050812*1953*^*00501*508121953*0*P*:~" + + "GS*FA*ReceiverDept*SenderDept*20050812*195335*000005*X*005010X230~" + + "ST*997*0001~" + + "AK1*HC*000001~" + + "AK9*A*1*1*1~" + + "SE*4*0001~" + + "GE*1*000005~" + + "IEA*1*508121953~").getBytes()); + + EDIStreamReader reader = factory.createEDIStreamReader(stream); + reader = factory.createFilteredReader(reader, (r) -> { + switch (r.getEventType()) { + case SEGMENT_ERROR: + case ELEMENT_DATA_ERROR: + case ELEMENT_OCCURRENCE_ERROR: + case START_TRANSACTION: // To set the schema + case START_COMPOSITE: // To ensure no composites signaled for "any" elements + return true; + default: + break; + } + return false; + }); + + assertEquals(EDIStreamEvent.START_TRANSACTION, reader.next(), "Expecting start of transaction"); + SchemaFactory schemaFactory = SchemaFactory.newFactory(); + URL schemaLocation = getClass().getResource("/x12/EDISchema997_support_any_elements.xml"); + Schema schema = schemaFactory.createSchema(schemaLocation); + reader.setTransactionSchema(schema); + + assertFalse(reader.hasNext(), "Unexpected errors in transaction"); + } + + @Test + public void testCompositesSupportedInAnyElementInAK1() throws EDISchemaException, EDIStreamException { + EDIInputFactory factory = EDIInputFactory.newFactory(); + InputStream stream = new ByteArrayInputStream(("" + + "ISA*00* *00* *ZZ*ReceiverID *ZZ*Sender *050812*1953*^*00501*508121953*0*P*:~" + + "GS*FA*ReceiverDept*SenderDept*20050812*195335*000005*X*005010X230~" + + "ST*997*0001~" + + "AK1*HC*000001**ANY1:ANY2~" + + "AK9*A*1*1*1~" + + "SE*4*0001~" + + "GE*1*000005~" + + "IEA*1*508121953~").getBytes()); + + EDIStreamReader reader = factory.createEDIStreamReader(stream); + reader = factory.createFilteredReader(reader, (r) -> { + switch (r.getEventType()) { + case SEGMENT_ERROR: + case ELEMENT_DATA_ERROR: + case ELEMENT_OCCURRENCE_ERROR: + case START_TRANSACTION: // To set the schema + return true; + default: + break; + } + return false; + }); + + assertEquals(EDIStreamEvent.START_TRANSACTION, reader.next(), "Expecting start of transaction"); + SchemaFactory schemaFactory = SchemaFactory.newFactory(); + URL schemaLocation = getClass().getResource("/x12/EDISchema997_support_any_elements.xml"); + Schema schema = schemaFactory.createSchema(schemaLocation); + reader.setTransactionSchema(schema); + + assertFalse(reader.hasNext(), "Unexpected errors in transaction"); + } + + @Test + public void testRequiredComponentInC030InAnyElementInAK4() throws EDISchemaException, EDIStreamException { + EDIInputFactory factory = EDIInputFactory.newFactory(); + InputStream stream = new ByteArrayInputStream(("" + + "ISA*00* *00* *ZZ*ReceiverID *ZZ*Sender *050812*1953*^*00501*508121953*0*P*:~" + + "GS*FA*ReceiverDept*SenderDept*20050812*195335*000005*X*005010X230~" + + "ST*997*0001~" + + "AK1*HC*000001~" + + "AK2*837*0021~" + + "AK3*NM1*8**8~" + + "AK4*8:::ANYCOMPONENT*66*7*MI~" + + "AK5*R*5~" + + "AK9*R*1*1*0~" + + "SE*4*0001~" + + "GE*1*000005~" + + "IEA*1*508121953~").getBytes()); + + EDIStreamReader reader = factory.createEDIStreamReader(stream); + reader = factory.createFilteredReader(reader, (r) -> { + switch (r.getEventType()) { + case SEGMENT_ERROR: + case ELEMENT_DATA_ERROR: + case ELEMENT_OCCURRENCE_ERROR: + case START_TRANSACTION: // To set the schema + return true; + default: + break; + } + return false; + }); + + assertEquals(EDIStreamEvent.START_TRANSACTION, reader.next(), "Expecting start of transaction"); + SchemaFactory schemaFactory = SchemaFactory.newFactory(); + URL schemaLocation = getClass().getResource("/x12/EDISchema997_support_any_elements.xml"); + Schema schema = schemaFactory.createSchema(schemaLocation); + reader.setTransactionSchema(schema); + + assertFalse(reader.hasNext(), "Unexpected errors in transaction"); + } + + @Test + public void testMissingRequiredComponentInC030InAnyElementInAK4() throws EDISchemaException, EDIStreamException { + EDIInputFactory factory = EDIInputFactory.newFactory(); + InputStream stream = new ByteArrayInputStream(("" + + "ISA*00* *00* *ZZ*ReceiverID *ZZ*Sender *050812*1953*^*00501*508121953*0*P*:~" + + "GS*FA*ReceiverDept*SenderDept*20050812*195335*000005*X*005010X230~" + + "ST*997*0001~" + + "AK1*HC*000001~" + + "AK2*837*0021~" + + "AK3*NM1*8**8~" + + "AK4*8:::*66*7*MI~" + + "AK5*R*5~" + + "AK9*R*1*1*0~" + + "SE*4*0001~" + + "GE*1*000005~" + + "IEA*1*508121953~").getBytes()); + + EDIStreamReader reader = factory.createEDIStreamReader(stream); + reader = factory.createFilteredReader(reader, (r) -> { + switch (r.getEventType()) { + case SEGMENT_ERROR: + case ELEMENT_DATA_ERROR: + case ELEMENT_OCCURRENCE_ERROR: + case START_TRANSACTION: // To set the schema + return true; + default: + break; + } + return false; + }); + + assertEquals(EDIStreamEvent.START_TRANSACTION, reader.next(), "Expecting start of transaction"); + SchemaFactory schemaFactory = SchemaFactory.newFactory(); + URL schemaLocation = getClass().getResource("/x12/EDISchema997_support_any_elements.xml"); + Schema schema = schemaFactory.createSchema(schemaLocation); + reader.setTransactionSchema(schema); + + assertTrue(reader.hasNext(), "Expected error missing"); + assertEquals(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR, reader.getEventType()); + assertEquals(EDIStreamValidationError.REQUIRED_DATA_ELEMENT_MISSING, reader.getErrorType()); + assertEquals("AK4", reader.getLocation().getSegmentTag()); + assertEquals(1, reader.getLocation().getElementPosition()); + assertEquals(1, reader.getLocation().getElementOccurrence()); + assertEquals(4, reader.getLocation().getComponentPosition()); + + assertFalse(reader.hasNext(), "Unexpected errors in transaction"); + } } diff --git a/src/test/resources/x12/EDISchema997_support_any_elements.xml b/src/test/resources/x12/EDISchema997_support_any_elements.xml new file mode 100644 index 00000000..af7930e2 --- /dev/null +++ b/src/test/resources/x12/EDISchema997_support_any_elements.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +