Skip to content

Commit

Permalink
Merge pull request #171 from xlate/169_edifact_writer_validation
Browse files Browse the repository at this point in the history
Skip validation for EDIFACT UNA; validate empty elements from syntax
  • Loading branch information
MikeEdgar committed Jan 23, 2021
2 parents a3c58c0 + 97c1844 commit 21d9b70
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 43 deletions.
111 changes: 69 additions & 42 deletions src/main/java/io/xlate/edi/internal/stream/StaEDIStreamWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.logging.Logger;

Expand Down Expand Up @@ -92,6 +93,7 @@ public class StaEDIStreamWriter implements EDIStreamWriter, ElementDataHandler,
private CharBuffer elementBuffer = CharBuffer.allocate(500);
private final StringBuilder formattedElement = new StringBuilder();
private List<EDIValidationException> errors = new ArrayList<>();
private CharArraySequence elementHolder = new CharArraySequence();

private char segmentTerminator;
private char segmentTagTerminator;
Expand Down Expand Up @@ -403,12 +405,6 @@ public EDIStreamWriter endInterchange() throws EDIStreamException {
public EDIStreamWriter writeStartSegment(String name) throws EDIStreamException {
ensureLevel(LEVEL_INTERCHANGE);
location.incrementSegmentPosition(name);
validate(validator -> validator.validateSegment(this, name));

if (exitTransaction(name)) {
transaction = false;
validate(validator -> validator.validateSegment(this, name));
}

if (state == State.INITIAL) {
dialect = DialectFactory.getDialect(name);
Expand All @@ -422,19 +418,24 @@ public EDIStreamWriter writeStartSegment(String name) throws EDIStreamException
*/
dialect = DialectFactory.getDialect(EDIFACTDialect.UNA);
writeServiceAdviceString();
segmentValidation(name);
// Now write the UNB
writeString(name);
} else {
writeString(name);

if (EDIFACTDialect.UNA.equals(name)) {
writeString(name);
writeServiceAdviceCharacters();
} else {
segmentValidation(name);
writeString(name);
}
}
} else {
segmentValidation(name);
writeString(name);
}
} else {
segmentValidation(name);
writeString(name);
}

Expand All @@ -445,6 +446,15 @@ public EDIStreamWriter writeStartSegment(String name) throws EDIStreamException
return this;
}

void segmentValidation(String name) {
validate(validator -> validator.validateSegment(this, name));

if (exitTransaction(name)) {
transaction = false;
validate(validator -> validator.validateSegment(this, name));
}
}

