Skip to content

Commit

Permalink
Allow implementation nodes to occur/recur in any order in the input
Browse files Browse the repository at this point in the history
  • Loading branch information
MikeEdgar committed Nov 27, 2021
1 parent 263c837 commit 832c597
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 93 deletions.
31 changes: 31 additions & 0 deletions src/main/java/io/xlate/edi/internal/stream/StaEDIStreamReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,37 @@ public StaEDIStreamReader(
this.lexer = new Lexer(stream, charset, proxy, location, ignoreExtraneousCharacters());
}

@Override
public String toString() {
StringBuilder buffer = new StringBuilder("StaEDIStreamReader(");

buffer.append("complete=").append(Boolean.valueOf(complete));
buffer.append(", ").append("closed=").append(Boolean.valueOf(closed));

if (!closed) {
EDIStreamEvent event = getEventType();
buffer.append(", ").append("event=").append(getEventType());

switch (event) {
case SEGMENT_ERROR:
case ELEMENT_DATA_ERROR:
case ELEMENT_OCCURRENCE_ERROR:
buffer.append(", ").append("error=").append(getErrorType());
buffer.append(", ").append("referenceCode=").append(getReferenceCode());
break;
case ELEMENT_DATA:
buffer.append(", ").append("text=").append(getText());
break;
default:
break;
}

buffer.append(", ").append("location=").append(location);
}

return buffer.append(')').toString();
}

