diff --git a/quickfixj-core/src/main/java/quickfix/Message.java b/quickfixj-core/src/main/java/quickfix/Message.java
index eb9d56c21a..8f8a09dbd0 100644
--- a/quickfixj-core/src/main/java/quickfix/Message.java
+++ b/quickfixj-core/src/main/java/quickfix/Message.java
@@ -19,6 +19,16 @@
package quickfix;
+import java.io.ByteArrayOutputStream;
+import java.text.DecimalFormat;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
import org.quickfixj.CharsetSupport;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
@@ -57,17 +67,6 @@
import quickfix.field.XmlData;
import quickfix.field.XmlDataLen;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import java.io.ByteArrayOutputStream;
-import java.text.DecimalFormat;
-import java.util.Iterator;
-import java.util.List;
-
/**
* Represents a FIX message.
*/
@@ -509,7 +508,7 @@ && isNextField(dd, header, BodyLength.FIELD)
header.setField(field);
if (dd != null && dd.isGroup(DataDictionary.HEADER_ID, field.getField())) {
- parseGroup(DataDictionary.HEADER_ID, field, dd, header, doValidation);
+ parseGroup(DataDictionary.HEADER_ID, field, dd, dd, header, doValidation);
}
field = extractField(dd, header);
@@ -548,7 +547,7 @@ private void parseBody(DataDictionary dd, boolean doValidation) throws InvalidMe
setField(header, field);
// Group case
if (dd != null && dd.isGroup(DataDictionary.HEADER_ID, field.getField())) {
- parseGroup(DataDictionary.HEADER_ID, field, dd, header, doValidation);
+ parseGroup(DataDictionary.HEADER_ID, field, dd, dd, header, doValidation);
}
if (doValidation && dd != null && dd.isCheckFieldsOutOfOrder())
throw new FieldException(SessionRejectReason.TAG_SPECIFIED_OUT_OF_REQUIRED_ORDER,
@@ -557,7 +556,7 @@ private void parseBody(DataDictionary dd, boolean doValidation) throws InvalidMe
setField(this, field);
// Group case
if (dd != null && dd.isGroup(getMsgType(), field.getField())) {
- parseGroup(getMsgType(), field, dd, this, doValidation);
+ parseGroup(getMsgType(), field, dd, dd, this, doValidation);
}
}
@@ -572,7 +571,7 @@ private void setField(FieldMap fields, StringField field) {
fields.setField(field);
}
- private void parseGroup(String msgType, StringField field, DataDictionary dd, FieldMap parent, boolean doValidation)
+ private void parseGroup(String msgType, StringField field, DataDictionary dd, DataDictionary parentDD, FieldMap parent, boolean doValidation)
throws InvalidMessage {
final DataDictionary.GroupInfo rg = dd.getGroup(msgType, field.getField());
final DataDictionary groupDataDictionary = rg.getDataDictionary();
@@ -602,14 +601,14 @@ private void parseGroup(String msgType, StringField field, DataDictionary dd, Fi
previousOffset = -1;
// QFJ-742
if (groupDataDictionary.isGroup(msgType, tag)) {
- parseGroup(msgType, field, groupDataDictionary, group, doValidation);
+ parseGroup(msgType, field, groupDataDictionary, parentDD, group, doValidation);
}
} else if (groupDataDictionary.isGroup(msgType, tag)) {
if (!firstFieldFound) {
throw new InvalidMessage("The group " + groupCountTag
+ " must set the delimiter field " + firstField + " in " + messageData);
}
- parseGroup(msgType, field, groupDataDictionary, group, doValidation);
+ parseGroup(msgType, field, groupDataDictionary, parentDD, group, doValidation);
} else if (groupDataDictionary.isField(tag)) {
if (!firstFieldFound) {
throw new FieldException(
@@ -629,16 +628,8 @@ private void parseGroup(String msgType, StringField field, DataDictionary dd, Fi
group.setField(field);
} else {
// QFJ-169/QFJ-791: handle unknown repeating group fields in the body
- if (!(DataDictionary.HEADER_ID.equals(msgType))) {
- if (!isTrailerField(tag) && !dd.isMsgField(msgType, tag)) {
- if (doValidation) {
- boolean fail = dd.checkFieldFailure(tag, false);
- if (fail) {
- throw new FieldException(
- SessionRejectReason.TAG_NOT_DEFINED_FOR_THIS_MESSAGE_TYPE, tag);
- }
- }
- group.setField(field);
+ if (!isTrailerField(tag) && !(DataDictionary.HEADER_ID.equals(msgType))) {
+ if (checkFieldValidation(parent, parentDD, field, msgType, doValidation, group)) {
continue;
}
}
@@ -654,6 +645,21 @@ private void parseGroup(String msgType, StringField field, DataDictionary dd, Fi
parent.setGroupCount(groupCountTag, declaredGroupCount);
}
+ private boolean checkFieldValidation(FieldMap parent, DataDictionary parentDD, StringField field, String msgType, boolean doValidation, Group group) throws FieldException {
+ boolean isField = (parent instanceof Group) ? parentDD.isField(field.getTag()) : parentDD.isMsgField(msgType, field.getTag());
+ if (!isField) {
+ if (doValidation) {
+ boolean fail = parentDD.checkFieldFailure(field.getTag(), false);
+ if (fail) {
+ throw new FieldException(SessionRejectReason.TAG_NOT_DEFINED_FOR_THIS_MESSAGE_TYPE, field.getTag());
+ }
+ }
+ group.setField(field);
+ return true;
+ }
+ return false;
+ }
+
private void parseTrailer(DataDictionary dd) throws InvalidMessage {
StringField field = extractField(dd, trailer);
while (field != null) {
diff --git a/quickfixj-core/src/test/java/quickfix/MessageTest.java b/quickfixj-core/src/test/java/quickfix/MessageTest.java
index f4be922d50..3a22107ca2 100644
--- a/quickfixj-core/src/test/java/quickfix/MessageTest.java
+++ b/quickfixj-core/src/test/java/quickfix/MessageTest.java
@@ -1602,6 +1602,64 @@ public void testInvalidFieldInGroup() throws Exception {
assertFalse(group.isSetField(QuoteAckStatus.FIELD));
}
+ @Test
+ // QFJ-169/QFJ-791
+ public void testNestedRepeatingGroup()
+ throws Exception {
+
+ String newOrdersSingleString = "8=FIX.4.4|9=265|35=D|34=62|49=sender|52=20160803-12:55:42.094|"
+ + "56=target|11=16H03A0000021|15=CHF|22=4|38=13|40=2|44=132|48=CH000000000|54=1|55=[N/A]|59=0|"
+ + "60=20160803-12:55:41.866|207=XXXX|423=2|526=foo|528=P|"
+ // tag 20000 is not defined, tag 22000 is defined for NewOrderSingle in FIX44_Custom_Test.xml
+ + "453=1|448=test|447=D|452=7|20000=0|802=1|523=test|803=25|22000=foobar|10=244|";
+
+ quickfix.fix44.NewOrderSingle nos = new quickfix.fix44.NewOrderSingle();
+ // using custom dictionary with user-defined tag 22000
+ final DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml");
+ dataDictionary.setCheckUserDefinedFields(false);
+ nos.fromString(newOrdersSingleString.replaceAll("\\|", "\001"), dataDictionary, true);
+ assertNull(nos.getException());
+ dataDictionary.validate(nos);
+
+ // defined tag should be set on the message
+ assertTrue(nos.isSetField(22000));
+ // undefined tag should not be set on the message
+ assertFalse(nos.isSetField(20000));
+ Group partyGroup = nos.getGroup(1, quickfix.field.NoPartyIDs.FIELD);
+ // undefined tag should be set on the group instead
+ assertTrue(partyGroup.isSetField(20000));
+ assertFalse(partyGroup.getGroup(1, quickfix.field.NoPartySubIDs.FIELD).isSetField(20000));
+ }
+
+ @Test
+ // QFJ-169/QFJ-791
+ public void testNestedRepeatingSubGroup()
+ throws Exception {
+
+ String newOrdersSingleString = "8=FIX.4.4|9=265|35=D|34=62|49=sender|52=20160803-12:55:42.094|"
+ + "56=target|11=16H03A0000021|15=CHF|22=4|38=13|40=2|44=132|48=CH000000000|54=1|55=[N/A]|59=0|"
+ + "60=20160803-12:55:41.866|207=XXXX|423=2|526=foo|528=P|"
+ // tag 20000 is not defined, tag 22000 is defined for NewOrderSingle in FIX44_Custom_Test.xml
+ + "453=1|448=test|447=D|452=7|802=1|523=test|803=25|20000=0|22000=foobar|10=244|";
+
+ quickfix.fix44.NewOrderSingle nos = new quickfix.fix44.NewOrderSingle();
+ // using custom dictionary with user-defined tag 22000
+ final DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml");
+ dataDictionary.setCheckUserDefinedFields(false);
+ nos.fromString(newOrdersSingleString.replaceAll("\\|", "\001"), dataDictionary, true);
+ assertNull(nos.getException());
+ dataDictionary.validate(nos);
+
+ // defined tag should be set on the message
+ assertTrue(nos.isSetField(22000));
+ // undefined tag should not be set on the message
+ assertFalse(nos.isSetField(20000));
+ Group partyGroup = nos.getGroup(1, quickfix.field.NoPartyIDs.FIELD);
+ // undefined tag should be set on the subgroup instead
+ assertFalse(partyGroup.isSetField(20000));
+ assertTrue(partyGroup.getGroup(1, quickfix.field.NoPartySubIDs.FIELD).isSetField(20000));
+ }
+
private void assertHeaderField(Message message, String expectedValue, int field)
throws FieldNotFound {
assertEquals(expectedValue, message.getHeader().getString(field));
diff --git a/quickfixj-core/src/test/java/quickfix/RepeatingGroupTest.java b/quickfixj-core/src/test/java/quickfix/RepeatingGroupTest.java
index 1fd26df101..8c99963494 100644
--- a/quickfixj-core/src/test/java/quickfix/RepeatingGroupTest.java
+++ b/quickfixj-core/src/test/java/quickfix/RepeatingGroupTest.java
@@ -28,6 +28,7 @@
import quickfix.field.SettlDate2;
import quickfix.field.Symbol;
import quickfix.fix44.Quote;
+import quickfix.fix50sp2.QuoteRequest;
public class RepeatingGroupTest extends TestCase {
@@ -123,19 +124,19 @@ private quickfix.fix44.QuoteRequest.NoRelatedSym buildNestedGroupWithStandardFie
return gNoRelatedSym;
}
- private quickfix.fix50.QuoteRequest.NoRelatedSym buildNestedGroupWithStandardFieldsFIX50(
+ private quickfix.fix50sp2.QuoteRequest.NoRelatedSym buildNestedGroupWithStandardFieldsFIX50SP2(
String settingValue) {
// The root group
- final quickfix.fix50.QuoteRequest.NoRelatedSym gNoRelatedSym = new quickfix.fix50.QuoteRequest.NoRelatedSym();
+ final quickfix.fix50sp2.QuoteRequest.NoRelatedSym gNoRelatedSym = new quickfix.fix50sp2.QuoteRequest.NoRelatedSym();
// The nested group
- final quickfix.fix50.QuoteRequest.NoRelatedSym.NoLegs nestedgroup = new quickfix.fix50.QuoteRequest.NoRelatedSym.NoLegs();
+ final quickfix.fix50sp2.QuoteRequest.NoRelatedSym.NoLegs nestedgroup = new quickfix.fix50sp2.QuoteRequest.NoRelatedSym.NoLegs();
nestedgroup.setField(new LegSymbol(settingValue));
gNoRelatedSym.addGroup(nestedgroup);
// Adding a second fake nested group to avoid being the case of having
// one element which is not relevant :-)
- final quickfix.fix50.QuoteRequest.NoRelatedSym.NoLegs oneMoreNestedgroup = new quickfix.fix50.QuoteRequest.NoRelatedSym.NoLegs();
+ final quickfix.fix50sp2.QuoteRequest.NoRelatedSym.NoLegs oneMoreNestedgroup = new quickfix.fix50sp2.QuoteRequest.NoRelatedSym.NoLegs();
oneMoreNestedgroup.setField(new LegSymbol("Donald"));
gNoRelatedSym.addGroup(oneMoreNestedgroup);
@@ -305,7 +306,6 @@ public void testValidationWithNestedGroupAndStandardFields() throws InvalidMessa
gNoRelatedSym.setField(new Symbol("SYM00"));
quoteRequest.addGroup(gNoRelatedSym);
-
quoteRequest.addGroup(gNoRelatedSym);
final String sourceFIXString = quoteRequest.toString();
@@ -321,28 +321,25 @@ public void testValidationWithNestedGroupAndStandardFields() throws InvalidMessa
}
public void testValidationWithNestedGroupAndStandardFieldsFIX50SP2() throws InvalidMessage, ConfigError {
- final quickfix.fix50.QuoteRequest quoteRequest = new quickfix.fix50.QuoteRequest();
+ final quickfix.fix50sp2.QuoteRequest quoteRequest = new quickfix.fix50sp2.QuoteRequest();
final quickfix.field.QuoteReqID gQuoteReqID = new quickfix.field.QuoteReqID();
gQuoteReqID.setValue("12342");
quoteRequest.setField(gQuoteReqID);
- final quickfix.fix50.QuoteRequest.NoRelatedSym gNoRelatedSym = buildNestedGroupWithStandardFieldsFIX50("DEFAULT_VALUE");
+ final quickfix.fix50sp2.QuoteRequest.NoRelatedSym gNoRelatedSym = buildNestedGroupWithStandardFieldsFIX50SP2("DEFAULT_VALUE");
gNoRelatedSym.setField(new Symbol("SYM00"));
gNoRelatedSym.setField(new SettlDate2("20120801"));
quoteRequest.addGroup(gNoRelatedSym);
-
quoteRequest.addGroup(gNoRelatedSym);
final String sourceFIXString = quoteRequest.toString();
- final DataDictionary fix50DataDictionary = new DataDictionary("FIX50SP2.xml");
- final quickfix.fix50.QuoteRequest validatedMessage = (quickfix.fix50.QuoteRequest) buildValidatedMessage(
- sourceFIXString, fix50DataDictionary);
- String validateFIXString = null;
- if (validatedMessage != null) {
- validateFIXString = validatedMessage.toString();
- }
+ final DataDictionary fix50sp2DataDictionary = new DataDictionary("FIX50SP2.xml");
+ final quickfix.fix50sp2.QuoteRequest validatedMessage = (quickfix.fix50sp2.QuoteRequest) messageFactory.create(FixVersions.FIX50SP2, QuoteRequest.MSGTYPE);
+ validatedMessage.fromString(sourceFIXString, fix50sp2DataDictionary, true);
+
+ String validateFIXString = validatedMessage.toString();
assertEquals("Message validation failed", sourceFIXString, validateFIXString);
assertEquals(2, validatedMessage.getGroupCount(gNoRelatedSym.getFieldTag()));
@@ -405,11 +402,7 @@ public void testGroupFieldsOrderWithCustomDataDictionary() throws InvalidMessage
assertNull("Invalid message", validatedMessage.getException());
- String validatedFIXString = null;
- if (validatedMessage != null) {
- validatedFIXString = validatedMessage.toString();
- }
-
+ String validatedFIXString = validatedMessage.toString();
assertEquals("Message validation failed",
MessageUtils.checksum(sourceFIXString), MessageUtils.checksum(validatedFIXString));
}
diff --git a/quickfixj-core/src/test/resources/FIX44_Custom_Test.xml b/quickfixj-core/src/test/resources/FIX44_Custom_Test.xml
index c62dd66e30..5585f8346f 100644
--- a/quickfixj-core/src/test/resources/FIX44_Custom_Test.xml
+++ b/quickfixj-core/src/test/resources/FIX44_Custom_Test.xml
@@ -1206,6 +1206,7 @@
+
@@ -6649,5 +6650,6 @@
+