void terminateSegmentTag() throws EDIStreamException {
if (this.segmentTagTerminator != '\0') {
write(this.segmentTagTerminator);
Expand Down Expand Up @@ -820,7 +830,15 @@ public boolean binaryData(InputStream binary) {

@Override
public boolean elementData(char[] text, int start, int length) {
// No operation
elementHolder.set(text, start, length);
dialect.elementData(elementHolder, location);

withValidator(validator -> {
if (!validator.validateElement(dialect, location, elementHolder, null)) {
reportElementErrors(validator, elementHolder);
}
});

return true;
}

Expand Down Expand Up @@ -880,82 +898,91 @@ public void segmentError(CharSequence token, EDIReference typeReference, EDIStre
}

private void validate(Consumer<Validator> command) {
Validator validator = validator();

if (validator != null) {
withValidator(validator -> {
errors.clear();
command.accept(validator);

if (!errors.isEmpty()) {
throw validationExceptionChain(errors);
}
}
});
}

private void validateCompositeOccurrence() {
final Validator validator = validator();

if (validator != null) {
withValidator(validator -> {
errors.clear();

if (!validator.validCompositeOccurrences(dialect, location)) {
for (UsageError error : validator.getElementErrors()) {
elementError(error.getError().getCategory(),
error.getError(),
error.getTypeReference(),
"",
location.getElementPosition(),
location.getComponentPosition(),
location.getElementOccurrence());
}
reportElementErrors(validator, "");
}

if (!errors.isEmpty()) {
throw validationExceptionChain(errors);
}
}
});
}

private CharSequence validateElement(Runnable setupCommand, CharSequence data) {
final Validator validator = validator();
final CharSequence result;
return withValidator(validator -> {
CharSequence elementData;

if (validator != null) {
if (this.formatElements) {
result = this.formattedElement;
elementData = this.formattedElement;
this.formattedElement.setLength(0);
this.formattedElement.append(data); // Validator will clear and re-format if configured
} else {
result = data;
elementData = data;
}

errors.clear();
setupCommand.run();

if (!validator.validateElement(dialect, location, data, this.formattedElement)) {
for (UsageError error : validator.getElementErrors()) {
elementError(error.getError().getCategory(),
error.getError(),
error.getTypeReference(),
result,
location.getElementPosition(),
location.getComponentPosition(),
location.getElementOccurrence());
}
reportElementErrors(validator, elementData);
}

if (!errors.isEmpty()) {
throw validationExceptionChain(errors);
}

dialect.elementData(result, location);
dialect.elementData(elementData, location);
return elementData;
}, () -> data);
}

void withValidator(Consumer<Validator> process) {
final Validator validator = validator();

if (validator != null) {
process.accept(validator);
}
}

<T> T withValidator(Function<Validator, T> process, Supplier<T> unvalidatedResult) {
final Validator validator = validator();
final T result;

if (validator != null) {
result = process.apply(validator);
} else {
result = data;
result = unvalidatedResult.get();
}

return result;
}

void reportElementErrors(Validator validator, CharSequence data) {
for (UsageError error : validator.getElementErrors()) {
elementError(error.getError().getCategory(),
error.getError(),
error.getTypeReference(),
data,
location.getElementPosition(),
location.getComponentPosition(),
location.getElementOccurrence());
}
}

EDIValidationException validationExceptionChain(List<EDIValidationException> errors) {
Iterator<EDIValidationException> iter = errors.iterator();
EDIValidationException first = iter.next();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import io.xlate.edi.stream.EDIOutputFactory;
import io.xlate.edi.stream.EDIStreamConstants;
import io.xlate.edi.stream.EDIStreamConstants.Delimiters;
import io.xlate.edi.stream.EDIStreamConstants.Standards;
import io.xlate.edi.stream.EDIStreamEvent;
import io.xlate.edi.stream.EDIStreamException;
import io.xlate.edi.stream.EDIStreamReader;
Expand Down Expand Up @@ -2026,7 +2027,7 @@ void testOutputCompositeMissingSyntax() throws Exception {
write(writer, "UNH", "1", new String[] { "CONTRL", "4", "2", "UN" });
write(writer, "UCI", "1", "SENDER", "RECEIVER", "7");

writer.writeStartSegment("UCM").writeElement("1");
writer.writeStartSegment("UCM").writeElement("1").writeEmptyElement().writeElement("7");
EDIValidationException thrown = assertThrows(EDIValidationException.class, () -> writer.writeEndSegment());
EDIStreamValidationError error = thrown.getError();
assertEquals(EDIStreamValidationError.CONDITIONAL_REQUIRED_DATA_ELEMENT_MISSING, error);
Expand Down Expand Up @@ -2117,4 +2118,36 @@ void testIncompleteUNB() throws IOException, EDIStreamException, EDISchemaExcept

assertEquals("UNA:+.? 'UNB+UNOA:3'UNH", new String(stream.toByteArray()));
}

@Test
void testIncompleteUNBValidated() throws IOException, EDIStreamException, EDISchemaException {
final EDIOutputFactory factory = EDIOutputFactory.newFactory();
ByteArrayOutputStream stream = new ByteArrayOutputStream(4096);
EDIValidationException thrown = null;

try (EDIStreamWriter writer = factory.createEDIStreamWriter(stream)) {
Schema schema = SchemaFactory.newFactory().getControlSchema(Standards.EDIFACT, new String[] { "UNOA", "3" });
writer.setControlSchema(schema);

writer.startInterchange();
writer.writeStartSegment("UNA")
.writeEndSegment();
writer.writeStartSegment("UNB")
.writeStartElement()
.writeComponent("UNOA")
.writeComponent("3")
.endElement();

thrown = assertThrows(EDIValidationException.class, () -> writer.writeEndSegment());
}

for (int position : Arrays.asList(2, 3, 4, 5)) {
assertEquals(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR, thrown.getEvent());
assertEquals(EDIStreamValidationError.REQUIRED_DATA_ELEMENT_MISSING, thrown.getError());
assertEquals("UNB", thrown.getLocation().getSegmentTag());
assertEquals(2, thrown.getLocation().getSegmentPosition());
assertEquals(position, thrown.getLocation().getElementPosition());
thrown = thrown.getNextException();
}
}
}

0 comments on commit 21d9b70

Please sign in to comment.