Skip to content

Commit

Permalink
Allow versions for elementType, validate X12 GS date based on version
Browse files Browse the repository at this point in the history
- Support for different min/max lengths and enumerations depending on
the dialect-specific version of a transaction
- Make invalid text available in more scenarios for invalid elements
  • Loading branch information
MikeEdgar committed Jun 15, 2020
1 parent 422d49d commit 71c7725
Show file tree
Hide file tree
Showing 35 changed files with 676 additions and 76 deletions.
98 changes: 89 additions & 9 deletions src/main/java/io/xlate/edi/internal/schema/ElementType.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,91 @@
******************************************************************************/
package io.xlate.edi.internal.schema;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Supplier;

import io.xlate.edi.schema.EDISimpleType;

@SuppressWarnings("java:S2160") // Intentionally inherit 'equals' from superclass
//java:S107 : Constructor has 8 arguments
//java:S2160 : Intentionally inherit 'equals' from superclass
@SuppressWarnings({ "java:S107", "java:S2160" })
class ElementType extends BasicType implements EDISimpleType {

private static final String TOSTRING_FORMAT = "id: %s, type: %s, base: %s, code: %s, minLength: %d, maxLength: %d, values: %s";
private Base base;
private String code;
private int number;
private long minLength;
private long maxLength;
private Set<String> values;

ElementType(String id, Base base, String code, int number, long minLength, long maxLength, Set<String> values) {

final Base base;
final String code;
final int number;
final long minLength;
final long maxLength;
final Set<String> values;
final List<Version> versions;

static class Version {
final String minVersion;
final String maxVersion;
final Long minLength;
final Long maxLength;
final Set<String> values;

Version(String minVersion, String maxVersion, Long minLength, Long maxLength, Set<String> values) {
super();
this.minVersion = minVersion;
this.maxVersion = maxVersion;
this.minLength = minLength;
this.maxLength = maxLength;
this.values = values;
}

boolean appliesTo(String version) {
if (!minVersion.trim().isEmpty() && minVersion.compareTo(version) > 0) {
// version is less than the minVersion
return false;
}
if (!maxVersion.trim().isEmpty() && maxVersion.compareTo(version) < 0) {
// version is greater than the minVersion
return false;
}
return true;
}

public long getMinLength(ElementType defaultElement) {
return minLength != null ? minLength.longValue() : defaultElement.getMinLength();
}

public long getMaxLength(ElementType defaultElement) {
return maxLength != null ? maxLength.longValue() : defaultElement.getMaxLength();
}

public Set<String> getValueSet(ElementType defaultElement) {
return values != null ? values : defaultElement.getValueSet();
}
}

ElementType(String id, Base base, String code, int number, long minLength, long maxLength, Set<String> values, List<Version> versions) {
super(id, Type.ELEMENT);
this.base = base;
this.code = code;
this.number = number;
this.minLength = minLength;
this.maxLength = maxLength;
this.values = Collections.unmodifiableSet(new LinkedHashSet<>(values));
this.versions = Collections.unmodifiableList(new ArrayList<>(versions));
}

<T> T getVersionAttribute(String version, BiFunction<Version, ElementType, T> versionedSupplier, Supplier<T> defaultSupplier) {
for (Version ver : versions) {
if (ver.appliesTo(version)) {
return versionedSupplier.apply(ver, this);
}
}

return defaultSupplier.get();
}

@Override
Expand All @@ -57,6 +117,11 @@ public String getCode() {
return code;
}

@Override
public boolean hasVersions() {
return !versions.isEmpty();
}

/**
* @see io.xlate.edi.schema.EDISimpleType#getNumber()
* @deprecated
Expand All @@ -73,13 +138,28 @@ public long getMinLength() {
return minLength;
}

@Override
public long getMinLength(String version) {
return getVersionAttribute(version, Version::getMinLength, this::getMinLength);
}

@Override
public long getMaxLength() {
return maxLength;
}

@Override
public long getMaxLength(String version) {
return getVersionAttribute(version, Version::getMaxLength, this::getMaxLength);
}

@Override
public Set<String> getValueSet() {
return values;
}

@Override
public Set<String> getValueSet(String version) {
return getVersionAttribute(version, Version::getValueSet, this::getValueSet);
}
}
62 changes: 58 additions & 4 deletions src/main/java/io/xlate/edi/internal/schema/SchemaReaderBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ abstract class SchemaReaderBase implements SchemaReader {
0,
0,
99_999,
Collections.emptySet());
Collections.emptySet(),
Collections.emptyList());

static final EDIComplexType ANY_COMPOSITE = new StructureType(StaEDISchema.ANY_COMPOSITE_ID,
Type.COMPOSITE,
Expand All @@ -77,6 +78,7 @@ abstract class SchemaReaderBase implements SchemaReader {
final QName qnSequence;
final QName qnEnumeration;
final QName qnValue;
final QName qnVersion;
final QName qnAny;

final QName qnCompositeType;
Expand Down Expand Up @@ -106,6 +108,7 @@ public SchemaReaderBase(String xmlns, XMLStreamReader reader) {
qnSequence = new QName(xmlns, "sequence");
qnEnumeration = new QName(xmlns, "enumeration");
qnValue = new QName(xmlns, "value");
qnVersion = new QName(xmlns, "version");
qnAny = new QName(xmlns, "any");

qnCompositeType = new QName(xmlns, "compositeType");
Expand Down Expand Up @@ -542,7 +545,58 @@ ElementType readSimpleType(XMLStreamReader reader) {
long minLength = parseAttribute(reader, "minLength", Long::parseLong, 1L);
long maxLength = parseAttribute(reader, "maxLength", Long::parseLong, 1L);

return new ElementType(name, intBase, code, number, minLength, maxLength, readEnumerationValues(reader));
final Set<String> values;
final List<ElementType.Version> versions;

if (nextTag(reader, "reading elementType contents") == XMLStreamConstants.END_ELEMENT) {
values = Collections.emptySet();
versions = Collections.emptyList();
} else {
if (qnEnumeration.equals(reader.getName())) {
values = readEnumerationValues(reader);
nextTag(reader, "reading after elementType enumeration");
} else {
values = Collections.emptySet();
}

if (qnVersion.equals(reader.getName())) {
versions = new ArrayList<>();

while (qnVersion.equals(reader.getName())) {
versions.add(readSimpleTypeVersion(reader));
nextTag(reader, "reading after elementType version");
}
} else {
versions = Collections.emptyList();
}

if (!qnElementType.equals(reader.getName())) {
throw unexpectedElement(reader.getName(), reader);
}
}

return new ElementType(name, intBase, code, number, minLength, maxLength, values, versions);
}

ElementType.Version readSimpleTypeVersion(XMLStreamReader reader) {
String minVersion = parseAttribute(reader, "minVersion", String::valueOf, "");
String maxVersion = parseAttribute(reader, "maxVersion", String::valueOf, "");
long minLength = parseAttribute(reader, "minLength", Long::parseLong, 1L);
long maxLength = parseAttribute(reader, "maxLength", Long::parseLong, 1L);

Set<String> values;

if (nextTag(reader, "reading elementType version contents") == XMLStreamConstants.END_ELEMENT) {
// Set to null instead of empty to indicate that no enumeration is present in this version
values = null;
} else if (qnEnumeration.equals(reader.getName())) {
values = readEnumerationValues(reader);
nextTag(reader, "reading after elementType version enumeration");
} else {
throw unexpectedElement(reader.getName(), reader);
}

return new ElementType.Version(minVersion, maxVersion, minLength, maxLength, values);
}

Set<String> readEnumerationValues(XMLStreamReader reader) {
Expand All @@ -556,10 +610,10 @@ Set<String> readEnumerationValues(XMLStreamReader reader) {
if (event == XMLStreamConstants.START_ELEMENT) {
if (element.equals(qnValue)) {
values = readEnumerationValue(reader, values);
} else if (!element.equals(qnEnumeration)) {
} else {
throw unexpectedElement(element, reader);
}
} else if (qnElementType.equals(element) || qnEnumeration.equals(element)) {
} else {
endOfEnumeration = true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,7 @@ public void loopEnd(CharSequence id) {
public void elementError(EDIStreamEvent event,
EDIStreamValidationError error,
CharSequence referenceCode,
CharSequence data,
int element,
int component,
int repetition) {
Expand All @@ -695,7 +696,7 @@ public void elementError(EDIStreamEvent event,
copy.setElementOccurrence(repetition);
copy.setComponentPosition(component);

errors.add(new EDIValidationException(event, error, copy, null));
errors.add(new EDIValidationException(event, error, copy, data));
}

@Override
Expand Down Expand Up @@ -726,6 +727,7 @@ private void validateElement(Runnable setupCommand, CharSequence data) {
elementError(error.getError().getCategory(),
error.getError(),
error.getCode(),
data,
location.getElementPosition(),
location.getComponentPosition(),
location.getElementOccurrence());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,25 @@ public interface Dialect {
*/
void transactionEnd();

/**
* Notify the dialect that a group is complete.
*/
void groupEnd();

/**
* Returns the identifying elements of the current transaction's version.
*
* @return the array of elements identifying the current transaction's version
* @return the array of elements identifying the current transaction's
* version
*/
String[] getTransactionVersion();

/**
* Returns the identifying elements of the current transaction's version as
* a single String joined with period `.` characters.
*
* @return the String representation of the elements identifying the current
* transaction's version
*/
String getTransactionVersionString();
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public class EDIFACTDialect implements Dialect {
private static final int TX_RELEASE = 2;
private static final int TX_ASSIGNED_CODE = 3;
private String[] transactionVersion = new String[4];
private String transactionVersionString;

EDIFACTDialect() {
clearTransactionVersion();
}

@Override
public void setHeaderTag(String tag) {
Expand Down Expand Up @@ -246,6 +251,11 @@ void clearTransactionVersion() {
for (int i = 0; i < transactionVersion.length; i++) {
transactionVersion[i] = "";
}
updateTransactionVersionString(null);
}

void updateTransactionVersionString(String[] transactionVersion) {
transactionVersionString = transactionVersion != null ? String.join(".", transactionVersion) : "";
}

@Override
Expand Down Expand Up @@ -293,9 +303,11 @@ public void elementData(CharSequence data, Location location) {
break;
case 4:
transactionVersion[TX_AGENCY] = data.toString();
updateTransactionVersionString(transactionVersion);
break;
case 5:
transactionVersion[TX_ASSIGNED_CODE] = data.toString();
updateTransactionVersionString(transactionVersion);
break;
default:
break;
Expand All @@ -309,8 +321,18 @@ public void transactionEnd() {
clearTransactionVersion();
}

@Override
public void groupEnd() {
clearTransactionVersion();
}

@Override
public String[] getTransactionVersion() {
return transactionVersion;
}

@Override
public String getTransactionVersionString() {
return transactionVersionString;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ public void loopEnd(CharSequence id) {
dialect.transactionEnd();
enqueueEvent(EDIStreamEvent.END_TRANSACTION, EDIStreamValidationError.NONE, id, null);
} else if (EDIType.Type.GROUP.toString().equals(id)) {
dialect.groupEnd();
enqueueEvent(EDIStreamEvent.END_GROUP, EDIStreamValidationError.NONE, id, null);
} else {
enqueueEvent(EDIStreamEvent.END_LOOP, EDIStreamValidationError.NONE, id, id);
Expand Down Expand Up @@ -216,6 +217,7 @@ boolean exitTransaction(CharSequence tag) {
public boolean segmentEnd() {
if (validator() != null) {
validator().validateSyntax(this, this, location, false);
validator().validateVersionConstraints(dialect, this);
}

location.clearSegmentLocations();
Expand Down Expand Up @@ -372,6 +374,7 @@ public void segmentError(CharSequence token, EDIStreamValidationError error) {
public void elementError(final EDIStreamEvent event,
final EDIStreamValidationError error,
final CharSequence referenceCode,
final CharSequence data,
final int element,
final int component,
final int repetition) {
Expand All @@ -381,7 +384,7 @@ public void elementError(final EDIStreamEvent event,
copy.setElementOccurrence(repetition);
copy.setComponentPosition(component);

enqueueEvent(event, error, null, referenceCode, copy);
enqueueEvent(event, error, data, referenceCode, copy);
}

private Validator validator() {
Expand Down
Loading

0 comments on commit 71c7725

Please sign in to comment.