Repository: xlate/staedi (java)

Issue:
`getTransactionType()` should be available up to event END_TRANSACTION
It currently throws `IllegalStateException` there.

Pull request:
Make transaction metadata available between begin/end transaction events
Closes #532 

Update the stream reader to enqueue the entire transaction header segment so that client applications have access to transaction type and an accurate transaction version starting with the `START_TRANSACTION` event and through (including) the `END_TRANSACTION` event.

Patch:
diff --git a/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamReader.java b/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamReader.java
index 409f4ea..5355cb6 100644
--- a/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamReader.java
+++ b/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamReader.java
@@ -210,12 +210,14 @@ public class StaEDIStreamReader implements EDIStreamReader, Configurable, Valida
 
         if (!proxy.nextEvent()) {
             proxy.resetEvents();
-            executeTask(lexer::parse, "Error parsing input");
+            do {
+                executeTask(lexer::parse, "Error parsing input");
+            } while (proxy.additionalEventsRequired());
         }
 
         final EDIStreamEvent event = proxy.getEvent();
 
-        LOGGER.finer(() -> "EDI event: " + event);
+        LOGGER.finer(() -> "EDI event: " + event + (event.isError() ? "; error type: " + proxy.getErrorType(): ""));
 
         if (event == EDIStreamEvent.END_INTERCHANGE) {
             executeTask(() -> complete = !proxy.hasNext() && !lexer.hasRemaining(), "Error reading input");
diff --git a/src/main/java/io/xlate/edi/internal/stream/tokenization/ProxyEventHandler.java b/src/main/java/io/xlate/edi/internal/stream/tokenization/ProxyEventHandler.java
index 5514191..3e13669 100644
--- a/src/main/java/io/xlate/edi/internal/stream/tokenization/ProxyEventHandler.java
+++ b/src/main/java/io/xlate/edi/internal/stream/tokenization/ProxyEventHandler.java
@@ -129,6 +129,20 @@ public class ProxyEventHandler implements EventHandler {
         return current(StreamEvent::getType, null);
     }
 
+    public boolean additionalEventsRequired() {
+        if (eventQueue.peekFirst().getType() == EDIStreamEvent.START_TRANSACTION) {
+            /*
+             * When the START_TRANSACTION event is next up in the queue, this will
+             * signal to the reader to continue feeding the lexer until the end of the
+             * transaction header segment so that all metadata (type and version) are
+             * available _starting with_ the START_TRANSACTION.
+             */
+            return eventQueue.peekLast().getType() != EDIStreamEvent.END_SEGMENT;
+        }
+
+        return false;
+    }
+
     public CharBuffer getCharacters() {
         return current(StreamEvent::getData, null);
     }
@@ -143,7 +157,17 @@ public class ProxyEventHandler implements EventHandler {
             return false;
         }
 
-        eventPool.add(eventQueue.removeFirst());
+        StreamEvent lastEvent = eventQueue.removeFirst();
+
+        if (lastEvent.getType() == EDIStreamEvent.END_TRANSACTION) {
+            /*
+             * Retain the transaction metadata in the dialect until after
+             * the reader is past END_TRANSACTION.
+             */
+            dialect.transactionEnd();
+        }
+
+        eventPool.add(lastEvent);
         return !eventQueue.isEmpty();
     }
 
@@ -240,7 +264,6 @@ public class ProxyEventHandler implements EventHandler {
 
         if (LOOP_CODE_TRANSACTION.equals(loopCode)) {
             transaction = false;
-            dialect.transactionEnd();
             enqueueEvent(EDIStreamEvent.END_TRANSACTION, EDIStreamValidationError.NONE, null, typeReference, location);
         } else if (LOOP_CODE_GROUP.equals(loopCode)) {
             dialect.groupEnd();


Test patch:
diff --git a/src/test/java/io/xlate/edi/internal/stream/EventSequenceTest.java b/src/test/java/io/xlate/edi/internal/stream/EventSequenceTest.java
index 2372b15..b992184 100644
--- a/src/test/java/io/xlate/edi/internal/stream/EventSequenceTest.java
+++ b/src/test/java/io/xlate/edi/internal/stream/EventSequenceTest.java
@@ -17,6 +17,7 @@ package io.xlate.edi.internal.stream;
 
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.ByteArrayInputStream;
@@ -636,4 +637,39 @@ class EventSequenceTest {
         assertEquals("E1", e1refs.get(0).getReferencedType().getId());
         assertEquals("E1", e1refs.get(1).getReferencedType().getId());
     }
+
+
+    @Test
+    void testTransactionMetadataAvailable() throws EDIStreamException, IllegalStateException {
+        EDIInputFactory factory = EDIInputFactory.newFactory();
+        InputStream stream = new ByteArrayInputStream((""
+                + "ISA*00*          *00*          *ZZ*ReceiverID     *ZZ*Sender         *050812*1953*^*00501*508121953*0*P*:~"
+                + "GS*FA*ReceiverDept*SenderDept*20050812*195335*000005*X*005010X230~"
+                + "ST*997*0001*005010X230~"
+                + "AK1*HC*000001~"
+                + "AK9*R*1*1*0~"
+                + "SE*8*0001~"
+                + "GE*1*000005~"
+                + "IEA*1*508121953~").getBytes());
+
+        @SuppressWarnings("resource")
+        EDIStreamReader reader = factory.createEDIStreamReader(stream);
+        while (reader.next() != EDIStreamEvent.START_TRANSACTION) {
+            assertThrows(IllegalStateException.class, reader::getTransactionType);
+        }
+
+        assertEquals(EDIStreamEvent.START_TRANSACTION, reader.getEventType());
+
+        do {
+            assertEquals("997", reader.getTransactionType());
+            assertArrayEquals(new String[] { "X", "005010X230" }, reader.getTransactionVersion());
+        } while (reader.next() != EDIStreamEvent.END_TRANSACTION);
+
+        assertEquals(EDIStreamEvent.END_TRANSACTION, reader.getEventType());
+        assertEquals("997", reader.getTransactionType());
+        assertArrayEquals(new String[] { "X", "005010X230" }, reader.getTransactionVersion());
+
+        reader.next();
+        assertThrows(IllegalStateException.class, reader::getTransactionType);
+    }
 }
diff --git a/src/test/resources/logging.properties b/src/test/resources/logging.properties
index b2327fb..dd0149a 100644
--- a/src/test/resources/logging.properties
+++ b/src/test/resources/logging.properties
@@ -2,6 +2,6 @@
 handlers=java.util.logging.ConsoleHandler
 java.util.logging.ConsoleHandler.level=FINEST
 java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
-java.util.logging.SimpleFormatter.format=[%1$tF %1$tT] [%4$-7s] %5$s %n
+java.util.logging.SimpleFormatter.format=[%1$tF %1$tT] [%4$-7s] %3$s %5$s %n
 
 io.xlate.edi.level=FINER