From 643a64dd23e1f0cf7f788b41a754e11a81b26323 Mon Sep 17 00:00:00 2001 From: pcdv Date: Wed, 6 Jul 2022 17:51:50 +0200 Subject: [PATCH] Allow setting custom FIX tags in encoders (WIP) #462 --- .../artio/builder/CommonEncoderImpl.java | 45 +++++++++++++++++++ .../generation/EncoderGenerator.java | 14 +++++- .../artio/dictionary/ExampleDictionary.java | 7 +++ .../generation/EncoderGeneratorTest.java | 14 ++++++ 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 artio-codecs/src/main/java/uk/co/real_logic/artio/builder/CommonEncoderImpl.java diff --git a/artio-codecs/src/main/java/uk/co/real_logic/artio/builder/CommonEncoderImpl.java b/artio-codecs/src/main/java/uk/co/real_logic/artio/builder/CommonEncoderImpl.java new file mode 100644 index 0000000000..12adea47f2 --- /dev/null +++ b/artio-codecs/src/main/java/uk/co/real_logic/artio/builder/CommonEncoderImpl.java @@ -0,0 +1,45 @@ +package uk.co.real_logic.artio.builder; + +import uk.co.real_logic.artio.util.MutableAsciiBuffer; + +/** + * Class provides common implementation methods used by encoders. + */ +public class CommonEncoderImpl +{ + // TODO resizable buffer or way to set a size + protected MutableAsciiBuffer customTagsBuffer = new MutableAsciiBuffer(new byte[64]); + + protected int customTagsLength = 0; + + private int putTagHeader(final int tag) + { + int pos = customTagsLength; + pos += customTagsBuffer.putIntAscii(pos, tag); + customTagsBuffer.putByte(pos++, (byte)'='); + return pos; + } + + public CommonEncoderImpl customTag(final int tag, final int value) + { + int pos = putTagHeader(tag); + pos += customTagsBuffer.putIntAscii(pos, value); + customTagsBuffer.putSeparator(pos++); + customTagsLength = pos; + return this; + } + + public CommonEncoderImpl customTagAscii(final int tag, final CharSequence value) + { + int pos = putTagHeader(tag); + pos += customTagsBuffer.putStringWithoutLengthAscii(pos, value); + customTagsBuffer.putSeparator(pos++); + customTagsLength = pos; + return this; + } + + public void resetCustomTags() + { + customTagsLength = 0; + } +} diff --git a/artio-codecs/src/main/java/uk/co/real_logic/artio/dictionary/generation/EncoderGenerator.java b/artio-codecs/src/main/java/uk/co/real_logic/artio/dictionary/generation/EncoderGenerator.java index e4cbefa608..1d88926e0c 100644 --- a/artio-codecs/src/main/java/uk/co/real_logic/artio/dictionary/generation/EncoderGenerator.java +++ b/artio-codecs/src/main/java/uk/co/real_logic/artio/dictionary/generation/EncoderGenerator.java @@ -20,6 +20,7 @@ import org.agrona.MutableDirectBuffer; import org.agrona.concurrent.UnsafeBuffer; import org.agrona.generation.OutputManager; +import uk.co.real_logic.artio.builder.CommonEncoderImpl; import uk.co.real_logic.artio.builder.Encoder; import uk.co.real_logic.artio.builder.FieldBagEncoder; import uk.co.real_logic.artio.builder.SessionHeaderEncoder; @@ -210,6 +211,7 @@ protected void generateAggregateFile(final Aggregate aggregate, final AggregateT MutableDirectBuffer.class, UnsafeBuffer.class, AsciiSequenceView.class, + CommonEncoderImpl.class, FieldBagEncoder.class); generateAggregateClass(aggregate, aggregateType, className, out); }); @@ -342,7 +344,7 @@ private String classDeclaration( } else { - extendsClause = ""; + extendsClause = " extends CommonEncoderImpl"; } return String.format( "\n" + @@ -368,6 +370,9 @@ private String completeResetMethod( case HEADER: additionalReset = " beginStringAsCopy(DEFAULT_BEGIN_STRING, 0, DEFAULT_BEGIN_STRING.length);\n"; break; + case MESSAGE: + additionalReset = " resetCustomTags();\n"; + break; default: additionalReset = ""; } @@ -994,6 +999,13 @@ private String encodeMethod(final List entries, final AggregateType aggre if (aggregateType == AggregateType.MESSAGE) { suffix = + "\n" + + " if (customTagsLength > 0)\n" + + " {\n" + + " buffer.putBytes(position, customTagsBuffer, 0, customTagsLength);\n" + + " position += customTagsLength;\n" + + " }\n" + + "\n" + " position += trailer.startTrailer(buffer, position);\n" + "\n" + " final int messageStart = header.finishHeader(buffer, bodyStart, position - bodyStart);\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 f9bf351d36..aab7213edf 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 @@ -418,6 +418,13 @@ public final class ExampleDictionary "8=FIX.4.4\0019=91\00135=0\001115=abc\001116=2\001117=1.1\001127=19700101-00:00:00.001" + "\001124=2\001130=2\001131=1\001404=10\001131=2\001404=20\00110=176\001"; + public static final String WITH_CUSTOM_TAGS = + "8=FIX.4.4\0019=110\00135=0\001115=abc\001116=2\001117=1.1\001127=19700101-00:00:00.001" + + "\001124=2\001130=2\001131=1\001404=10\001131=2\001404=20" + + "\00110100=42" + + "\00110101=foo" + + "\00110=227\001"; + public static final String NESTED_COMPONENT_MESSAGE = "8=FIX.4.4\0019=120\00135=0\001115=abc\001116=2\001117=1.1\001127=19700101-00:00:00.001" + "\001124=2\001130=2\001131=1\001404=10\001131=2\001404=20\001141=180\001142=2\001143=99\001143=100\001" + diff --git a/artio-codecs/src/test/java/uk/co/real_logic/artio/dictionary/generation/EncoderGeneratorTest.java b/artio-codecs/src/test/java/uk/co/real_logic/artio/dictionary/generation/EncoderGeneratorTest.java index 7312cac69e..0e4ea88e1b 100644 --- a/artio-codecs/src/test/java/uk/co/real_logic/artio/dictionary/generation/EncoderGeneratorTest.java +++ b/artio-codecs/src/test/java/uk/co/real_logic/artio/dictionary/generation/EncoderGeneratorTest.java @@ -23,6 +23,7 @@ import org.junit.BeforeClass; import org.junit.Test; import uk.co.real_logic.artio.EncodingException; +import uk.co.real_logic.artio.builder.CommonEncoderImpl; import uk.co.real_logic.artio.builder.Encoder; import uk.co.real_logic.artio.builder.FieldBagEncoder; import uk.co.real_logic.artio.fields.DecimalFloat; @@ -737,6 +738,19 @@ public void shouldValidateMissingRequiredFloatFields() throws Exception encoder.encode(buffer, 1); } + @Test + public void shouldSetCustomTags() throws Exception + { + final Encoder encoder = newHeartbeat(); + setRequiredFields(encoder); + setupComponent(encoder); + + ((CommonEncoderImpl)encoder).customTag(10100, 42); + ((CommonEncoderImpl)encoder).customTagAscii(10101, "foo"); + + assertEncodesTo(encoder, WITH_CUSTOM_TAGS); + } + @Test(expected = EncodingException.class) public void shouldValidateMissingRequiredIntFields() throws Exception {