Repository: xlate/staedi (java)

Issue:
Align EDIFACT Decimal Handling to Standard
* Version 3 and earlier: use position 3 of service advice (`UNA`)
* Version 4 and later: **ignore** position 3 of service advice (`UNA`) and support either comma `,` or full stop `.` as decimal notation characters in numeric fields.

Pull request description:
Ignore decimal value from service advice segment, version 4 and after
Fixes #145 

Patch:
diff --git a/src/main/java/io/xlate/edi/internal/stream/tokenization/Dialect.java b/src/main/java/io/xlate/edi/internal/stream/tokenization/Dialect.java
index af206f5..0b7e9eb 100644
--- a/src/main/java/io/xlate/edi/internal/stream/tokenization/Dialect.java
+++ b/src/main/java/io/xlate/edi/internal/stream/tokenization/Dialect.java
@@ -50,6 +50,10 @@ public abstract class Dialect {
         return segmentDelimiter;
     }
 
+    public boolean isDecimalMark(char value) {
+        return value == getDecimalMark();
+    }
+
     public abstract String getStandard();
 
     public abstract String[] getVersion();
diff --git a/src/main/java/io/xlate/edi/internal/stream/tokenization/EDIFACTDialect.java b/src/main/java/io/xlate/edi/internal/stream/tokenization/EDIFACTDialect.java
index e095b55..e4520d8 100644
--- a/src/main/java/io/xlate/edi/internal/stream/tokenization/EDIFACTDialect.java
+++ b/src/main/java/io/xlate/edi/internal/stream/tokenization/EDIFACTDialect.java
@@ -31,6 +31,7 @@ public class EDIFACTDialect extends Dialect {
     static final char DFLT_REPETITION_SEPARATOR = '*';
     static final char DFLT_RELEASE_CHARACTER = '?';
     static final char DFLT_DECIMAL_MARK = '.';
+    static final char ALT_DECIMAL_MARK = ',';
 
     private static final int EDIFACT_UNA_LENGTH = 9;
 
@@ -41,6 +42,7 @@ public class EDIFACTDialect extends Dialect {
     private int unbStart = -1;
     private boolean initialized;
     private boolean rejected;
+    private boolean ignoreDecimalAdvice;
 
     private static final int TX_AGENCY = 0;
     private static final int TX_VERSION = 1;
@@ -70,12 +72,22 @@ public class EDIFACTDialect extends Dialect {
         return headerTag;
     }
 
+    @Override
+    public boolean isDecimalMark(char value) {
+        if (!this.ignoreDecimalAdvice) {
+            return super.isDecimalMark(value);
+        }
+
+        return value == DFLT_DECIMAL_MARK || value == ALT_DECIMAL_MARK;
+    }
+
     boolean initialize(CharacterSet characters) {
         String[] parsedVersion = parseVersion();
 
         if (parsedVersion.length > 1) {
             this.version = parsedVersion;
             final String syntaxVersion = this.version[1];
+            this.ignoreDecimalAdvice = syntaxVersion.compareTo("4") >= 0;
 
             characters.setClass(componentDelimiter, CharacterClass.COMPONENT_DELIMITER);
             characters.setClass(elementDelimiter, CharacterClass.ELEMENT_DELIMITER);
diff --git a/src/main/java/io/xlate/edi/internal/stream/validation/DecimalValidator.java b/src/main/java/io/xlate/edi/internal/stream/validation/DecimalValidator.java
index fe1fa77..a2eb705 100644
--- a/src/main/java/io/xlate/edi/internal/stream/validation/DecimalValidator.java
+++ b/src/main/java/io/xlate/edi/internal/stream/validation/DecimalValidator.java
@@ -30,7 +30,6 @@ class DecimalValidator extends NumericValidator {
 
     @Override
     int validate(Dialect dialect, CharSequence value) {
-        final char decimalMark = dialect.getDecimalMark();
         int length = value.length();
 
         int dec = 0;
@@ -65,7 +64,7 @@ class DecimalValidator extends NumericValidator {
                 break;
 
             default:
-                if (value.charAt(i) == decimalMark) {
+                if (dialect.isDecimalMark(value.charAt(i))) {
                     length--;
                     invalid = !validDecimalSymbol(++dec, exp, invalid);
                 } else {


Test patch:
diff --git a/src/test/java/io/xlate/edi/internal/stream/tokenization/EDIFACTDialectTest.java b/src/test/java/io/xlate/edi/internal/stream/tokenization/EDIFACTDialectTest.java
index 9243696..3a5dfbe 100644
--- a/src/test/java/io/xlate/edi/internal/stream/tokenization/EDIFACTDialectTest.java
+++ b/src/test/java/io/xlate/edi/internal/stream/tokenization/EDIFACTDialectTest.java
@@ -96,6 +96,18 @@ class EDIFACTDialectTest {
         assertEquals(' ', edifact.getReleaseIndicator());
     }
 
+    @Test
+    void testDecimalMarkIgnoredVersion4() throws EDIException {
+        EDIFACTDialect edifact = (EDIFACTDialect) DialectFactory.getDialect("UNA".toCharArray(), 0, 3);
+        CharacterSet characters = new CharacterSet();
+        "UNA:+_ *'UNB+UNOA:4+111111111:1+222222222:1+200726:1455+1'".chars().forEach(c -> edifact.appendHeader(characters, (char) c));
+
+        assertTrue(edifact.initialize(characters));
+        assertTrue(edifact.isDecimalMark('.'));
+        assertTrue(edifact.isDecimalMark(','));
+        assertFalse(edifact.isDecimalMark('_'));
+    }
+
     @Test
     void testBlankSegmentTermPreVersion4() throws EDIException {
         EDIFACTDialect edifact = (EDIFACTDialect) DialectFactory.getDialect("UNA".toCharArray(), 0, 3);
@@ -111,6 +123,17 @@ class EDIFACTDialectTest {
         assertEquals('\\', edifact.getReleaseIndicator());
     }
 
+    @Test
+    void testDecimalMarkUsedPreVersion4() throws EDIException {
+        EDIFACTDialect edifact = (EDIFACTDialect) DialectFactory.getDialect("UNA".toCharArray(), 0, 3);
+        CharacterSet characters = new CharacterSet();
+        "UNA:+,\\* UNB+UNOA:3+111111111:1+222222222:1+200726:1455+1 ".chars().forEach(c -> edifact.appendHeader(characters, (char) c));
+
+        assertTrue(edifact.initialize(characters));
+        assertFalse(edifact.isDecimalMark('.'));
+        assertTrue(edifact.isDecimalMark(','));
+    }
+
     @Test
     void testBlankVersionUNA() throws EDIException {
         EDIFACTDialect edifact = (EDIFACTDialect) DialectFactory.getDialect("UNA".toCharArray(), 0, 3);