private void ensureOpen() {
if (closed) {
throw new IllegalStateException("Reader is closed");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -564,72 +564,14 @@ private void enqueueEvent(EDIStreamEvent event,
Location location) {

StreamEvent target = getPooledEvent();
EDIStreamEvent associatedEvent = eventQueue.isEmpty() ? null : getAssociatedEvent(error);

if (eventExists(associatedEvent)) {
/*
* Ensure segment errors occur before other event types
* when the array has other events already present.
*/
int offset = eventQueue.size();
boolean complete = false;

while (!complete) {
StreamEvent enqueuedEvent = eventQueue.get(offset - 1);

if (enqueuedEvent.type == associatedEvent) {
complete = true;
} else {
if (eventQueue.size() == offset) {
eventQueue.add(offset, enqueuedEvent);
} else {
eventQueue.set(offset, enqueuedEvent);
}
offset--;
}
}

eventQueue.set(offset, target);
} else {
eventQueue.add(target);
}

target.type = event;
target.errorType = error;
target.setData(data);
target.setTypeReference(typeReference);
target.setLocation(location);
}

private boolean eventExists(EDIStreamEvent associatedEvent) {
int offset = eventQueue.size();

while (associatedEvent != null && offset > 0) {
if (eventQueue.get(offset - 1).type == associatedEvent) {
return true;
}
offset--;
}

return false;
eventQueue.add(target);
}

private static EDIStreamEvent getAssociatedEvent(EDIStreamValidationError error) {
final EDIStreamEvent event;

switch (error) {
case IMPLEMENTATION_LOOP_OCCURS_UNDER_MINIMUM_TIMES:
event = EDIStreamEvent.END_LOOP;
break;
case MANDATORY_SEGMENT_MISSING:
case IMPLEMENTATION_SEGMENT_BELOW_MINIMUM_USE:
event = null;
break;
default:
event = null;
break;
}

return event;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,25 @@ public static boolean hasMinimumUsage(String version, UsageNode node) {
return node == null || node.hasMinimumUsage(version);
}

public static UsageNode getParent(UsageNode node) {
return node != null ? node.getParent() : null;
}

public static UsageNode getFirstChild(UsageNode node) {
return node != null ? node.getFirstChild() : null;
}

public UsageNode getFirstSiblingSameType() {
if (parent == null) {
return this;
}

EDIType type = getReferencedType();
UsageNode sibling = getFirstSibling();

while (!type.equals(sibling.getReferencedType())) {
sibling = sibling.getNextSibling();
}

return sibling;
}

public static void resetChildren(UsageNode... nodes) {
for (UsageNode node : nodes) {
if (node != null) {
Expand Down Expand Up @@ -228,14 +239,17 @@ public UsageNode getFirstChild() {
return getChild(0);
}

UsageNode getChildById(CharSequence id) {
return children.stream()
.filter(c -> c != null && c.getId().contentEquals(id))
.findFirst()
.orElse(null);
private UsageNode getChildById(CharSequence id) {
for (UsageNode child : children) {
if (child.getId().contentEquals(id)) {
return child;
}
}
return null;
}

UsageNode getSiblingById(CharSequence id) {
return parent != null ? parent.getChildById(id) : null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@ void next(UsageNode nextImpl) {
}

void nagivateUp(int limit) {
standard = UsageNode.getParent(standard);
standard = standard.getParent();

if (impl != null && impl.getDepth() > limit) {
impl = UsageNode.getParent(impl);
impl = impl.getParent().getFirstSiblingSameType();
}
}

Expand Down Expand Up @@ -439,7 +439,7 @@ public void validateSegment(ValidationEventHandler handler, CharSequence tag) {
*/
checkMinimumUsage(cursor.standard);

UsageNode nextImpl = checkMinimumImplUsage(cursor.impl, cursor.standard);
UsageNode nextImpl = getNextImplementationNode(cursor.impl, cursor.standard.getReferencedType());

if (cursor.hasNextSibling()) {
// Advance to the next segment in the loop
Expand All @@ -461,16 +461,6 @@ public void validateSegment(ValidationEventHandler handler, CharSequence tag) {
handleMissingMandatory(handler);
}

UsageNode checkMinimumImplUsage(UsageNode nextImpl, UsageNode current) {
while (nextImpl != null && nextImpl.getReferencedType().equals(current.getReferencedType())) {
// Advance past multiple implementations of the 'current' standard node
checkMinimumUsage(nextImpl);
nextImpl = nextImpl.getNextSibling();
}

return nextImpl;
}

boolean handleNode(CharSequence tag, UsageNode current, UsageNode currentImpl, int startDepth, ValidationEventHandler handler) {
final boolean handled;

Expand Down Expand Up @@ -589,6 +579,17 @@ void checkMinimumUsage(UsageNode node) {
}
}

UsageNode getNextImplementationNode(UsageNode implNode, EDIType type) {
while (implNode != null && implNode.getReferencedType().equals(type)) {
// Advance past multiple implementations of the 'current' standard node
checkMinimumUsage(implNode);
implNode = implNode.getNextSibling();
}

// `implNode` will be an implementation of the type following `type`
return implNode;
}

boolean handleLoop(CharSequence tag, UsageNode current, UsageNode currentImpl, int startDepth, ValidationEventHandler handler) {
if (!current.getFirstChild().getId().contentEquals(tag)) {
return false;
Expand Down Expand Up @@ -781,7 +782,6 @@ public boolean selectImplementation(Deque<StreamEvent> eventQueue, ValidationEve
}

void handleImplementationSelected(UsageNode candidate, UsageNode implSeg, ValidationEventHandler handler) {
checkMinimumImplUsage(implNode, candidate, handler);
implSegmentCandidates.clear();
implNode = implSeg;
implSegmentSelected = true;
Expand Down Expand Up @@ -820,14 +820,6 @@ void handleImplementationSelected(UsageNode candidate, UsageNode implSeg, Valida
}
}

void checkMinimumImplUsage(UsageNode sibling, UsageNode selected, ValidationEventHandler handler) {
while (sibling != null && sibling != selected) {
checkMinimumUsage(sibling);
sibling = sibling.getNextSibling();
}
handleMissingMandatory(handler);
}

/**
* Validate any implementation elements previously skipped while searching
* for the loop discriminator element. Validation of enumerated values specified
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,6 @@ public boolean accept(EDIStreamReader reader) {
assertEvent(reader, EDIStreamEvent.SEGMENT_ERROR, EDIStreamValidationError.IMPLEMENTATION_SEGMENT_BELOW_MINIMUM_USE, "S13");
assertEvent(reader, EDIStreamEvent.END_LOOP, "0000A");


// Loop B - Occurrence 1
assertEvent(reader, EDIStreamEvent.START_LOOP, "0000B");
assertEvent(reader, EDIStreamEvent.SEGMENT_ERROR, EDIStreamValidationError.SEGMENT_EXCEEDS_MAXIMUM_USE, "S12");
Expand All @@ -463,15 +462,15 @@ public boolean accept(EDIStreamReader reader) {
assertEvent(reader, EDIStreamEvent.START_LOOP, "0000C");
assertEvent(reader, EDIStreamEvent.END_LOOP, "0000C");

assertEvent(reader, EDIStreamEvent.SEGMENT_ERROR, EDIStreamValidationError.IMPLEMENTATION_LOOP_OCCURS_UNDER_MINIMUM_TIMES, "S11");

// Loop D - Occurrence 1
assertEvent(reader, EDIStreamEvent.START_LOOP, "0000D");

// Standard Loop L0000 may only occur 5 times
assertEvent(reader, EDIStreamEvent.SEGMENT_ERROR, EDIStreamValidationError.LOOP_OCCURS_OVER_MAXIMUM_TIMES, "S11", "L0000");
assertEvent(reader, EDIStreamEvent.END_LOOP, "0000D");

assertEvent(reader, EDIStreamEvent.SEGMENT_ERROR, EDIStreamValidationError.IMPLEMENTATION_LOOP_OCCURS_UNDER_MINIMUM_TIMES, "S11");

// Loop L0001 has minOccurs=1 in standard (not used in implementation, invalid configuration)
assertEvent(reader, EDIStreamEvent.SEGMENT_ERROR, EDIStreamValidationError.MANDATORY_SEGMENT_MISSING, "S20");

Expand Down

0 comments on commit 832c597

Please sign in to comment.