Skip to content

Commit

Permalink
Merge pull request #175 from xlate/174_dialect_specific_state_tables
Browse files Browse the repository at this point in the history
Implement separate state tables for dialect search and each dialect
  • Loading branch information
MikeEdgar committed Feb 21, 2021
2 parents 3028bb4 + bf75262 commit 7d97eb3
Show file tree
Hide file tree
Showing 10 changed files with 400 additions and 135 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ private void write(int output) throws EDIStreamException {
throw new EDIStreamException(String.format("Invalid character: 0x%04X", output), location);
}

state = state.transition(clazz);
state = State.transition(state, dialect, clazz);

switch (state) {
case HEADER_X12_I: // I(SA)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@

public abstract class Dialect {

protected final String[] transactionVersion;
protected final int dialectStateCode;

protected char segmentDelimiter;
protected char segmentTagTerminator = '\0';
protected char elementDelimiter;
Expand All @@ -28,16 +31,28 @@ public abstract class Dialect {
protected char elementRepeater;

protected boolean initialized;
protected boolean rejected;
protected String rejectionMessage;

protected String transactionType;
protected final String[] transactionVersion;
protected String transactionVersionString;

protected Dialect(String[] initialTransactionVersion) {
protected Dialect(int dialectStateCode, String[] initialTransactionVersion) {
this.dialectStateCode = dialectStateCode;
this.transactionVersion = initialTransactionVersion;
}

public static String getStandard(Dialect dialect) {
return dialect != null ? dialect.getStandard() : "UNKNOWN";
}

public int getDialectStateCode() {
return dialectStateCode;
}

public State getTagSearchState() {
return State.TAG_SEARCH;
}

public char getComponentElementSeparator() {
return componentDelimiter;
}
Expand Down Expand Up @@ -75,7 +90,11 @@ public boolean isConfirmed() {
}

public boolean isRejected() {
return rejected;
return rejectionMessage != null;
}

public String getRejectionMessage() {
return rejectionMessage;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class EDIFACTDialect extends Dialect {
private static final int TX_ASSIGNED_CODE = 3;

EDIFACTDialect(String headerTag) {
super(new String[4]);
super(State.DialectCode.EDIFACT, new String[4]);
componentDelimiter = DFLT_COMPONENT_ELEMENT_SEPARATOR;
elementDelimiter = DFLT_DATA_ELEMENT_SEPARATOR;
decimalMark = DFLT_DECIMAL_MARK;
Expand Down Expand Up @@ -100,6 +100,7 @@ boolean initialize(CharacterSet characters) {
characters.setClass(segmentDelimiter, CharacterClass.SEGMENT_DELIMITER);
initialized = true;
} else {
rejectionMessage = "Unable to obtain version from EDIFACT header segment";
initialized = false;
}

Expand Down Expand Up @@ -138,6 +139,14 @@ public boolean isServiceAdviceSegment(CharSequence tag) {
return UNA.contentEquals(tag);
}

@Override
public State getTagSearchState() {
if (isServiceAdviceSegment(this.headerTag)) {
return State.HEADER_EDIFACT_UNB_SEARCH;
}
return State.TAG_SEARCH;
}

@Override
public String getStandard() {
return Standards.EDIFACT;
Expand Down Expand Up @@ -181,7 +190,7 @@ boolean processInterchangeHeader(CharacterSet characters, char value) {
*/
characters.setClass(elementDelimiter, CharacterClass.ELEMENT_DELIMITER);
} else if (segmentDelimiter == value) {
rejected = !initialize(characters);
initialize(characters);
return isConfirmed();
}

Expand Down Expand Up @@ -224,7 +233,7 @@ boolean processServiceStringAdvice(CharacterSet characters, char value) {
header.deleteCharAt(index--);
} else if (isIndexBeyondUNBFirstElement()) {
if (value == elementDelimiter || value == segmentDelimiter) {
rejected = !initialize(characters);
initialize(characters);
proceed = isConfirmed();
}
} else if (value == 'B') {
Expand Down
27 changes: 19 additions & 8 deletions src/main/java/io/xlate/edi/internal/stream/tokenization/Lexer.java
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,13 @@ public void parse() throws IOException, EDIException {

CharacterClass clazz = characters.getClass(input);
previous = state;
state = state.transition(clazz);
LOGGER.finer(() -> "State " + previous + "(" + clazz + ") -> " + state);
state = State.transition(state, dialect, clazz);
LOGGER.finer(() -> String.format("%s + (%s, '%s', %s) -> %s", previous, Dialect.getStandard(dialect), (char) input, clazz, state));

switch (state) {
case INITIAL:
case TAG_SEARCH:
case HEADER_TAG_SEARCH:
case HEADER_EDIFACT_UNB_SEARCH:
break;
case HEADER_X12_I:
case HEADER_X12_S:
Expand Down Expand Up @@ -219,11 +219,12 @@ public void parse() throws IOException, EDIException {
buffer.put((char) input);
}
break;
case HEADER_TAG_1: // U - When UNA is present
case HEADER_TAG_2: // N - When UNA is present
case HEADER_TAG_3: // B - When UNA is present
case HEADER_EDIFACT_UNB_1: // U - When UNA is present
case HEADER_EDIFACT_UNB_2: // N - When UNA is present
case HEADER_EDIFACT_UNB_3: // B - When UNA is present
handleStateHeaderTag(input);
break;
case HEADER_RELEASE:
case DATA_RELEASE:
// Skip this character - next character will be literal value
break;
Expand Down Expand Up @@ -384,7 +385,7 @@ void handleStateHeaderData(int input) throws EDIException {
switch (characters.getClass(input)) {
case SEGMENT_DELIMITER:
closeSegment();
state = State.HEADER_TAG_SEARCH;
state = dialect.getTagSearchState();
break;
case SEGMENT_TAG_DELIMITER:
case ELEMENT_DELIMITER:
Expand All @@ -400,6 +401,15 @@ void handleStateHeaderData(int input) throws EDIException {
}
}

/**
* Determine if the input text has been confirmed by the dialect as being
* initially accepted. If so, transition to the state given by the
* <code>confirmed</code> parameter.
*
* @param confirmed the state to transition to if the dialect is confirmed.
* @return true if the dialect is confirmed, otherwise false.
* @throws EDIException when the input text has been rejected by the dialect.
*/
private boolean dialectConfirmed(State confirmed) throws EDIException {
if (dialect.isConfirmed()) {
state = confirmed;
Expand All @@ -408,9 +418,10 @@ private boolean dialectConfirmed(State confirmed) throws EDIException {
} else if (dialect.isRejected()) {
buffer.clear();
clearQueues();
String rejectionMessage = dialect.getRejectionMessage();
dialect = null;
state = State.INITIAL;
throw error(EDIException.INVALID_STATE, "Invalid header segment");
throw error(EDIException.INVALID_STATE, rejectionMessage);
}

return false;
Expand Down

0 comments on commit 7d97eb3

Please sign in to comment.