Skip to content

Commit

Permalink
Merge pull request #26 from xlate/25_writer_get_standard
Browse files Browse the repository at this point in the history
Add `getStandard` method to `EDIStreamWriter`
  • Loading branch information
MikeEdgar authored May 21, 2020
2 parents 28c5e7d + ead5b7d commit 0045758
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 35 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,16 @@ jobs:
run: mvn -B verify javadoc:javadoc --file pom.xml

quality:
needs: [build, javadoc]
if: github.event_name == 'push' && github.repository == 'xlate/staedi'
runs-on: ubuntu-latest
name: Verify Quality

steps:
- uses: actions/checkout@v2
with:
# Disabling shallow clone is recommended for improving relevancy of reporting
fetch-depth: 0

- uses: actions/setup-java@v1.3.0
with:
java-version: 11
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>io.xlate</groupId>
<artifactId>staedi</artifactId>
<version>1.6.1-SNAPSHOT</version>
<version>1.7.0-SNAPSHOT</version>

<name>StAEDI : Streaming API for EDI for Java</name>
<description>Streaming API for EDI for Java</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public EDIStreamEvent getEventType() {

@Override
public String getStandard() {
if (lexer.getDialect() == null || lexer.getDialect().getStandard() == null) {
if (lexer.getDialect() == null) {
throw new IllegalStateException("standard not accessible");
}

Expand Down
18 changes: 12 additions & 6 deletions src/main/java/io/xlate/edi/internal/stream/StaEDIStreamWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ static char getDelimiter(Map<String, Object> properties, String key, Supplier<Ch
}

private static void ensureArgs(int arrayLength, int start, int end) {
if (start < 0 || start > arrayLength || end > arrayLength) {
if (start < 0 || start >= arrayLength || end > arrayLength) {
throw new IndexOutOfBoundsException();
}

Expand Down Expand Up @@ -205,6 +205,15 @@ public Location getLocation() {
return location;
}

@Override
public String getStandard() {
if (dialect == null) {
throw new IllegalStateException("standard not accessible");
}

return dialect.getStandard();
}

private Validator validator() {
// Do not use the transactionValidator in the period where it may be set/mutated by the user
return transaction && !transactionSchemaAllowed ? transactionValidator : controlValidator;
Expand All @@ -216,10 +225,7 @@ private void write(int output) throws EDIStreamException {
clazz = characters.getClass(output);

if (clazz == CharacterClass.INVALID) {
StringBuilder message = new StringBuilder();
message.append("Invalid character: 0x");
message.append(Integer.toHexString(output));
throw new EDIException(message.toString());
throw new EDIStreamException(String.format("Invalid character: 0x%04X", output), location);
}

state = state.transition(clazz);
Expand Down Expand Up @@ -251,7 +257,7 @@ private void write(int output) throws EDIStreamException {
}
}
} else {
throw new EDIException("Unexpected header character: '" + (char) output + "'");
throw new EDIStreamException(String.format("Unexpected header character: 0x%04X [%s]", output, (char) output), location);
}
break;
case INVALID:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,11 +226,6 @@ private void enqueueEvent(EDIStreamEvent ediEvent) throws XMLStreamException {
this.location);

case ELEMENT_OCCURRENCE_ERROR:
throw new XMLStreamException(String.format("Element %s has error %s",
ediReader.getText(),
ediReader.getErrorType()),
this.location);

case ELEMENT_DATA_ERROR:
throw new XMLStreamException(String.format("Element %s has error %s",
ediReader.getText(),
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/xlate/edi/stream/EDIStreamException.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ private static String displayLocation(Location location) {
display.append(", element ");
display.append(String.valueOf(location.getElementPosition()));

if (location.getElementOccurrence() > -1) {
display.append("(occurrence ");
if (location.getElementOccurrence() > 1) {
display.append(" (occurrence ");
display.append(String.valueOf(location.getElementOccurrence()));
display.append(')');
}
Expand Down
39 changes: 28 additions & 11 deletions src/main/java/io/xlate/edi/stream/EDIStreamWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ public interface EDIStreamWriter {
* Calls to this method are only valid before the interchange is started.
*
* @param controlSchema
* the schema instance to use for validation of control structures
* the schema instance to use for validation of control
* structures
* @throws IllegalStateException
* when the writer is not in its initial state
*
Expand All @@ -71,32 +72,48 @@ public interface EDIStreamWriter {
* <p>
* Sets the schema to be used for validation of the business transaction for
* this stream writer. This schema will be used to validate only the
* contents of a transaction/message, <em>not including</em> the begin/end control
* structures.
* contents of a transaction/message, <em>not including</em> the begin/end
* control structures.
* <p>
* This method may be called at any time. However, when non-null, the writer will make
* use of the transaction schema for output validation. It is the responsibility of the
* caller to set the transaction schema to null at the end of the business transaction.
* This method may be called at any time. However, when non-null, the writer
* will make use of the transaction schema for output validation. It is the
* responsibility of the caller to set the transaction schema to null at the
* end of the business transaction.
*
* @param transactionSchema
* the schema instance to use for validation of business transaction structures
* the schema instance to use for validation of business
* transaction structures
*
* @since 1.1
*/
void setTransactionSchema(Schema transactionSchema);

/**
* Return the current location of the writer. If the Location is unknown
* the processor should return an implementation of Location that returns -1
* for the location values. The location information is only valid until
* the next item is written to the output.
* Return the current location of the writer. If the Location is unknown the
* processor should return an implementation of Location that returns -1 for
* the location values. The location information is only valid until the
* next item is written to the output.
*
* @return current location of the writer
*
* @since 1.1
*/
Location getLocation();

/**
* Get the EDI standard name. Calls to this method are only valid when the
* interchange type has been determined, after the full interchange header
* segment has been written.
*
* @return the name of the EDI standard
* @throws IllegalStateException
* when the standard has not yet been determined, prior to the
* start of an interchange header segment being fully written
*
* @since 1.7
*/
String getStandard();

EDIStreamWriter startInterchange() throws EDIStreamException;

EDIStreamWriter endInterchange() throws EDIStreamException;
Expand Down
151 changes: 143 additions & 8 deletions src/test/java/io/xlate/edi/internal/stream/StaEDIStreamWriterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;

Expand All @@ -31,6 +32,7 @@
import java.util.Map;

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import io.xlate.edi.internal.schema.SchemaUtils;
import io.xlate.edi.schema.EDISchemaException;
Expand All @@ -45,6 +47,7 @@
import io.xlate.edi.stream.EDIStreamValidationError;
import io.xlate.edi.stream.EDIStreamWriter;
import io.xlate.edi.stream.EDIValidationException;
import io.xlate.edi.stream.Location;

@SuppressWarnings("resource")
class StaEDIStreamWriterTest {
Expand Down Expand Up @@ -158,6 +161,28 @@ void testWriteStartSegment() throws EDIStreamException {
assertEquals("ISA", stream.toString());
}

@Test
void testWriteInvalidHeaderElement() throws EDIStreamException {
EDIOutputFactory factory = EDIOutputFactory.newFactory();
OutputStream stream = new ByteArrayOutputStream(4096);
EDIStreamWriter writer = factory.createEDIStreamWriter(stream);
writer.startInterchange();
writer.writeStartSegment("ISA");
writer.writeElement("00").writeElement(" "); // Too long
writer.writeElement("00").writeElement(" "); // Too long
writer.writeElement("ZZ").writeElement("ReceiverID ");
writer.writeElement("ZZ").writeElement("Sender ");
writer.writeElement("050812");
writer.writeElement("1953");
writer.writeElement("^");
writer.writeElement("00501");
writer.writeElement("508121953");
writer.writeElement("0");
writer.writeElement("P");
EDIStreamException thrown = assertThrows(EDIStreamException.class, () -> writer.writeElement(":"));
assertEquals("Unexpected header character: 0x002A [*] in segment ISA at position 1, element 15", thrown.getMessage());
}

@Test
void testWriteStartSegmentIllegal() throws EDIStreamException {
EDIOutputFactory factory = EDIOutputFactory.newFactory();
Expand Down Expand Up @@ -220,6 +245,55 @@ void testWriteStartElementIllegal() throws EDIStreamException {
assertThrows(IllegalStateException.class, () -> writer.writeStartElement());
}

@Test
void testWriteInvalidCharacter() throws EDIStreamException {
EDIOutputFactory factory = EDIOutputFactory.newFactory();
OutputStream stream = new ByteArrayOutputStream(4096);
EDIStreamWriter writer = factory.createEDIStreamWriter(stream);
writer.startInterchange();
writer.writeStartSegment("ISA");
writer.writeStartElement();
EDIStreamException thrown = assertThrows(EDIStreamException.class,
() -> writer.writeElementData("\u0008\u0010"));
assertEquals("Invalid character: 0x0008 in segment ISA at position 1, element 1", thrown.getMessage());
}

@Test
void testWriteInvalidCharacterRepeatedComposite() throws EDIStreamException {
EDIOutputFactory factory = EDIOutputFactory.newFactory();
OutputStream stream = new ByteArrayOutputStream(4096);
EDIStreamWriter writer = factory.createEDIStreamWriter(stream);
writer.startInterchange();
writeHeader(writer);
writer.writeStartSegment("FOO");
writer.writeElement("BAR1");
writer.writeRepeatElement(); // starts new element
writer.writeComponent("BAR2");
writer.writeComponent("BAR3");
EDIStreamException thrown = assertThrows(EDIStreamException.class,
() -> writer.writeComponent("\u0008\u0010"));
assertEquals("Invalid character: 0x0008 in segment FOO at position 2, element 1 (occurrence 2), component 3", thrown.getMessage());
Location l = thrown.getLocation();
assertEquals("FOO", l.getSegmentTag());
assertEquals(2, l.getSegmentPosition());
assertEquals(1, l.getElementPosition());
assertEquals(2, l.getElementOccurrence());
assertEquals(3, l.getComponentPosition());
}

@Test
void testWriteInvalidSegmentTag() throws EDIStreamException {
EDIOutputFactory factory = EDIOutputFactory.newFactory();
OutputStream stream = new ByteArrayOutputStream(4096);
EDIStreamWriter writer = factory.createEDIStreamWriter(stream);
writer.startInterchange();
writeHeader(writer);
writer.writeStartSegment("G");
EDIStreamException thrown = assertThrows(EDIStreamException.class,
() -> writer.writeElement("FOO"));
assertEquals("Invalid state: INVALID; output 0x002A", thrown.getMessage());
}

@Test
void testWriteStartElementBinary() throws IllegalStateException, EDIStreamException {
EDIOutputFactory factory = EDIOutputFactory.newFactory();
Expand Down Expand Up @@ -357,6 +431,21 @@ void testWriteElementDataCharArray() throws EDIStreamException {
assertEquals("ISA*CHARS~", stream.toString());
}

@Test
void testWriteElementDataCharInvalidBoundaries() throws EDIStreamException {
EDIOutputFactory factory = EDIOutputFactory.newFactory();
OutputStream stream = new ByteArrayOutputStream(4096);
EDIStreamWriter writer = factory.createEDIStreamWriter(stream);
writer.startInterchange();
writeHeader(writer);
writer.writeStartSegment("GS");
writer.writeStartElement();
assertThrows(IndexOutOfBoundsException.class, () -> writer.writeElementData(new char[] { 'F', 'A' }, -1, 2));
assertThrows(IndexOutOfBoundsException.class, () -> writer.writeElementData(new char[] { 'F', 'A' }, 2, 1));
assertThrows(IndexOutOfBoundsException.class, () -> writer.writeElementData(new char[] { 'F', 'A' }, 0, 3));
assertThrows(IllegalArgumentException.class, () -> writer.writeElementData(new char[] { 'F', 'A' }, 0, -1));
}

@Test
void testWriteElementDataCharArrayIllegal() throws EDIStreamException {
EDIOutputFactory factory = EDIOutputFactory.newFactory();
Expand Down Expand Up @@ -390,6 +479,29 @@ void testWriteBinaryDataInputStream() throws EDIStreamException {
assertEquals("BIN*8*\n\u0000\u0001\u0002\u0003\u0004\u0005\t~", stream.toString());
}

@Test
void testWriteBinaryDataInputStreamIOException() throws Exception {
EDIOutputFactory factory = EDIOutputFactory.newFactory();
ByteArrayOutputStream stream = new ByteArrayOutputStream(4096);
EDIStreamWriter writer = factory.createEDIStreamWriter(stream);
InputStream binaryStream = Mockito.mock(InputStream.class);
IOException ioException = new IOException();
Mockito.when(binaryStream.read()).thenThrow(ioException);
writer.startInterchange();
writeHeader(writer);
stream.reset();
writer.writeStartSegment("BIN");
writer.writeStartElement();
writer.writeElementData("4");
writer.endElement();
writer.writeStartElementBinary();
EDIStreamException thrown = assertThrows(EDIStreamException.class,
() -> writer.writeBinaryData(binaryStream));
assertEquals("Exception writing binary element data in segment BIN at position 2, element 2",
thrown.getMessage());
assertSame(ioException, thrown.getCause());
}

@Test
void testWriteBinaryDataByteArray() throws EDIStreamException {
EDIOutputFactory factory = EDIOutputFactory.newFactory();
Expand Down Expand Up @@ -1055,19 +1167,42 @@ public int read() throws IOException {
break;
}

assertEquals(reader.getLocation().getSegmentPosition(), writer.getLocation().getSegmentPosition(), () ->
"Segment position mismatch");
assertEquals(reader.getLocation().getElementPosition(), writer.getLocation().getElementPosition(), () ->
"Element position mismatch");
assertEquals(reader.getLocation().getElementOccurrence(), writer.getLocation().getElementOccurrence(), () ->
"Element occurrence mismatch");
assertEquals(reader.getLocation().getComponentPosition(), writer.getLocation().getComponentPosition(), () ->
"Component position mismatch");
assertEquals(reader.getLocation().getSegmentPosition(),
writer.getLocation().getSegmentPosition(),
() -> "Segment position mismatch");
assertEquals(reader.getLocation().getElementPosition(),
writer.getLocation().getElementPosition(),
() -> "Element position mismatch");
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();
OutputStream stream = new ByteArrayOutputStream(1);
EDIStreamWriter writer = factory.createEDIStreamWriter(stream);
IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> writer.getStandard());
assertEquals("standard not accessible", thrown.getMessage());
}

@Test
void testGetStandardX12() throws EDIStreamException {
EDIOutputFactory factory = EDIOutputFactory.newFactory();
OutputStream stream = new ByteArrayOutputStream(1);
EDIStreamWriter writer = factory.createEDIStreamWriter(stream);
writer.startInterchange();
writer.writeStartSegment("ISA");
assertEquals(EDIStreamConstants.Standards.X12, writer.getStandard());
}
}

0 comments on commit 0045758

Please sign in to comment.