diff --git a/artio-codecs/src/main/java/uk/co/real_logic/artio/builder/CommonDecoderImpl.java b/artio-codecs/src/main/java/uk/co/real_logic/artio/builder/CommonDecoderImpl.java index aa952aba01..e3158aa3d3 100644 --- a/artio-codecs/src/main/java/uk/co/real_logic/artio/builder/CommonDecoderImpl.java +++ b/artio-codecs/src/main/java/uk/co/real_logic/artio/builder/CommonDecoderImpl.java @@ -31,6 +31,7 @@ public abstract class CommonDecoderImpl protected int invalidTagId = Decoder.NO_ERROR; protected int rejectReason = Decoder.NO_ERROR; protected AsciiBuffer buffer; + protected UnknownTagVisitor unknownTagVisitor; public int invalidTagId() { @@ -42,6 +43,11 @@ public int rejectReason() return rejectReason; } + public void setUnknownTagVisitor(final UnknownTagVisitor unknownTagVisitor) + { + this.unknownTagVisitor = unknownTagVisitor; + } + public int getInt( final AsciiBuffer buffer, final int startInclusive, final int endExclusive, final int tag, final boolean validation) diff --git a/artio-codecs/src/main/java/uk/co/real_logic/artio/builder/Decoder.java b/artio-codecs/src/main/java/uk/co/real_logic/artio/builder/Decoder.java index fa5111661b..f822c3a38f 100644 --- a/artio-codecs/src/main/java/uk/co/real_logic/artio/builder/Decoder.java +++ b/artio-codecs/src/main/java/uk/co/real_logic/artio/builder/Decoder.java @@ -91,4 +91,11 @@ public interface Decoder extends CharAppender * @return the encoder passed as an argument. */ Encoder toEncoder(Encoder encoder); + + /** + * Sets a visitor to be called when an unknown tag is encountered while decoding a message. + * + * @param visitor the visitor to invoke + */ + void setUnknownTagVisitor(UnknownTagVisitor visitor); } diff --git a/artio-codecs/src/main/java/uk/co/real_logic/artio/builder/UnknownTagVisitor.java b/artio-codecs/src/main/java/uk/co/real_logic/artio/builder/UnknownTagVisitor.java new file mode 100644 index 0000000000..5503d50b37 --- /dev/null +++ b/artio-codecs/src/main/java/uk/co/real_logic/artio/builder/UnknownTagVisitor.java @@ -0,0 +1,26 @@ +package uk.co.real_logic.artio.builder; + +import uk.co.real_logic.artio.util.AsciiBuffer; + +/** + * Interface for visiting unknown tags. + * + * @see CommonDecoderImpl#setUnknownTagVisitor(UnknownTagVisitor) + */ +public interface UnknownTagVisitor +{ + /** + * Called when an unknown tag is encountered while decoding a message. + * + * @param tag The tag number of the unknown tag + * @param buffer The buffer containing the unknown tag + * @param offset The offset at which the unknown tag starts + * @param length The length of the unknown tag + */ + void onUnknownTag( + int tag, + AsciiBuffer buffer, + int offset, + int length + ); +} diff --git a/artio-codecs/src/main/java/uk/co/real_logic/artio/dictionary/generation/DecoderGenerator.java b/artio-codecs/src/main/java/uk/co/real_logic/artio/dictionary/generation/DecoderGenerator.java index 54fc32a812..a3d7439b15 100644 --- a/artio-codecs/src/main/java/uk/co/real_logic/artio/dictionary/generation/DecoderGenerator.java +++ b/artio-codecs/src/main/java/uk/co/real_logic/artio/dictionary/generation/DecoderGenerator.java @@ -1705,7 +1705,10 @@ private String decodeMethod(final List entries, final Aggregate aggregate " unknownFields.add(tag);\n" + " }\n" + " }\n") + - + " if (unknownTagVisitor != null)\n" + + " {\n" + + " unknownTagVisitor.onUnknownTag(tag, buffer, valueOffset, valueLength);\n" + + " }\n" + // Skip the thing if it's a completely unknown field and you aren't validating messages " if (" + CODEC_REJECT_UNKNOWN_FIELD_ENABLED + " || " + unknownFieldPredicate(type) + ")\n" + diff --git a/artio-codecs/src/test/java/uk/co/real_logic/artio/dictionary/ExampleDictionary.java b/artio-codecs/src/test/java/uk/co/real_logic/artio/dictionary/ExampleDictionary.java index 85a464f37d..a78224d759 100644 --- a/artio-codecs/src/test/java/uk/co/real_logic/artio/dictionary/ExampleDictionary.java +++ b/artio-codecs/src/test/java/uk/co/real_logic/artio/dictionary/ExampleDictionary.java @@ -244,6 +244,10 @@ public final class ExampleDictionary "8=FIX.4.4\0019=53\00135=0\001115=abc\001116=2\001117=A\001127=19700101-00:00:00.001" + "\00110=043\001"; + public static final String UNKNOWN_TAG_MESSAGE = + "8=FIX.4.4\0019=53\00135=0\001115=abc\00110100=FOO\001116=2\001117=A\00110101=BAR\001127=19700101-00:00:00.001" + + "\00110=043\001"; + public static final String OUT_OF_RANGE_FLOAT_VALUE_MESSAGE = "8=FIX.4.4\0019=53\00135=0\001115=abc\001116=2\001117=10000000000000000000000\001" + "127=19700101-00:00:00.001\00110=043\001"; diff --git a/artio-codecs/src/test/java/uk/co/real_logic/artio/dictionary/generation/DecoderGeneratorFlyweightTest.java b/artio-codecs/src/test/java/uk/co/real_logic/artio/dictionary/generation/DecoderGeneratorFlyweightTest.java index 703064071f..2088765ec8 100644 --- a/artio-codecs/src/test/java/uk/co/real_logic/artio/dictionary/generation/DecoderGeneratorFlyweightTest.java +++ b/artio-codecs/src/test/java/uk/co/real_logic/artio/dictionary/generation/DecoderGeneratorFlyweightTest.java @@ -19,6 +19,10 @@ import org.junit.BeforeClass; import org.junit.Test; import uk.co.real_logic.artio.builder.Decoder; +import uk.co.real_logic.artio.builder.UnknownTagVisitor; +import uk.co.real_logic.artio.util.AsciiBuffer; + +import java.util.HashMap; import static org.junit.Assert.assertEquals; import static uk.co.real_logic.artio.dictionary.ExampleDictionary.*; @@ -65,4 +69,15 @@ public void shouldNotThrowWhenAccessingUnsetString() throws Exception assertTargetThrows(() -> getAsciiSequenceView(decoder, "testReqID"), IllegalArgumentException.class, "No value for optional field: TestReqID"); } + + @Test + public void shouldVisitUnknownTags() throws Exception + { + final Decoder decoder = newHeartbeat(); + final HashMap map = new HashMap<>(); + decoder.setUnknownTagVisitor((tag, buffer, offset, length) -> map.put(tag, buffer.getAscii(offset, length))); + decode(UNKNOWN_TAG_MESSAGE, decoder); + assertEquals("FOO", map.get(10100)); + assertEquals("BAR", map.get(10101)); + } }