Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hierarchical Levels via Schema #198

Merged
merged 8 commits into from
Jul 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
56 changes: 56 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,56 @@
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";

final int elementIndex;
final int componentIndex;

public ElementPosition(int elementPosition, int componentPosition) {
super();
this.elementIndex = elementPosition;
this.componentIndex = 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(elementIndex, other.elementIndex) &&
Objects.equals(componentIndex, other.componentIndex);
}

@Override
public int hashCode() {
return Objects.hash(elementIndex, componentIndex);
}

@Override
public String toString() {
return String.format(TOSTRING_FORMAT, elementIndex, componentIndex);
}

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

@Override
public int getComponentPosition() {
return componentIndex > 0 ? componentIndex : -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 final Set<String> valueSet;
private static final String TOSTRING_FORMAT = "position: %s, values: %s";
final EDIElementPosition position;
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 @@ -45,10 +45,6 @@ public int length() {
return length;
}

public char[] getText() {
return text;
}

@Override
public char charAt(int index) {
if (index < 0 || index >= length) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ public class StaEDIInputFactory extends EDIInputFactory {
public StaEDIInputFactory() {
supportedProperties.add(EDI_VALIDATE_CONTROL_STRUCTURE);
supportedProperties.add(EDI_VALIDATE_CONTROL_CODE_VALUES);
supportedProperties.add(EDI_IGNORE_EXTRANEOUS_CHARACTERS);
supportedProperties.add(EDI_NEST_HIERARCHICAL_LOOPS);

supportedProperties.add(XML_DECLARE_TRANSACTION_XMLNS);
supportedProperties.add(XML_WRAP_TRANSACTION_CONTENTS);
supportedProperties.add(EDI_IGNORE_EXTRANEOUS_CHARACTERS);

supportedProperties.add(JSON_NULL_EMPTY_ELEMENTS);
supportedProperties.add(JSON_OBJECT_ELEMENTS);
Expand Down
Loading