From d6c15fcc8f9a8a371a7a9e694fd73db6c9a128f3 Mon Sep 17 00:00:00 2001 From: Andrii Poletaiev Date: Wed, 6 Sep 2023 17:11:30 +0200 Subject: [PATCH 01/10] Add property to set repeating group delimiter from first tag in group --- .../main/java/quickfix/DataDictionary.java | 19 +++++++++++++++++++ .../src/main/java/quickfix/Message.java | 6 ++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/quickfixj-core/src/main/java/quickfix/DataDictionary.java b/quickfixj-core/src/main/java/quickfix/DataDictionary.java index d4113242c4..9770f8a9e9 100644 --- a/quickfixj-core/src/main/java/quickfix/DataDictionary.java +++ b/quickfixj-core/src/main/java/quickfix/DataDictionary.java @@ -93,6 +93,7 @@ private static Supplier createDocumentBuilderFactorySupp private boolean checkUserDefinedFields = true; private boolean checkUnorderedGroupFields = true; private boolean allowUnknownMessageFields = false; + private boolean firstFieldInGroupIsDelimiter = false; private String beginString; private String fullVersion; private String majorVersion; @@ -561,6 +562,24 @@ public boolean isAllowUnknownMessageFields() { return allowUnknownMessageFields; } + public boolean isFirstFieldInGroupIsDelimiter() { + return firstFieldInGroupIsDelimiter; + } + /** + * Controls whether any field which is + * first in the group would be used as delimiter + * + * @param flag true = use first field from message, false = follow data dictionary + */ + public void setFirstFieldInGroupIsDelimiter(boolean flag) { + firstFieldInGroupIsDelimiter = flag; + for (Map gm : groups.values()) { + for (GroupInfo gi : gm.values()) { + gi.getDataDictionary().setFirstFieldInGroupIsDelimiter(flag); + } + } + } + /** * Controls whether group fields are in the same order * diff --git a/quickfixj-core/src/main/java/quickfix/Message.java b/quickfixj-core/src/main/java/quickfix/Message.java index c974021e03..52a3457fb4 100644 --- a/quickfixj-core/src/main/java/quickfix/Message.java +++ b/quickfixj-core/src/main/java/quickfix/Message.java @@ -723,7 +723,7 @@ private void parseGroup(String msgType, StringField field, DataDictionary dd, Da throw MessageUtils.newInvalidMessageException("Repeating group count requires an Integer but found '" + field.getValue() + "' in " + messageData, this); } parent.setField(groupCountTag, field); - final int firstField = rg.getDelimiterField(); + int firstField = groupDataDictionary.isFirstFieldInGroupIsDelimiter() ? -1 : rg.getDelimiterField(); Group group = null; boolean inGroupParse = true; while (inGroupParse) { @@ -733,7 +733,9 @@ private void parseGroup(String msgType, StringField field, DataDictionary dd, Da break; } int tag = field.getTag(); - if (tag == firstField) { + boolean shouldCreateNewGroup = tag == firstField || (groupDataDictionary.isFirstFieldInGroupIsDelimiter() && firstField < 0); + if (shouldCreateNewGroup) { + firstField = tag; addGroupRefToParent(group, parent); group = new Group(groupCountTag, firstField, groupDataDictionary.getOrderedFields()); group.setField(field); From e60e3dbc48ab48e2a5ed1f0b6ec39d62918923c3 Mon Sep 17 00:00:00 2001 From: Andrii Poletaiev Date: Tue, 12 Sep 2023 13:58:24 +0200 Subject: [PATCH 02/10] Add property to set repeating group delimiter from first tag in group. Add tests --- .../main/java/quickfix/DataDictionary.java | 2 + .../java/quickfix/DataDictionaryTest.java | 51 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/quickfixj-core/src/main/java/quickfix/DataDictionary.java b/quickfixj-core/src/main/java/quickfix/DataDictionary.java index 9770f8a9e9..e375fa45fc 100644 --- a/quickfixj-core/src/main/java/quickfix/DataDictionary.java +++ b/quickfixj-core/src/main/java/quickfix/DataDictionary.java @@ -570,6 +570,7 @@ public boolean isFirstFieldInGroupIsDelimiter() { * first in the group would be used as delimiter * * @param flag true = use first field from message, false = follow data dictionary + * Must be used with enabled {@link #setCheckUnorderedGroupFields(boolean)} */ public void setFirstFieldInGroupIsDelimiter(boolean flag) { firstFieldInGroupIsDelimiter = flag; @@ -657,6 +658,7 @@ private void copyFrom(DataDictionary rhs) { setCheckUserDefinedFields(rhs.checkUserDefinedFields); setCheckUnorderedGroupFields(rhs.checkUnorderedGroupFields); setAllowUnknownMessageFields(rhs.allowUnknownMessageFields); + setFirstFieldInGroupIsDelimiter(rhs.firstFieldInGroupIsDelimiter); calculateOrderedFields(); } diff --git a/quickfixj-core/src/test/java/quickfix/DataDictionaryTest.java b/quickfixj-core/src/test/java/quickfix/DataDictionaryTest.java index e660f5d8bc..68e5fde1f3 100644 --- a/quickfixj-core/src/test/java/quickfix/DataDictionaryTest.java +++ b/quickfixj-core/src/test/java/quickfix/DataDictionaryTest.java @@ -54,6 +54,7 @@ import quickfix.field.TargetCompID; import quickfix.field.TimeInForce; import quickfix.field.TransactTime; +import quickfix.fix44.NewOrderMultileg; import quickfix.fix44.NewOrderSingle; import quickfix.fix44.Quote; import quickfix.fix44.QuoteRequest; @@ -78,6 +79,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; public class DataDictionaryTest { @@ -1493,6 +1495,55 @@ private String getCommonDataDictionaryString(String data) { return data; } + @Test + public void testFirstFieldInGroupIsDelimiter() throws Exception { + + final DataDictionary dataDictionary = new DataDictionary(getDictionary()); + dataDictionary.setCheckUnorderedGroupFields(false); + dataDictionary.setFirstFieldInGroupIsDelimiter(true); + + String fixMsg = "8=FIX.4.4\u00019=688\u000135=AB\u000149=AAA\u000156=BBB\u000134=21133\u000150=ABCABC" + + "\u000152=20230905-13:24:37.022\u000155=AAPL\u00011=ACC1\u000111=123456abcedf\u000121=1\u000138=5\u000154=1\u000140=2\u000144=-0.8" + + "\u000159=0\u000160=20230905-13:24:36.984\u0001100=ALGO\u0001167=MLEG\u0001555=3\u0001602=111\u0001600=AAA" + + "\u0001602=222\u0001654=231\u0001600=BBB\u0001602=333\u0001654=332\u0001600=CCC\u000158=TEXT\u000110=168\u0001"; + + String byDictFixMsg = "8=FIX.4.4\u00019=688\u000135=AB\u000149=AAA\u000156=BBB\u000134=21133\u000150=ABCABC" + + "\u000152=20230905-13:24:37.022\u000155=AAPL\u00011=ACC1\u000111=123456abcedf\u000121=1\u000138=5\u000154=1\u000140=2\u000144=-0.8" + + "\u000159=0\u000160=20230905-13:24:36.984\u0001100=ALGO\u0001167=MLEG\u0001555=3\u0001600=AAA\u0001602=111" + + "\u0001600=BBB\u0001602=222\u0001654=231\u0001600=CCC\u0001602=333\u0001654=332\u000158=TEXT\u000110=168\u0001"; + + //doValidation and firstFieldInGroupIsDelimiter -> should NOT fail + final NewOrderMultileg noml1 = new NewOrderMultileg(); + noml1.fromString(fixMsg, dataDictionary, true); + dataDictionary.validate(noml1); + assertTrue(noml1.hasGroup(555)); + assertEquals(3, noml1.getGroupCount(555)); + //delimiter should be first tag in group + assertEquals(602, noml1.getGroup(1, 555).delim()); + + dataDictionary.setFirstFieldInGroupIsDelimiter(false); + dataDictionary.setCheckUnorderedGroupFields(true); + final NewOrderMultileg noml2 = new NewOrderMultileg(); + noml2.fromString(fixMsg, dataDictionary, true); + //when firstFieldInGroupIsDelimiter = false and setCheckUnorderedGroupFields = true - exception is thrown + assertThrows(FieldException.class, () -> dataDictionary.validate(noml2)); + + final NewOrderMultileg noml3 = new NewOrderMultileg(); + noml3.fromString(fixMsg, dataDictionary, true); + //when firstFieldInGroupIsDelimiter = true and setCheckUnorderedGroupFields = true - exception is thrown + assertThrows(FieldException.class, () -> dataDictionary.validate(noml3)); + + dataDictionary.setCheckUnorderedGroupFields(true); + final NewOrderMultileg noml4 = new NewOrderMultileg(); + noml4.fromString(byDictFixMsg, dataDictionary, true); + //when firstFieldInGroupIsDelimiter = true and setCheckUnorderedGroupFields = true, message aligns with dictionary - do NOT fail + dataDictionary.validate(noml4); + assertTrue(noml4.hasGroup(555)); + assertEquals(3, noml4.getGroupCount(555)); + //delimiter should be dictionary first tag = 600 + assertEquals(600, noml4.getGroup(1, 555).delim()); + } + // // Group Validation Tests in RepeatingGroupTest From a2b7ed322731a0081dea3b3e07071c5ba5183ed2 Mon Sep 17 00:00:00 2001 From: Andrii Poletaiev Date: Thu, 14 Sep 2023 14:17:52 +0200 Subject: [PATCH 03/10] Add property to set repeating group delimiter from first tag in group. Add configuration setting and documentation --- .../src/main/doc/usermanual/usage/configuration.html | 8 ++++++++ .../src/main/java/quickfix/DefaultSessionFactory.java | 5 +++++ quickfixj-core/src/main/java/quickfix/Session.java | 6 ++++++ 3 files changed, 19 insertions(+) diff --git a/quickfixj-core/src/main/doc/usermanual/usage/configuration.html b/quickfixj-core/src/main/doc/usermanual/usage/configuration.html index 6708d0e9ca..2d3bbbde32 100644 --- a/quickfixj-core/src/main/doc/usermanual/usage/configuration.html +++ b/quickfixj-core/src/main/doc/usermanual/usage/configuration.html @@ -464,6 +464,14 @@

QuickFIX Settings

Y
N Y + + FirstFieldInGroupIsDelimiter + Session validation setting for enabling whether first found field in repeating group will be used as + delimiter. Values are "Y" or "N". Default is "N". + Y
+ N + N + Initiator diff --git a/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java b/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java index cd83b72206..2a73af517f 100644 --- a/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java +++ b/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java @@ -309,6 +309,11 @@ private DataDictionary createDataDictionary(SessionID sessionID, SessionSettings Session.SETTING_ALLOW_UNKNOWN_MSG_FIELDS)); } + if (settings.isSetting(sessionID, Session.SETTING_FIRST_FIELD_IN_GROUP_IS_DELIMITER)) { + dataDictionary.setCheckUnorderedGroupFields(settings.getBool(sessionID, + Session.SETTING_FIRST_FIELD_IN_GROUP_IS_DELIMITER)); + } + return dataDictionary; } diff --git a/quickfixj-core/src/main/java/quickfix/Session.java b/quickfixj-core/src/main/java/quickfix/Session.java index 7e52be07bf..9159fb4a7f 100644 --- a/quickfixj-core/src/main/java/quickfix/Session.java +++ b/quickfixj-core/src/main/java/quickfix/Session.java @@ -376,6 +376,12 @@ public class Session implements Closeable { */ public static final String SETTING_RESEND_REQUEST_CHUNK_SIZE = "ResendRequestChunkSize"; + /** + * Session validation setting for enabling whether first found field in repeating group will be used as + * delimiter. Values are "Y" or "N". Default is "N". + */ + public static final String SETTING_FIRST_FIELD_IN_GROUP_IS_DELIMITER = "FirstFieldInGroupIsDelimiter"; + public static final String SETTING_MAX_SCHEDULED_WRITE_REQUESTS = "MaxScheduledWriteRequests"; public static final String SETTING_VALIDATE_CHECKSUM = "ValidateChecksum"; From f6f24d25c57f2ffebebe2df3310f9b76c4ff3be6 Mon Sep 17 00:00:00 2001 From: Andrii Poletaiev Date: Thu, 14 Sep 2023 14:18:52 +0200 Subject: [PATCH 04/10] Add property to set repeating group delimiter from first tag in group. --- .../src/main/java/quickfix/DefaultSessionFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java b/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java index 2a73af517f..3f4f76e2f7 100644 --- a/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java +++ b/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java @@ -310,7 +310,7 @@ private DataDictionary createDataDictionary(SessionID sessionID, SessionSettings } if (settings.isSetting(sessionID, Session.SETTING_FIRST_FIELD_IN_GROUP_IS_DELIMITER)) { - dataDictionary.setCheckUnorderedGroupFields(settings.getBool(sessionID, + dataDictionary.setFirstFieldInGroupIsDelimiter(settings.getBool(sessionID, Session.SETTING_FIRST_FIELD_IN_GROUP_IS_DELIMITER)); } From 502913468389808e8dd600bf307ed84e1087692f Mon Sep 17 00:00:00 2001 From: Andrii Poletaiev Date: Mon, 18 Sep 2023 12:13:11 +0200 Subject: [PATCH 05/10] Adjust javadocs. Fix tests. --- .../main/java/quickfix/DataDictionary.java | 2 +- .../src/main/java/quickfix/Message.java | 2 +- .../java/quickfix/DataDictionaryTest.java | 32 ++++++++++++------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/quickfixj-core/src/main/java/quickfix/DataDictionary.java b/quickfixj-core/src/main/java/quickfix/DataDictionary.java index e375fa45fc..6be6346dae 100644 --- a/quickfixj-core/src/main/java/quickfix/DataDictionary.java +++ b/quickfixj-core/src/main/java/quickfix/DataDictionary.java @@ -570,7 +570,7 @@ public boolean isFirstFieldInGroupIsDelimiter() { * first in the group would be used as delimiter * * @param flag true = use first field from message, false = follow data dictionary - * Must be used with enabled {@link #setCheckUnorderedGroupFields(boolean)} + * Must be used with disabled {@link #setCheckUnorderedGroupFields(boolean)} */ public void setFirstFieldInGroupIsDelimiter(boolean flag) { firstFieldInGroupIsDelimiter = flag; diff --git a/quickfixj-core/src/main/java/quickfix/Message.java b/quickfixj-core/src/main/java/quickfix/Message.java index 52a3457fb4..fa0ede882a 100644 --- a/quickfixj-core/src/main/java/quickfix/Message.java +++ b/quickfixj-core/src/main/java/quickfix/Message.java @@ -733,7 +733,7 @@ private void parseGroup(String msgType, StringField field, DataDictionary dd, Da break; } int tag = field.getTag(); - boolean shouldCreateNewGroup = tag == firstField || (groupDataDictionary.isFirstFieldInGroupIsDelimiter() && firstField < 0); + boolean shouldCreateNewGroup = tag == firstField || (groupDataDictionary.isFirstFieldInGroupIsDelimiter() && firstField == -1); if (shouldCreateNewGroup) { firstField = tag; addGroupRefToParent(group, parent); diff --git a/quickfixj-core/src/test/java/quickfix/DataDictionaryTest.java b/quickfixj-core/src/test/java/quickfix/DataDictionaryTest.java index 68e5fde1f3..fc706b5987 100644 --- a/quickfixj-core/src/test/java/quickfix/DataDictionaryTest.java +++ b/quickfixj-core/src/test/java/quickfix/DataDictionaryTest.java @@ -1499,8 +1499,6 @@ private String getCommonDataDictionaryString(String data) { public void testFirstFieldInGroupIsDelimiter() throws Exception { final DataDictionary dataDictionary = new DataDictionary(getDictionary()); - dataDictionary.setCheckUnorderedGroupFields(false); - dataDictionary.setFirstFieldInGroupIsDelimiter(true); String fixMsg = "8=FIX.4.4\u00019=688\u000135=AB\u000149=AAA\u000156=BBB\u000134=21133\u000150=ABCABC" + "\u000152=20230905-13:24:37.022\u000155=AAPL\u00011=ACC1\u000111=123456abcedf\u000121=1\u000138=5\u000154=1\u000140=2\u000144=-0.8" + @@ -1512,36 +1510,48 @@ public void testFirstFieldInGroupIsDelimiter() throws Exception { "\u000159=0\u000160=20230905-13:24:36.984\u0001100=ALGO\u0001167=MLEG\u0001555=3\u0001600=AAA\u0001602=111" + "\u0001600=BBB\u0001602=222\u0001654=231\u0001600=CCC\u0001602=333\u0001654=332\u000158=TEXT\u000110=168\u0001"; - //doValidation and firstFieldInGroupIsDelimiter -> should NOT fail + dataDictionary.setFirstFieldInGroupIsDelimiter(true); + dataDictionary.setCheckUnorderedGroupFields(false); final NewOrderMultileg noml1 = new NewOrderMultileg(); noml1.fromString(fixMsg, dataDictionary, true); dataDictionary.validate(noml1); assertTrue(noml1.hasGroup(555)); assertEquals(3, noml1.getGroupCount(555)); + //when firstFieldInGroupIsDelimiter = true and setCheckUnorderedGroupFields = false - valid //delimiter should be first tag in group assertEquals(602, noml1.getGroup(1, 555).delim()); dataDictionary.setFirstFieldInGroupIsDelimiter(false); - dataDictionary.setCheckUnorderedGroupFields(true); + dataDictionary.setCheckUnorderedGroupFields(false); final NewOrderMultileg noml2 = new NewOrderMultileg(); noml2.fromString(fixMsg, dataDictionary, true); - //when firstFieldInGroupIsDelimiter = false and setCheckUnorderedGroupFields = true - exception is thrown + //when firstFieldInGroupIsDelimiter = false and setCheckUnorderedGroupFields = false - exception is thrown assertThrows(FieldException.class, () -> dataDictionary.validate(noml2)); + dataDictionary.setFirstFieldInGroupIsDelimiter(false); + dataDictionary.setCheckUnorderedGroupFields(true); final NewOrderMultileg noml3 = new NewOrderMultileg(); noml3.fromString(fixMsg, dataDictionary, true); - //when firstFieldInGroupIsDelimiter = true and setCheckUnorderedGroupFields = true - exception is thrown + //when firstFieldInGroupIsDelimiter = false and setCheckUnorderedGroupFields = true - exception is thrown assertThrows(FieldException.class, () -> dataDictionary.validate(noml3)); + dataDictionary.setFirstFieldInGroupIsDelimiter(true); dataDictionary.setCheckUnorderedGroupFields(true); final NewOrderMultileg noml4 = new NewOrderMultileg(); - noml4.fromString(byDictFixMsg, dataDictionary, true); + noml4.fromString(fixMsg, dataDictionary, true); + //when firstFieldInGroupIsDelimiter = true and setCheckUnorderedGroupFields = true - exception is thrown, since order of tags is incorrect. + assertThrows(FieldException.class, () -> dataDictionary.validate(noml4)); + + dataDictionary.setFirstFieldInGroupIsDelimiter(true); + dataDictionary.setCheckUnorderedGroupFields(true); + final NewOrderMultileg noml5 = new NewOrderMultileg(); + noml5.fromString(byDictFixMsg, dataDictionary, true); //when firstFieldInGroupIsDelimiter = true and setCheckUnorderedGroupFields = true, message aligns with dictionary - do NOT fail - dataDictionary.validate(noml4); - assertTrue(noml4.hasGroup(555)); - assertEquals(3, noml4.getGroupCount(555)); + dataDictionary.validate(noml5); + assertTrue(noml5.hasGroup(555)); + assertEquals(3, noml5.getGroupCount(555)); //delimiter should be dictionary first tag = 600 - assertEquals(600, noml4.getGroup(1, 555).delim()); + assertEquals(600, noml5.getGroup(1, 555).delim()); } From bafae3b0e1678481ce4023918c3b6803d6bbebf7 Mon Sep 17 00:00:00 2001 From: Christoph John Date: Wed, 20 Sep 2023 16:17:28 +0200 Subject: [PATCH 06/10] minor clarification in javadoc --- quickfixj-core/src/main/java/quickfix/DataDictionary.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quickfixj-core/src/main/java/quickfix/DataDictionary.java b/quickfixj-core/src/main/java/quickfix/DataDictionary.java index 6be6346dae..91b551a764 100644 --- a/quickfixj-core/src/main/java/quickfix/DataDictionary.java +++ b/quickfixj-core/src/main/java/quickfix/DataDictionary.java @@ -567,9 +567,9 @@ public boolean isFirstFieldInGroupIsDelimiter() { } /** * Controls whether any field which is - * first in the group would be used as delimiter + * first in the repeating group would be used as delimiter * - * @param flag true = use first field from message, false = follow data dictionary + * @param flag true = use first field from repeating group, false = follow data dictionary * Must be used with disabled {@link #setCheckUnorderedGroupFields(boolean)} */ public void setFirstFieldInGroupIsDelimiter(boolean flag) { @@ -582,7 +582,7 @@ public void setFirstFieldInGroupIsDelimiter(boolean flag) { } /** - * Controls whether group fields are in the same order + * Controls whether repeating group fields are in the same order * * @param flag true = checked, false = not checked */ From e368c17904eab1242194cd75f82399a18bc26dbd Mon Sep 17 00:00:00 2001 From: Andrii Poletaiev Date: Fri, 29 Nov 2024 22:47:40 -0600 Subject: [PATCH 07/10] Solve merge conflicts. Merge master. --- .../src/main/java/quickfix/Message.java | 4 ++-- .../main/java/quickfix/ValidationSettings.java | 16 ++++++++++++++++ .../java/quickfix/DefaultSessionFactory.java | 5 +++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/quickfixj-base/src/main/java/quickfix/Message.java b/quickfixj-base/src/main/java/quickfix/Message.java index b3cc64ce29..52874d43dd 100644 --- a/quickfixj-base/src/main/java/quickfix/Message.java +++ b/quickfixj-base/src/main/java/quickfix/Message.java @@ -733,7 +733,7 @@ private void parseGroup(String msgType, StringField field, DataDictionary dd, Da throw MessageUtils.newInvalidMessageException("Repeating group count requires an Integer but found '" + field.getValue() + "' in " + messageData, this); } parent.setField(groupCountTag, field); - int firstField = groupDataDictionary.isFirstFieldInGroupIsDelimiter() ? -1 : rg.getDelimiterField(); + int firstField = dds.isFirstFieldInGroupIsDelimiter() ? -1 : rg.getDelimiterField(); Group group = null; boolean inGroupParse = true; while (inGroupParse) { @@ -743,7 +743,7 @@ private void parseGroup(String msgType, StringField field, DataDictionary dd, Da break; } int tag = field.getTag(); - boolean shouldCreateNewGroup = tag == firstField || (groupDataDictionary.isFirstFieldInGroupIsDelimiter() && firstField == -1); + boolean shouldCreateNewGroup = tag == firstField || (dds.isFirstFieldInGroupIsDelimiter() && firstField == -1); if (shouldCreateNewGroup) { firstField = tag; addGroupRefToParent(group, parent); diff --git a/quickfixj-base/src/main/java/quickfix/ValidationSettings.java b/quickfixj-base/src/main/java/quickfix/ValidationSettings.java index f2d242eac6..b90be976d6 100644 --- a/quickfixj-base/src/main/java/quickfix/ValidationSettings.java +++ b/quickfixj-base/src/main/java/quickfix/ValidationSettings.java @@ -25,6 +25,7 @@ public class ValidationSettings { boolean checkUserDefinedFields = true; boolean checkUnorderedGroupFields = true; boolean allowUnknownMessageFields = false; + boolean firstFieldInGroupIsDelimiter = false; public ValidationSettings() {} @@ -65,6 +66,10 @@ public boolean isAllowUnknownMessageFields() { return allowUnknownMessageFields; } + public boolean isFirstFieldInGroupIsDelimiter() { + return firstFieldInGroupIsDelimiter; + } + /** * Controls whether group fields are in the same order * @@ -95,4 +100,15 @@ public void setCheckUserDefinedFields(boolean flag) { public void setAllowUnknownMessageFields(boolean allowUnknownFields) { allowUnknownMessageFields = allowUnknownFields; } + + /** + * Controls whether any field which is + * first in the group would be used as delimiter + * + * @param flag true = use first field from message, false = follow data dictionary + * Must be used with disabled {@link #setCheckUnorderedGroupFields(boolean)} + */ + public void setFirstFieldInGroupIsDelimiter(boolean flag) { + firstFieldInGroupIsDelimiter = flag; + } } diff --git a/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java b/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java index 9da621fe7e..71e4efbf89 100644 --- a/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java +++ b/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java @@ -314,6 +314,11 @@ private ValidationSettings createValidationSettings(SessionID sessionID, Session Session.SETTING_ALLOW_UNKNOWN_MSG_FIELDS)); } + if (settings.isSetting(sessionID, Session.SETTING_FIRST_FIELD_IN_GROUP_IS_DELIMITER)) { + validationSettings.setFirstFieldInGroupIsDelimiter(settings.getBool(sessionID, + Session.SETTING_FIRST_FIELD_IN_GROUP_IS_DELIMITER)); + } + return validationSettings; } From 7a32f28c7912874f3f1e322629f2ec2e044ffb51 Mon Sep 17 00:00:00 2001 From: Andrii Poletaiev Date: Fri, 29 Nov 2024 23:28:46 -0600 Subject: [PATCH 08/10] Solve merge conflicts. Merge master. Fix tests --- .../src/test/java/quickfix/MessageTest.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/quickfixj-core/src/test/java/quickfix/MessageTest.java b/quickfixj-core/src/test/java/quickfix/MessageTest.java index 9ae2ad0f52..5ee57911ff 100644 --- a/quickfixj-core/src/test/java/quickfix/MessageTest.java +++ b/quickfixj-core/src/test/java/quickfix/MessageTest.java @@ -27,6 +27,9 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; +import static quickfix.DataDictionaryTest.getDictionary; + import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.ZoneOffset; @@ -120,6 +123,7 @@ import quickfix.fix44.ExecutionReport; import quickfix.fix44.IndicationOfInterest; import quickfix.fix44.Logon; +import quickfix.fix44.NewOrderMultileg; import quickfix.fix44.Logon.NoMsgTypes; import quickfix.fix44.NewOrderCross; import quickfix.fix44.NewOrderSingle.NoPartyIDs; @@ -1445,6 +1449,66 @@ public void testValidateFieldsOutOfOrderPreFIXT11() throws Exception { assertEquals(tcrOrdered.toString(), tcrUnOrdered.toString()); } + @Test + public void testFirstFieldInGroupIsDelimiter() throws Exception { + + final DataDictionary dataDictionary = new DataDictionary(getDictionary()); + ValidationSettings validationSettings = new ValidationSettings(); + + String fixMsg = "8=FIX.4.4\u00019=688\u000135=AB\u000149=AAA\u000156=BBB\u000134=21133\u000150=ABCABC" + + "\u000152=20230905-13:24:37.022\u000155=AAPL\u00011=ACC1\u000111=123456abcedf\u000121=1\u000138=5\u000154=1\u000140=2\u000144=-0.8" + + "\u000159=0\u000160=20230905-13:24:36.984\u0001100=ALGO\u0001167=MLEG\u0001555=3\u0001602=111\u0001600=AAA" + + "\u0001602=222\u0001654=231\u0001600=BBB\u0001602=333\u0001654=332\u0001600=CCC\u000158=TEXT\u000110=168\u0001"; + + String byDictFixMsg = "8=FIX.4.4\u00019=688\u000135=AB\u000149=AAA\u000156=BBB\u000134=21133\u000150=ABCABC" + + "\u000152=20230905-13:24:37.022\u000155=AAPL\u00011=ACC1\u000111=123456abcedf\u000121=1\u000138=5\u000154=1\u000140=2\u000144=-0.8" + + "\u000159=0\u000160=20230905-13:24:36.984\u0001100=ALGO\u0001167=MLEG\u0001555=3\u0001600=AAA\u0001602=111" + + "\u0001600=BBB\u0001602=222\u0001654=231\u0001600=CCC\u0001602=333\u0001654=332\u000158=TEXT\u000110=168\u0001"; + + validationSettings.setFirstFieldInGroupIsDelimiter(true); + validationSettings.setCheckUnorderedGroupFields(false); + final NewOrderMultileg noml1 = new NewOrderMultileg(); + noml1.fromString(fixMsg, dataDictionary, validationSettings, true); + dataDictionary.validate(noml1, validationSettings); + assertTrue(noml1.hasGroup(555)); + assertEquals(3, noml1.getGroupCount(555)); + //when firstFieldInGroupIsDelimiter = true and setCheckUnorderedGroupFields = false - valid + //delimiter should be first tag in group + assertEquals(602, noml1.getGroup(1, 555).delim()); + + validationSettings.setFirstFieldInGroupIsDelimiter(false); + validationSettings.setCheckUnorderedGroupFields(false); + final NewOrderMultileg noml2 = new NewOrderMultileg(); + noml2.fromString(fixMsg, dataDictionary, validationSettings, true); + //when firstFieldInGroupIsDelimiter = false and setCheckUnorderedGroupFields = false - exception is thrown + assertThrows(FieldException.class, () -> dataDictionary.validate(noml2, validationSettings)); + + validationSettings.setFirstFieldInGroupIsDelimiter(false); + validationSettings.setCheckUnorderedGroupFields(true); + final NewOrderMultileg noml3 = new NewOrderMultileg(); + noml3.fromString(fixMsg, dataDictionary, validationSettings, true); + //when firstFieldInGroupIsDelimiter = false and setCheckUnorderedGroupFields = true - exception is thrown + assertThrows(FieldException.class, () -> dataDictionary.validate(noml3, validationSettings)); + + validationSettings.setFirstFieldInGroupIsDelimiter(true); + validationSettings.setCheckUnorderedGroupFields(true); + final NewOrderMultileg noml4 = new NewOrderMultileg(); + noml4.fromString(fixMsg, dataDictionary, validationSettings, true); + //when firstFieldInGroupIsDelimiter = true and setCheckUnorderedGroupFields = true - exception is thrown, since order of tags is incorrect. + assertThrows(FieldException.class, () -> dataDictionary.validate(noml4, validationSettings)); + + validationSettings.setFirstFieldInGroupIsDelimiter(true); + validationSettings.setCheckUnorderedGroupFields(true); + final NewOrderMultileg noml5 = new NewOrderMultileg(); + noml5.fromString(byDictFixMsg, dataDictionary, validationSettings, true); + //when firstFieldInGroupIsDelimiter = true and setCheckUnorderedGroupFields = true, message aligns with dictionary - do NOT fail + dataDictionary.validate(noml5, validationSettings); + assertTrue(noml5.hasGroup(555)); + assertEquals(3, noml5.getGroupCount(555)); + //delimiter should be dictionary first tag = 600 + assertEquals(600, noml5.getGroup(1, 555).delim()); + } + private void assertHeaderField(Message message, String expectedValue, int field) throws FieldNotFound { assertEquals(expectedValue, message.getHeader().getString(field)); From 582e2d2526d52997f08950f21a70ebe6b6b2af65 Mon Sep 17 00:00:00 2001 From: Andrii Poletaiev Date: Wed, 4 Dec 2024 10:15:53 -0600 Subject: [PATCH 09/10] MR review changes, Copy constructor should retain setting --- quickfixj-base/src/main/java/quickfix/ValidationSettings.java | 1 + .../src/test/java/quickfix/ValidationSettingsTest.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/quickfixj-base/src/main/java/quickfix/ValidationSettings.java b/quickfixj-base/src/main/java/quickfix/ValidationSettings.java index b90be976d6..1f9006a303 100644 --- a/quickfixj-base/src/main/java/quickfix/ValidationSettings.java +++ b/quickfixj-base/src/main/java/quickfix/ValidationSettings.java @@ -35,6 +35,7 @@ public ValidationSettings(ValidationSettings validationSettings) { this.checkUserDefinedFields = validationSettings.checkUserDefinedFields; this.checkUnorderedGroupFields = validationSettings.checkUnorderedGroupFields; this.allowUnknownMessageFields = validationSettings.allowUnknownMessageFields; + this.firstFieldInGroupIsDelimiter = validationSettings.firstFieldInGroupIsDelimiter; } /** diff --git a/quickfixj-base/src/test/java/quickfix/ValidationSettingsTest.java b/quickfixj-base/src/test/java/quickfix/ValidationSettingsTest.java index a46eefff8d..6b50a8f5c4 100644 --- a/quickfixj-base/src/test/java/quickfix/ValidationSettingsTest.java +++ b/quickfixj-base/src/test/java/quickfix/ValidationSettingsTest.java @@ -14,6 +14,7 @@ public void copyConstructor_retains_settings() { validationSettings.setCheckFieldsOutOfOrder(false); validationSettings.setCheckUnorderedGroupFields(false); validationSettings.setCheckUserDefinedFields(false); + validationSettings.setFirstFieldInGroupIsDelimiter(true); ValidationSettings validationSettingsCopy = new ValidationSettings(validationSettings); @@ -22,5 +23,6 @@ public void copyConstructor_retains_settings() { assertEquals(validationSettingsCopy.isCheckFieldsOutOfOrder(), validationSettings.isCheckFieldsOutOfOrder()); assertEquals(validationSettingsCopy.isCheckUnorderedGroupFields(), validationSettings.isCheckUnorderedGroupFields()); assertEquals(validationSettingsCopy.isCheckUserDefinedFields(), validationSettings.isCheckUserDefinedFields()); + assertEquals(validationSettingsCopy.isFirstFieldInGroupIsDelimiter(), validationSettings.isFirstFieldInGroupIsDelimiter()); } } From 23de1617a236d92bd0a34e1a6f35c81511a69d96 Mon Sep 17 00:00:00 2001 From: Andrii Poletaiev Date: Mon, 16 Dec 2024 20:29:48 -0600 Subject: [PATCH 10/10] Update documentation --- quickfixj-core/src/main/doc/usermanual/usage/configuration.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickfixj-core/src/main/doc/usermanual/usage/configuration.html b/quickfixj-core/src/main/doc/usermanual/usage/configuration.html index d98fce4979..8e68d4c35a 100644 --- a/quickfixj-core/src/main/doc/usermanual/usage/configuration.html +++ b/quickfixj-core/src/main/doc/usermanual/usage/configuration.html @@ -467,7 +467,7 @@

QuickFIX Settings

FirstFieldInGroupIsDelimiter Session validation setting for enabling whether first found field in repeating group will be used as - delimiter. Values are "Y" or "N". Default is "N". + delimiter. Values are "Y" or "N". Default is "N". ValidateUnorderedGroupFields should be set to "N" Y
N N