#ifdef _MSC_VER
#include "stdafx.h"
#else
#include "config.h"
#endif
#include "DataDictionary.h"
#include "Message.h"
#include <fstream>
#include <memory>
#include "PUGIXML_DOMDocument.h"
#ifdef _MSC_VER
#define RESET_AUTO_PTR(OLD, NEW) OLD = NEW;
#else
#define RESET_AUTO_PTR(OLD, NEW) OLD.reset(NEW.release());
#endif
namespace FIX {
DataDictionary::DataDictionary()
: m_hasVersion(false),
m_checkFieldsOutOfOrder(true),
m_checkFieldsHaveValues(true),
m_checkUserDefinedFields(true),
m_allowUnknownMessageFields(false),
m_storeMsgFieldsOrder(false) {}
DataDictionary::DataDictionary(std::istream &stream, bool preserveMsgFldsOrder) EXCEPT(ConfigError)
: m_hasVersion(false),
m_checkFieldsOutOfOrder(true),
m_checkFieldsHaveValues(true),
m_checkUserDefinedFields(true),
m_allowUnknownMessageFields(false),
m_storeMsgFieldsOrder(preserveMsgFldsOrder) {
readFromStream(stream);
}
DataDictionary::DataDictionary(const std::string &url, bool preserveMsgFldsOrder) EXCEPT(ConfigError)
: m_hasVersion(false),
m_checkFieldsOutOfOrder(true),
m_checkFieldsHaveValues(true),
m_checkUserDefinedFields(true),
m_allowUnknownMessageFields(false),
m_storeMsgFieldsOrder(preserveMsgFldsOrder),
m_orderedFieldsArray(0) {
readFromURL(url);
}
DataDictionary::DataDictionary(const DataDictionary ©) { *this = copy; }
DataDictionary::~DataDictionary() {
FieldToGroup::iterator i;
for (i = m_groups.begin(); i != m_groups.end(); ++i) {
const FieldPresenceMap &presenceMap = i->second;
FieldPresenceMap::const_iterator iter = presenceMap.begin();
for (; iter != presenceMap.end(); ++iter) {
delete iter->second.second;
}
}
}
DataDictionary &DataDictionary::operator=(const DataDictionary &rhs) {
m_hasVersion = rhs.m_hasVersion;
m_checkFieldsOutOfOrder = rhs.m_checkFieldsOutOfOrder;
m_checkFieldsHaveValues = rhs.m_checkFieldsHaveValues;
m_storeMsgFieldsOrder = rhs.m_storeMsgFieldsOrder;
m_checkUserDefinedFields = rhs.m_checkUserDefinedFields;
m_allowUnknownMessageFields = rhs.m_allowUnknownMessageFields;
m_beginString = rhs.m_beginString;
m_messageFields = rhs.m_messageFields;
m_requiredFields = rhs.m_requiredFields;
m_messages = rhs.m_messages;
m_fields = rhs.m_fields;
m_orderedFields = rhs.m_orderedFields;
m_orderedFieldsArray = rhs.m_orderedFieldsArray;
m_headerFields = rhs.m_headerFields;
m_trailerFields = rhs.m_trailerFields;
m_fieldTypes = rhs.m_fieldTypes;
m_fieldValues = rhs.m_fieldValues;
m_fieldNames = rhs.m_fieldNames;
m_names = rhs.m_names;
m_valueNames = rhs.m_valueNames;
m_dataFields = rhs.m_dataFields;
m_headerOrderedFields = rhs.m_headerOrderedFields;
m_headerOrder = rhs.m_headerOrder;
m_trailerOrderedFields = rhs.m_trailerOrderedFields;
m_trailerOrder = rhs.m_trailerOrder;
m_messageOrderedFields = rhs.m_messageOrderedFields;
FieldToGroup::const_iterator i = rhs.m_groups.begin();
for (; i != rhs.m_groups.end(); ++i) {
const FieldPresenceMap &presenceMap = i->second;
FieldPresenceMap::const_iterator iter = presenceMap.begin();
for (; iter != presenceMap.end(); ++iter) {
addGroup(iter->first, i->first, iter->second.first, *iter->second.second);
}
}
return *this;
}
void DataDictionary::validate(
const Message &message,
const DataDictionary *const pSessionDD,
const DataDictionary *const pAppDD) EXCEPT(FIX::Exception) {
const Header &header = message.getHeader();
const BeginString &beginString = FIELD_GET_REF(header, BeginString);
const MsgType &msgType = FIELD_GET_REF(header, MsgType);
if (pSessionDD != 0 && pSessionDD->m_hasVersion) {
if (pSessionDD->getVersion() != beginString) {
throw UnsupportedVersion();
}
}
int field = 0;
if ((pSessionDD != 0 && pSessionDD->m_checkFieldsOutOfOrder) || (pAppDD != 0 && pAppDD->m_checkFieldsOutOfOrder)) {
if (!message.hasValidStructure(field)) {
throw TagOutOfOrder(field);
}
}
if (pAppDD != 0 && pAppDD->m_hasVersion) {
pAppDD->checkMsgType(msgType);
pAppDD->checkHasRequired(message.getHeader(), message, message.getTrailer(), msgType);
}
if (pSessionDD != 0) {
pSessionDD->iterate(message.getHeader(), msgType);
pSessionDD->iterate(message.getTrailer(), msgType);
}
if (pAppDD != 0) {
pAppDD->iterate(message, msgType);
}
}
void DataDictionary::iterate(const FieldMap &map, const MsgType &msgType) const {
int lastField = 0;
FieldMap::const_iterator i;
for (i = map.begin(); i != map.end(); ++i) {
const FieldBase &field = (*i);
if (i != map.begin() && (field.getTag() == lastField)) {
throw RepeatedTag(lastField);
}
checkHasValue(field);
if (m_hasVersion) {
checkValidFormat(field);
checkValue(field);
}
if (m_beginString.getValue().length() && shouldCheckTag(field)) {
checkValidTagNumber(field);
if (!Message::isHeaderField(field, this) && !Message::isTrailerField(field, this)) {
checkIsInMessage(field, msgType);
checkGroupCount(field, map, msgType);
}
}
lastField = field.getTag();
}
}
void DataDictionary::readFromURL(const std::string &url) EXCEPT(ConfigError) {
DOMDocumentPtr pDoc(new PUGIXML_DOMDocument());
if (!pDoc->load(url)) {
throw ConfigError(url + ": Could not parse data dictionary file");
}
try {
readFromDocument(pDoc);
} catch (ConfigError &e) {
throw ConfigError(url + ": " + e.what());
}
}
void DataDictionary::readFromStream(std::istream &stream) EXCEPT(ConfigError) {
DOMDocumentPtr pDoc(new PUGIXML_DOMDocument());
if (!pDoc->load(stream)) {
throw ConfigError("Could not parse data dictionary stream");
}
readFromDocument(pDoc);
}
void DataDictionary::readFromDocument(const DOMDocumentPtr &pDoc) EXCEPT(ConfigError) {
DOMNodePtr pFixNode = pDoc->getNode("/fix");
if (!pFixNode.get()) {
throw ConfigError("Could not parse data dictionary file"
", or no <fix> node found at root");
}
DOMAttributesPtr attrs = pFixNode->getAttributes();
std::string type = "FIX";
if (attrs->get("type", type)) {
if (type != "FIX" && type != "FIXT") {
throw ConfigError("type attribute must be FIX or FIXT");
}
}
std::string major;
if (!attrs->get("major", major)) {
throw ConfigError("major attribute not found on <fix>");
}
std::string minor;
if (!attrs->get("minor", minor)) {
throw ConfigError("minor attribute not found on <fix>");
}
setVersion(type + "." + major + "." + minor);
DOMNodePtr pFieldsNode = pDoc->getNode("/fix/fields");
if (!pFieldsNode.get()) {
throw ConfigError("<fields> section not found in data dictionary");
}
DOMNodePtr pFieldNode = pFieldsNode->getFirstChildNode();
if (!pFieldNode.get()) {
throw ConfigError("No fields defined");
}
while (pFieldNode.get()) {
if (pFieldNode->getName() == "field") {
DOMAttributesPtr attrs = pFieldNode->getAttributes();
std::string name;
if (!attrs->get("name", name)) {
throw ConfigError("<field> does not have a name attribute");
}
std::string number;
if (!attrs->get("number", number)) {
throw ConfigError("<field> " + name + " does not have a number attribute");
}
int num = atoi(number.c_str());
std::string type;
if (!attrs->get("type", type)) {
throw ConfigError("<field> " + name + " does not have a type attribute");
}
addField(num);
addFieldType(num, XMLTypeToType(type));
addFieldName(num, name);
DOMNodePtr pFieldValueNode = pFieldNode->getFirstChildNode();
while (pFieldValueNode.get()) {
if (pFieldValueNode->getName() == "value") {
DOMAttributesPtr attrs = pFieldValueNode->getAttributes();
std::string enumeration;
if (!attrs->get("enum", enumeration)) {
throw ConfigError("<value> does not have enum attribute in field " + name);
}
addFieldValue(num, enumeration);
std::string description;
if (attrs->get("description", description)) {
addValueName(num, enumeration, description);
}
}
RESET_AUTO_PTR(pFieldValueNode, pFieldValueNode->getNextSiblingNode());
}
}
RESET_AUTO_PTR(pFieldNode, pFieldNode->getNextSiblingNode());
}
if (type == "FIXT" || (type == "FIX" && major < "5")) {
DOMNodePtr pHeaderNode = pDoc->getNode("/fix/header");
if (!pHeaderNode.get()) {
throw ConfigError("<header> section not found in data dictionary");
}
DOMNodePtr pHeaderFieldNode = pHeaderNode->getFirstChildNode();
if (!pHeaderFieldNode.get()) {
throw ConfigError("No header fields defined");
}
while (pHeaderFieldNode.get()) {
if (pHeaderFieldNode->getName() == "field" || pHeaderFieldNode->getName() == "group") {
DOMAttributesPtr attrs = pHeaderFieldNode->getAttributes();
std::string name;
if (!attrs->get("name", name)) {
throw ConfigError("<field> does not have a name attribute");
}
std::string required;
attrs->get("required", required);
bool isRequired = (required == "Y" || required == "y");
addHeaderField(lookupXMLFieldNumber(pDoc.get(), name), isRequired);
}
if (pHeaderFieldNode->getName() == "group") {
DOMAttributesPtr attrs = pHeaderFieldNode->getAttributes();
std::string required;
attrs->get("required", required);
bool isRequired = (required == "Y" || required == "y");
addXMLGroup(pDoc.get(), pHeaderFieldNode.get(), "_header_", *this, isRequired);
}
RESET_AUTO_PTR(pHeaderFieldNode, pHeaderFieldNode->getNextSiblingNode());
}
}
if (type == "FIXT" || (type == "FIX" && major < "5")) {
DOMNodePtr pTrailerNode = pDoc->getNode("/fix/trailer");
if (!pTrailerNode.get()) {
throw ConfigError("<trailer> section not found in data dictionary");
}
DOMNodePtr pTrailerFieldNode = pTrailerNode->getFirstChildNode();
if (!pTrailerFieldNode.get()) {
throw ConfigError("No trailer fields defined");
}
while (pTrailerFieldNode.get()) {
if (pTrailerFieldNode->getName() == "field" || pTrailerFieldNode->getName() == "group") {
DOMAttributesPtr attrs = pTrailerFieldNode->getAttributes();
std::string name;
if (!attrs->get("name", name)) {
throw ConfigError("<field> does not have a name attribute");
}
std::string required;
attrs->get("required", required);
bool isRequired = (required == "Y" || required == "y");
addTrailerField(lookupXMLFieldNumber(pDoc.get(), name), isRequired);
}
if (pTrailerFieldNode->getName() == "group") {
DOMAttributesPtr attrs = pTrailerFieldNode->getAttributes();
std::string required;
attrs->get("required", required);
bool isRequired = (required == "Y" || required == "y");
addXMLGroup(pDoc.get(), pTrailerFieldNode.get(), "_trailer_", *this, isRequired);
}
RESET_AUTO_PTR(pTrailerFieldNode, pTrailerFieldNode->getNextSiblingNode());
}
}
DOMNodePtr pMessagesNode = pDoc->getNode("/fix/messages");
if (!pMessagesNode.get()) {
throw ConfigError("<messages> section not found in data dictionary");
}
DOMNodePtr pMessageNode = pMessagesNode->getFirstChildNode();
if (!pMessageNode.get()) {
throw ConfigError("No messages defined");
}
while (pMessageNode.get()) {
if (pMessageNode->getName() == "message") {
DOMAttributesPtr attrs = pMessageNode->getAttributes();
std::string msgtype;
if (!attrs->get("msgtype", msgtype)) {
throw ConfigError("<message> does not have a msgtype attribute");
}
addMsgType(msgtype);
std::string name;
if (attrs->get("name", name)) {
addValueName(35, msgtype, name);
}
DOMNodePtr pMessageFieldNode = pMessageNode->getFirstChildNode();
while (pMessageFieldNode.get()) {
if (pMessageFieldNode->getName() == "field" || pMessageFieldNode->getName() == "group") {
DOMAttributesPtr attrs = pMessageFieldNode->getAttributes();
std::string name;
if (!attrs->get("name", name)) {
throw ConfigError("<field> does not have a name attribute");
}
int num = lookupXMLFieldNumber(pDoc.get(), name);
addMsgField(msgtype, num);
std::string required;
if (attrs->get("required", required) && (required == "Y" || required == "y")) {
addRequiredField(msgtype, num);
}
} else if (pMessageFieldNode->getName() == "component") {
DOMAttributesPtr attrs = pMessageFieldNode->getAttributes();
std::string required;
attrs->get("required", required);
bool isRequired = (required == "Y" || required == "y");
addXMLComponentFields(pDoc.get(), pMessageFieldNode.get(), msgtype, *this, isRequired);
}
if (pMessageFieldNode->getName() == "group") {
DOMAttributesPtr attrs = pMessageFieldNode->getAttributes();
std::string required;
attrs->get("required", required);
bool isRequired = (required == "Y" || required == "y");
addXMLGroup(pDoc.get(), pMessageFieldNode.get(), msgtype, *this, isRequired);
}
RESET_AUTO_PTR(pMessageFieldNode, pMessageFieldNode->getNextSiblingNode());
}
}
RESET_AUTO_PTR(pMessageNode, pMessageNode->getNextSiblingNode());
}
}
message_order const &DataDictionary::getOrderedFields() const {
if (m_orderedFieldsArray) {
return m_orderedFieldsArray;
}
return m_orderedFieldsArray = message_order(m_orderedFields.data(), m_orderedFields.size());
}
message_order const &DataDictionary::getHeaderOrderedFields() const EXCEPT(ConfigError) {
if (m_headerOrder) {
return m_headerOrder;
}
if (m_headerOrderedFields.size() == 0) {
throw ConfigError("<Header> does not have a stored message order");
}
return m_headerOrder = message_order(m_headerOrderedFields.data(), m_headerOrderedFields.size());
}
message_order const &DataDictionary::getTrailerOrderedFields() const EXCEPT(ConfigError) {
if (m_trailerOrder) {
return m_trailerOrder;
}
if (m_trailerOrderedFields.size() == 0) {
throw ConfigError("<Trailer> does not have a stored message order");
}
return m_trailerOrder = message_order(m_trailerOrderedFields.data(), m_trailerOrderedFields.size());
}
const message_order &DataDictionary::getMessageOrderedFields(const std::string &msgType) const EXCEPT(ConfigError) {
MsgTypeToOrderedFields::const_iterator iter = m_messageOrderedFields.find(msgType);
if (iter == m_messageOrderedFields.end()) {
throw ConfigError("<Message> " + msgType + " does not have a stored message order");
}
return iter->second.getMessageOrder();
}
int DataDictionary::lookupXMLFieldNumber(DOMDocument *pDoc, DOMNode *pNode) const {
DOMAttributesPtr attrs = pNode->getAttributes();
std::string name;
if (!attrs->get("name", name)) {
throw ConfigError("No name given to field");
}
return lookupXMLFieldNumber(pDoc, name);
}
int DataDictionary::lookupXMLFieldNumber(DOMDocument *pDoc, const std::string &name) const {
NameToField::const_iterator i = m_names.find(name);
if (i == m_names.end()) {
throw ConfigError("Field " + name + " not defined in fields section");
}
return i->second;
}
int DataDictionary::addXMLComponentFields(
DOMDocument *pDoc,
DOMNode *pNode,
const std::string &msgtype,
DataDictionary &DD,
bool componentRequired) {
int firstField = 0;
DOMAttributesPtr attrs = pNode->getAttributes();
std::string name;
if (!attrs->get("name", name)) {
throw ConfigError("No name given to component");
}
DOMNodePtr pComponentNode = pDoc->getNode("/fix/components/component[@name='" + name + "']");
if (pComponentNode.get() == 0) {
throw ConfigError("Component not found: " + name);
}
DOMNodePtr pComponentFieldNode = pComponentNode->getFirstChildNode();
while (pComponentFieldNode.get()) {
if (pComponentFieldNode->getName() == "field" || pComponentFieldNode->getName() == "group") {
DOMAttributesPtr attrs = pComponentFieldNode->getAttributes();
std::string name;
if (!attrs->get("name", name)) {
throw ConfigError("No name given to field");
}
int field = lookupXMLFieldNumber(pDoc, name);
if (firstField == 0) {
firstField = field;
}
std::string required;
if (attrs->get("required", required) && (required == "Y" || required == "y") && componentRequired) {
addRequiredField(msgtype, field);
}
DD.addField(field);
DD.addMsgField(msgtype, field);
}
if (pComponentFieldNode->getName() == "component") {
DOMAttributesPtr attrs = pComponentFieldNode->getAttributes();
std::string required;
attrs->get("required", required);
bool isRequired = (required == "Y" || required == "y");
addXMLComponentFields(pDoc, pComponentFieldNode.get(), msgtype, DD, isRequired);
}
if (pComponentFieldNode->getName() == "group") {
DOMAttributesPtr attrs = pComponentFieldNode->getAttributes();
std::string required;
attrs->get("required", required);
bool isRequired = (required == "Y" || required == "y");
addXMLGroup(pDoc, pComponentFieldNode.get(), msgtype, DD, isRequired);
}
RESET_AUTO_PTR(pComponentFieldNode, pComponentFieldNode->getNextSiblingNode());
}
return firstField;
}
void DataDictionary::addXMLGroup(
DOMDocument *pDoc,
DOMNode *pNode,
const std::string &msgtype,
DataDictionary &DD,
bool groupRequired) {
DOMAttributesPtr attrs = pNode->getAttributes();
std::string name;
if (!attrs->get("name", name)) {
throw ConfigError("No name given to group");
}
int group = lookupXMLFieldNumber(pDoc, name);
int delim = 0;
int field = 0;
DataDictionary groupDD;
DOMNodePtr node = pNode->getFirstChildNode();
while (node.get()) {
if (node->getName() == "field") {
field = lookupXMLFieldNumber(pDoc, node.get());
groupDD.addField(field);
DOMAttributesPtr attrs = node->getAttributes();
std::string required;
if (attrs->get("required", required) && (required == "Y" || required == "y") && groupRequired) {
groupDD.addRequiredField(msgtype, field);
}
} else if (node->getName() == "component") {
field = addXMLComponentFields(pDoc, node.get(), msgtype, groupDD, false);
} else if (node->getName() == "group") {
field = lookupXMLFieldNumber(pDoc, node.get());
groupDD.addField(field);
DOMAttributesPtr attrs = node->getAttributes();
std::string required;
if (attrs->get("required", required) && (required == "Y" || required == "y") && groupRequired) {
groupDD.addRequiredField(msgtype, field);
}
bool isRequired = false;
if (attrs->get("required", required)) {
isRequired = (required == "Y" || required == "y");
}
addXMLGroup(pDoc, node.get(), msgtype, groupDD, isRequired);
}
if (delim == 0) {
delim = field;
}
RESET_AUTO_PTR(node, node->getNextSiblingNode());
}
if (delim) {
DD.addGroup(msgtype, group, delim, groupDD);
}
}
TYPE::Type DataDictionary::XMLTypeToType(const std::string &type) const {
if (m_beginString < "FIX.4.2" && type == "CHAR") {
return TYPE::String;
}
if (type == "STRING") {
return TYPE::String;
}
if (type == "CHAR") {
return TYPE::Char;
}
if (type == "PRICE") {
return TYPE::Price;
}
if (type == "INT") {
return TYPE::Int;
}
if (type == "AMT") {
return TYPE::Amt;
}
if (type == "QTY") {
return TYPE::Qty;
}
if (type == "CURRENCY") {
return TYPE::Currency;
}
if (type == "MULTIPLEVALUESTRING") {
return TYPE::MultipleValueString;
}
if (type == "MULTIPLESTRINGVALUE") {
return TYPE::MultipleStringValue;
}
if (type == "MULTIPLECHARVALUE") {
return TYPE::MultipleCharValue;
}
if (type == "EXCHANGE") {
return TYPE::Exchange;
}
if (type == "UTCTIMESTAMP") {
return TYPE::UtcTimeStamp;
}
if (type == "BOOLEAN") {
return TYPE::Boolean;
}
if (type == "LOCALMKTDATE") {
return TYPE::LocalMktDate;
}
if (type == "DATA") {
return TYPE::Data;
}
if (type == "FLOAT") {
return TYPE::Float;
}
if (type == "PRICEOFFSET") {
return TYPE::PriceOffset;
}
if (type == "MONTHYEAR") {
return TYPE::MonthYear;
}
if (type == "DAYOFMONTH") {
return TYPE::DayOfMonth;
}
if (type == "UTCDATE") {
return TYPE::UtcDate;
}
if (type == "UTCDATEONLY") {
return TYPE::UtcDateOnly;
}
if (type == "UTCTIMEONLY") {
return TYPE::UtcTimeOnly;
}
if (type == "NUMINGROUP") {
return TYPE::NumInGroup;
}
if (type == "PERCENTAGE") {
return TYPE::Percentage;
}
if (type == "SEQNUM") {
return TYPE::SeqNum;
}
if (type == "LENGTH") {
return TYPE::Length;
}
if (type == "COUNTRY") {
return TYPE::Country;
}
if (type == "TIME") {
return TYPE::UtcTimeStamp;
}
return TYPE::Unknown;
}
}