Skip to content

Commit

Permalink
Specify hierarchical loop identifiers via schema instead of hard-coding
Browse files Browse the repository at this point in the history
  • Loading branch information
MikeEdgar committed Jul 7, 2021
1 parent 3d4c97c commit 019fef6
Show file tree
Hide file tree
Showing 24 changed files with 2,495 additions and 2,108 deletions.
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.17.1-SNAPSHOT</version>
<version>1.18.0-SNAPSHOT</version>

<name>StAEDI : Streaming API for EDI for Java</name>
<description>Streaming API for EDI for Java</description>
Expand Down
55 changes: 55 additions & 0 deletions src/main/java/io/xlate/edi/internal/schema/ElementPosition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.xlate.edi.internal.schema;

import java.util.Objects;

import io.xlate.edi.schema.EDIElementPosition;

public class ElementPosition implements EDIElementPosition {

private static final String TOSTRING_FORMAT = "%d.%d";
private final int elementPosition;
private final int componentPosition;

public ElementPosition(int elementPosition, int componentPosition) {
super();
this.elementPosition = elementPosition;
this.componentPosition = componentPosition;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}

if (!getClass().isInstance(o)) {
return false;
}

ElementPosition other = (ElementPosition) o;

return Objects.equals(elementPosition, other.elementPosition) &&
Objects.equals(componentPosition, other.componentPosition);
}

@Override
public int hashCode() {
return Objects.hash(elementPosition, componentPosition);
}

@Override
public String toString() {
return String.format(TOSTRING_FORMAT, elementPosition, componentPosition);
}

@Override
public int getElementPosition() {
return elementPosition;
}

@Override
public int getComponentPosition() {
return componentPosition > 0 ? componentPosition : -1;
}

}
40 changes: 40 additions & 0 deletions src/main/java/io/xlate/edi/internal/schema/LoopType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.xlate.edi.internal.schema;

import java.util.List;

import io.xlate.edi.schema.EDIElementPosition;
import io.xlate.edi.schema.EDILoopType;
import io.xlate.edi.schema.EDIReference;
import io.xlate.edi.schema.EDISyntaxRule;
import io.xlate.edi.schema.EDIType;

@SuppressWarnings("java:S2160") // Intentionally inherit 'equals' from superclass
class LoopType extends StructureType implements EDILoopType {

private final EDIElementPosition levelIdPosition;
private final EDIElementPosition parentIdPosition;

LoopType(String code,
List<EDIReference> references,
List<EDISyntaxRule> syntaxRules,
EDIElementPosition levelIdPosition,
EDIElementPosition parentIdPosition,
String title,
String description) {

super(code, EDIType.Type.LOOP, code, references, syntaxRules, title, description);
this.levelIdPosition = levelIdPosition;
this.parentIdPosition = parentIdPosition;
}

@Override
public EDIElementPosition getLevelIdPosition() {
return levelIdPosition;
}

@Override
public EDIElementPosition getParentIdPosition() {
return parentIdPosition;
}

}
33 changes: 32 additions & 1 deletion src/main/java/io/xlate/edi/internal/schema/SchemaReaderBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static io.xlate.edi.internal.schema.StaEDISchemaFactory.unexpectedEvent;
import static java.util.stream.Collectors.toList;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -25,6 +26,7 @@
import javax.xml.stream.XMLStreamReader;

import io.xlate.edi.schema.EDIComplexType;
import io.xlate.edi.schema.EDIElementPosition;
import io.xlate.edi.schema.EDIReference;
import io.xlate.edi.schema.EDISchemaException;
import io.xlate.edi.schema.EDISimpleType;
Expand All @@ -45,6 +47,8 @@ abstract class SchemaReaderBase implements SchemaReader {
static final String ATTR_MIN_OCCURS = "minOccurs";
static final String ATTR_MAX_OCCURS = "maxOccurs";
static final String ATTR_TITLE = "title";
static final String ATTR_LEVEL_ID_POSITION = "levelIdPosition";
static final String ATTR_PARENT_ID_POSITION = "parentIdPosition";

static final EDIReference ANY_ELEMENT_REF_OPT = new Reference(StaEDISchema.ANY_ELEMENT_ID, LOCALNAME_ELEMENT, 0, 1, null, null);
static final EDIReference ANY_COMPOSITE_REF_OPT = new Reference(StaEDISchema.ANY_COMPOSITE_ID, LOCALNAME_COMPOSITE, 0, 99, null, null);
Expand Down Expand Up @@ -388,11 +392,15 @@ StructureType readComplexType(XMLStreamReader reader,
final EDIType.Type type = complex.get(complexType);
final String id;
String code = parseAttribute(reader, "code", String::valueOf, null);
EDIElementPosition levelIdPosition = null;
EDIElementPosition parentIdPosition = null;

if (qnTransaction.equals(complexType)) {
id = StaEDISchema.TRANSACTION_ID;
} else if (qnLoop.equals(complexType)) {
id = code;
levelIdPosition = parseElementPosition(reader, ATTR_LEVEL_ID_POSITION);
parentIdPosition = parseElementPosition(reader, ATTR_PARENT_ID_POSITION);
} else {
id = parseAttribute(reader, "name", String::valueOf);

Expand Down Expand Up @@ -423,7 +431,15 @@ StructureType readComplexType(XMLStreamReader reader,
event = reader.getEventType();

if (event == XMLStreamConstants.END_ELEMENT) {
return new StructureType(id, type, code, refs, rules, title, descr);
StructureType structure;

if (qnLoop.equals(complexType)) {
structure = new LoopType(code, refs, rules, levelIdPosition, parentIdPosition, title, descr);
} else {
structure = new StructureType(id, type, code, refs, rules, title, descr);
}

return structure;
} else {
throw unexpectedEvent(reader);
}
Expand Down Expand Up @@ -806,6 +822,21 @@ void logUnusedTypes(Map<String, EDIType> types, Level level) {
}
}

EDIElementPosition parseElementPosition(XMLStreamReader reader, String attrName) {
BigDecimal positionAttr = parseAttribute(reader, attrName, BigDecimal::new, null);

if (positionAttr == null) {
return null;
}

int elementPosition = positionAttr.intValue();
int componentPosition = positionAttr.remainder(BigDecimal.ONE)
.movePointRight(positionAttr.scale())
.intValue();

return new ElementPosition(elementPosition, componentPosition);
}

protected abstract String readReferencedId(XMLStreamReader reader);

protected abstract void readInclude(XMLStreamReader reader, Map<String, EDIType> types) throws EDISchemaException;
Expand Down
37 changes: 18 additions & 19 deletions src/main/java/io/xlate/edi/internal/schema/SchemaReaderV3.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static io.xlate.edi.internal.schema.StaEDISchemaFactory.schemaException;
import static io.xlate.edi.internal.schema.StaEDISchemaFactory.unexpectedElement;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
Expand Down Expand Up @@ -34,6 +33,7 @@
import io.xlate.edi.internal.schema.implementation.SegmentImpl;
import io.xlate.edi.internal.schema.implementation.TransactionImpl;
import io.xlate.edi.schema.EDIComplexType;
import io.xlate.edi.schema.EDIElementPosition;
import io.xlate.edi.schema.EDIReference;
import io.xlate.edi.schema.EDISchemaException;
import io.xlate.edi.schema.EDIType;
Expand Down Expand Up @@ -215,7 +215,7 @@ LoopImplementation readLoopImplementation(XMLStreamReader reader, QName complexT
String typeId;
int minOccurs = 0;
int maxOccurs = 0;
BigDecimal discriminatorAttr = null;
EDIElementPosition discriminatorPos = null;
String title = null;

if (transactionLoop) {
Expand All @@ -226,7 +226,7 @@ LoopImplementation readLoopImplementation(XMLStreamReader reader, QName complexT
typeId = parseAttribute(reader, "type", String::valueOf);
minOccurs = parseAttribute(reader, ATTR_MIN_OCCURS, Integer::parseInt, -1);
maxOccurs = parseAttribute(reader, ATTR_MAX_OCCURS, Integer::parseInt, -1);
discriminatorAttr = parseAttribute(reader, ATTR_DISCRIMINATOR, BigDecimal::new, null);
discriminatorPos = parseElementPosition(reader, ATTR_DISCRIMINATOR);
title = parseAttribute(reader, ATTR_TITLE, String::valueOf, null);
}

Expand All @@ -237,9 +237,9 @@ LoopImplementation readLoopImplementation(XMLStreamReader reader, QName complexT

Discriminator disc = null;

if (discriminatorAttr != null) {
if (discriminatorPos != null) {
SegmentImpl segImpl = (SegmentImpl) sequence.get(0);
disc = buildDiscriminator(discriminatorAttr, segImpl.getSequence());
disc = buildDiscriminator(discriminatorPos, segImpl.getSequence());
}

return new LoopImpl(minOccurs, maxOccurs, id, typeId, disc, sequence, title, descr);
Expand All @@ -266,13 +266,13 @@ SegmentImpl readSegmentImplementation() {
String code = parseAttribute(reader, "code", String::valueOf, typeId);
int minOccurs = parseAttribute(reader, ATTR_MIN_OCCURS, Integer::parseInt, -1);
int maxOccurs = parseAttribute(reader, ATTR_MAX_OCCURS, Integer::parseInt, -1);
BigDecimal discriminatorAttr = parseAttribute(reader, ATTR_DISCRIMINATOR, BigDecimal::new, null);
EDIElementPosition discriminatorPos = parseElementPosition(reader, ATTR_DISCRIMINATOR);
String title = parseAttribute(reader, ATTR_TITLE, String::valueOf, null);

return readTypeImplementation(reader,
() -> readSequence(reader, e -> readPositionedSequenceEntry(e, sequence, true)),
descr -> whenExpected(reader, qnSegment, () -> {
Discriminator disc = buildDiscriminator(discriminatorAttr, sequence);
Discriminator disc = buildDiscriminator(discriminatorPos, sequence);
SegmentImpl segment = new SegmentImpl(minOccurs,
maxOccurs,
typeId,
Expand Down Expand Up @@ -312,45 +312,44 @@ void readPositionedSequenceEntry(QName entryName, List<EDITypeImplementation> se
}
}

Discriminator buildDiscriminator(BigDecimal discriminatorAttr,
Discriminator buildDiscriminator(EDIElementPosition discriminatorPos,
List<EDITypeImplementation> sequence) {
Discriminator disc = null;

if (discriminatorAttr != null) {
int elementPosition = discriminatorAttr.intValue();
int componentPosition = discriminatorAttr.remainder(BigDecimal.ONE)
.movePointRight(discriminatorAttr.scale()).intValue();
if (discriminatorPos != null) {
final int elementPosition = discriminatorPos.getElementPosition();
final int componentPosition = discriminatorPos.getComponentPosition();

EDITypeImplementation eleImpl = getDiscriminatorElement(discriminatorAttr, elementPosition, sequence, "element");
EDITypeImplementation eleImpl = getDiscriminatorElement(discriminatorPos, elementPosition, sequence, "element");

if (eleImpl instanceof CompositeImpl) {
sequence = ((CompositeImpl) eleImpl).getSequence();
eleImpl = getDiscriminatorElement(discriminatorAttr, componentPosition, sequence, "component");
eleImpl = getDiscriminatorElement(discriminatorPos, componentPosition, sequence, "component");
}

Set<String> discValues;

if (eleImpl != null) {
discValues = ((ElementImpl) eleImpl).getValueSet();
} else {
throw schemaException("Discriminator position is unused (not specified): " + discriminatorAttr, reader);
throw schemaException("Discriminator position is unused (not specified): " + discriminatorPos, reader);
}

if (!discValues.isEmpty()) {
disc = new DiscriminatorImpl(elementPosition, componentPosition, discValues);
disc = new DiscriminatorImpl(discriminatorPos, discValues);
} else {
throw schemaException("Discriminator element does not specify value enumeration: " + discriminatorAttr, reader);
throw schemaException("Discriminator element does not specify value enumeration: " + discriminatorPos, reader);
}
}

return disc;
}

EDITypeImplementation getDiscriminatorElement(BigDecimal attr, int position, List<EDITypeImplementation> sequence, String type) {
EDITypeImplementation getDiscriminatorElement(EDIElementPosition discriminatorPos, int position, List<EDITypeImplementation> sequence, String type) {
if (position > 0 && position <= sequence.size()) {
return sequence.get(position - 1);
} else {
throw schemaException("Discriminator " + type + " position invalid: " + attr, reader);
throw schemaException("Discriminator " + type + " position invalid: " + discriminatorPos, reader);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,17 @@
import java.util.Objects;
import java.util.Set;

import io.xlate.edi.schema.EDIElementPosition;
import io.xlate.edi.schema.implementation.Discriminator;

public class DiscriminatorImpl implements Discriminator {

private static final String TOSTRING_FORMAT = "position: [%d, %d], values: %s";
private final int elementPosition;
private final int componentPosition;
private static final String TOSTRING_FORMAT = "position: %s, values: %s";
private final EDIElementPosition position;
private final Set<String> valueSet;

public DiscriminatorImpl(int elementPosition, int componentPosition, Set<String> valueSet) {
super();
this.elementPosition = elementPosition;
this.componentPosition = componentPosition;
public DiscriminatorImpl(EDIElementPosition position, Set<String> valueSet) {
this.position = position;
this.valueSet = valueSet;
}

Expand All @@ -30,30 +28,28 @@ public boolean equals(Object o) {
}

DiscriminatorImpl other = (DiscriminatorImpl) o;

return Objects.equals(elementPosition, other.elementPosition) &&
Objects.equals(componentPosition, other.componentPosition) &&
return Objects.equals(position, other.position) &&
Objects.equals(valueSet, other.valueSet);
}

@Override
public int hashCode() {
return Objects.hash(elementPosition, componentPosition, valueSet);
return Objects.hash(position, valueSet);
}

@Override
public String toString() {
return String.format(TOSTRING_FORMAT, elementPosition, componentPosition, valueSet);
return String.format(TOSTRING_FORMAT, position, valueSet);
}

@Override
public int getElementPosition() {
return elementPosition;
return position.getElementPosition();
}

@Override
public int getComponentPosition() {
return componentPosition;
return position.getComponentPosition();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,6 @@ boolean ignoreExtraneousCharacters() {
}

boolean nestHierarchicalLoops() {
return getProperty(EDIInputFactory.EDI_NEST_HIERARCHICAL_LOOPS, Boolean::parseBoolean, false);
return getProperty(EDIInputFactory.EDI_NEST_HIERARCHICAL_LOOPS, Boolean::parseBoolean, true);
}
}

0 comments on commit 019fef6

Please sign in to comment.