Test FIX Protocol 4.4 message formatting

The below is not so much as a test but rather a sample code how to deal with a FIX 4.4 message – for those who need to quick start.

The code is self-explanatory – that is if you read the FIX Protocol documentation

 

package test.fix;

import static org.junit.Assert.*;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.junit.Test;

import quickfix.ConfigError;
import quickfix.DataDictionary;
import quickfix.Field;
import quickfix.FieldConvertError;
import quickfix.FieldMap;
import quickfix.FieldNotFound;
import quickfix.FieldType;
import quickfix.Group;
import quickfix.InvalidMessage;
import quickfix.Message;
import quickfix.MessageUtils;
import quickfix.Message.Header;
import quickfix.field.LastQty;
import quickfix.field.MsgType;
import quickfix.field.PossDupFlag;
import quickfix.field.PossResend;
import quickfix.field.SecurityID;
import quickfix.field.TargetCompID;

public class TestFix1 {

	// http://www.quickfixj.org/downloads/
	private final char ONE = '\001';

	@Test
	public void test() throws ConfigError, InvalidMessage, FieldNotFound, IOException {
	
	// 8=FIX.4.4^9=297^35=AE^49=AIM5^56=TILQ^17=905881^22=1^31=275.121547^32=18100^38=4979700^48=SMF6^55=SMF6^60=20151210-18:33:49.000^64=20151210^75=20151210^150=F^487=0^570=N^571=905881^552=1^54=2^37=905881^11=TMC-298.1561^1=GTAACORE^136=3^137=-135.75^138=USD^139=3^891=0^137=-1.81^138=USD^139=4^891=0^137=-497.75^138=USD^139=7^891=0^10=124^
	// 8=FIX.4.4^9=412^35=AE^34=3611^49=TILQ^52=20151210-18:35:59^56=GCS2^6=275.121547^14=18100^17=905881^22=1^31=275.121547^32=18100^38=4979700^39=1^48=SMF6^55=SMF6^60=20151210-18:33:49.000^64=20151210^75=20151210^150=F^151=4961600^487=0^570=N^571=TMC-298.1568^552=1^54=2^37=TMC-298.1561^11=TMCBBG-TICKET-2015-12-10-905881^1=GTAACORE^136=3^137=-135.75^138=USD^139=3^891=0^137=-1.81^138=USD^139=4^891=0^137=-497.75^138=USD^139=7^891=0^10=017^
	
	String messageStringHR="8=FIX.4.4^9=195^35=AE^49=AIM5^56=TILQ^17=740223^22=8^31=99.5^32=492200000^"
	+ "38=489739000^48=US912828L997^55=T^60=20151030-17:42:59.000^64=20151102^75=20151030^150=F^"
	+ "487=0^570=N^571=740223^552=1^54=1^37=740223^11=TMC-270.2^1=NT7H^10=126^";
	
	messageStringHR="8=FIX.4.4^9=297^35=AE^49=AIM5^56=TILQ^17=905881^22=1^31=275.121547^32=18100^38=4979700^"
	+ "48=SMF6^55=SMF6^60=20151210-18:33:49.000^64=20151210^75=20151210^150=F^487=0^570=N^571=905881^552=1^"
	+ "54=2^37=905881^11=TMC-298.1561^1=GTAACORE^136=3^137=-135.75^138=USD^139=3^891=0^137=-1.81^138=USD^"
	+ "139=4^891=0^137=-497.75^138=USD^139=7^891=0^10=124^";
	String messageString=messageStringHR.replace('^', ONE);
	
	DataDictionary dataDict = DataDictionaryUtils.loadDataDictionary("TIL_CORE_FIX44.xml");
	FixMessagePrinter fixMessagePrinter = new FixMessagePrinter();
	
	Message message = DataDictionaryUtils.parseRawFixMsg(dataDict, messageString, false);
	
	System.out.printf("Raw String:%n%n%s%n%n", messageString);
	System.out.printf("FixMsgObj:%n%n%s%n", fixMessagePrinter.print(message, dataDict, 4));
	
	System.out.println("LastQty="+message.getDecimal(LastQty.FIELD));
	
	FieldMapReader r = new FieldMapReader(message);
	System.out.println("TargetCompID="+r.getDecimal(TargetCompID.FIELD));
	}
}

// Helper classes

class FixMessagePrinter {
	private int m_indent = 4;
	private static final String CR = System.getProperty("line.separator");

	/**
	 * Default constructor
	 */
	public FixMessagePrinter() { }

	/**
	 * @return the indent
	 */
	public int getIndent() {
		return m_indent;
	}

	/**
	 * @param indent
	 *            the indent to set
	 */
	public void setIndent(int indent) {
		m_indent = indent;
	}

	/**
	 *
	 * @param message
	 * @return
	 */
	public String print(Message message) {
		return print(message, null, m_indent);
	}

	/**
	*
	* @param message
	* @param dataDictionary
	* @param indent space indentation for each level
	* @return
	*/
	public String print(Message message, DataDictionary dataDictionary, int indent)
	{
		StringBuffer writer = new StringBuffer();
		
		String levelSpacing = getIndentSpacing(indent);
		
		printFields(writer, "", levelSpacing, "header", message.getHeader(), dataDictionary);
		printFields(writer, "", levelSpacing, "body", message, dataDictionary);
		printFields(writer, "", levelSpacing, "trailer", message.getTrailer(), dataDictionary);
		
		return writer.toString();
	}

	private String getIndentSpacing(int indent)
	{
		StringBuffer sb = new StringBuffer(indent);
		
		for (int i=0; i < indent; i++) {
			sb.append(" ");
		}
		
		return sb.toString();
	}

	private void printFields(StringBuffer writer, String currentSpacing, String levelSpacing, String section, FieldMap fieldMap, DataDictionary dataDictionary)
	{
		Iterator<Field<?>> fieldItr = fieldMap.iterator();
		
		while (fieldItr.hasNext()) {
		
			Field<?> field = fieldItr.next();
			
			if (isGroupCountField(dataDictionary, field)) {
				// skip group count field
				continue;
			}
			
			formatOneField(writer, currentSpacing, field.getTag(), field.getObject().toString(), dataDictionary);
		}
		
		// group
		// noGroup(tagID) = groupCount
		//
		// field 1 = a
		// field 2 = b
		
		// field 1 = d
		// field 2 = e
		Iterator<Integer> groupKeyItr = fieldMap.groupKeyIterator();
		while (groupKeyItr.hasNext()) {
		
			int groupTagID = ((Integer) groupKeyItr.next()).intValue();
			int groupCount = fieldMap.getGroupCount(groupTagID);
			
			writer.append(CR);
			formatOneField(writer, currentSpacing, groupTagID, Integer.toString(groupCount), dataDictionary);
			
			// group elements
			List<Group> groups = fieldMap.getGroups(groupTagID);
			Iterator<Group> groupItr = groups.iterator();
			final String nextLevelSpacing = currentSpacing + levelSpacing;
			while (groupItr.hasNext()) {
				Group group = (Group) groupItr.next();
				writer.append(CR);
				printFields(writer, nextLevelSpacing, levelSpacing, "group-" + groupTagID, group, dataDictionary);
			}
		}
	}

	private boolean isGroupCountField(DataDictionary dd, Field<?> field) {
		if (dd == null) {
			return false;
		}

		FieldType fieldTypeEnum = dd.getFieldTypeEnum(field.getTag());

		return fieldTypeEnum != null &amp;&amp; fieldTypeEnum == FieldType.NumInGroup;
	}

	/**
	* @param writer
	* @param field
	* @param dataDictionary
	*/
	private void formatOneField(StringBuffer writer, String currentSpacing, int tagID, String tagValue, DataDictionary dataDictionary) {
		// tagName(tagID) = "abc def"
		// tagName(tagID) = enumName(1)
		
		writer.append(currentSpacing);
		
		String tagName = null;
		String enumName = null;
		
		if (dataDictionary != null) {
			tagName = dataDictionary.getFieldName(tagID);
			enumName = dataDictionary.getValueName(tagID, tagValue);
		}
		
		if (tagName == null) {
			/// tagID =
			writer.append(tagID);
		} else {
			// tagName(tagID) =
			writer.append(tagName).append("(").append(tagID).append(")");
		}
		
		writer.append(" = ");
		
		if (enumName == null) {
			// "value"
			writer.append("\"").append(tagValue).append("\"");
		} else {
			// enumName(value)
			writer.append(enumName).append("(").append(tagValue).append(")");
		}
		
		writer.append(CR);
	}

}

/**
 * QuickFixJ getter wrapper to get default value instead of FieldNotFound
 * Exception
 *
 */
class FieldMapReader {

	private FieldMap m_fieldMap = null;

	private String m_defaultString = null;
	private BigDecimal m_defaultDecimal = null;
	private BigInteger m_defaultBigInt = null;
	private Integer m_defaultInteger = null;
	private double m_defaultDouble = 0;
	private int m_defaultInt = 0;
	private char m_defaultChar = '\u0000';
	private boolean m_defaultBoolean = false;
	private Date m_defaultDate = null;

	private DateTimeFormatter m_localDateFormat = DateTimeFormat.forPattern("yyyyMMdd");
	private DateTimeFormatter m_localTimeFormat = DateTimeFormat.forPattern("HH:mm:ss");
	private DateTimeFormatter m_localTimeMillisFormat = DateTimeFormat.forPattern("HH:mm:ss.SSS");
	private DateTimeFormatter m_localTimestampFormat = DateTimeFormat.forPattern("yyyyMMdd-HH:mm:ss");
	private DateTimeFormatter m_localTimestampMillisFormat = DateTimeFormat.forPattern("yyyyMMdd-HH:mm:ss.SSS");

	private Header m_header = null;
	private FieldMapReader m_headerReader = null;

	public FieldMapReader() {
	}

	public FieldMapReader(FieldMap fieldMap) {
		setFieldMap(fieldMap);
	}

	/**
	 * @return the fieldMap
	 */
	public FieldMap getFieldMap() {
		return m_fieldMap;
	}

	/**
	 * @param fieldMap
	 *            the fieldMap to set
	 */
	public void setFieldMap(FieldMap fieldMap) {
		m_fieldMap = fieldMap;

		if (fieldMap instanceof Message) {
			m_header = ((Message) fieldMap).getHeader();
			m_headerReader = new FieldMapReader(m_header);
		}
	}

	/**
	 * @return the headerReader
	 */
	public FieldMapReader getHeaderReader() {
		return m_headerReader;
	}

	/**
	 *
	 * @param mustExist
	 * @return header msgType
	 */
	public String getMsgType(boolean mustExist) {
		return getMsgType(getHeaderReader(), mustExist);
	}

	/**
	*
	* @param headerReader
	* @param mustExist
	* @return
	*/
	public static String getMsgType(FieldMapReader headerReader, boolean mustExist) {
		if (headerReader == null) {
			throw new RuntimeException("headerReader is null");
		} else {
			return headerReader.getString(MsgType.FIELD, mustExist);
		}
	}

	/**
	 * @return the defaultBigInt
	 */
	public BigInteger getDefaultBigInt() {
		return m_defaultBigInt;
	}

	/**
	 * @param defaultBigInt
	 *            the defaultBigInt to set
	 */
	public void setDefaultBigInt(BigInteger defaultBigInt) {
		m_defaultBigInt = defaultBigInt;
	}

	/**
	 * @return the defaultInteger
	 */
	public Integer getDefaultInteger() {
		return m_defaultInteger;
	}

	/**
	 * @param defaultInteger
	 *            the defaultInteger to set
	 */
	public void setDefaultInteger(Integer defaultInteger) {
		m_defaultInteger = defaultInteger;
	}

	/**
	 * @return the defaultInt
	 */
	public int getDefaultInt() {
		return m_defaultInt;
	}

	/**
	 * @param defaultInt
	 *            the defaultInt to set
	 */
	public void setDefaultInt(int defaultInt) {
		m_defaultInt = defaultInt;
	}

	/**
	 * @return the defaultString
	 */
	public String getDefaultString() {
		return m_defaultString;
	}

	/**
	 * @param defaultString
	 *            the defaultString to set
	 */
	public void setDefaultString(String defaultString) {
		m_defaultString = defaultString;
	}

	/**
	 * @return the defaultDecimal
	 */
	public BigDecimal getDefaultDecimal() {
		return m_defaultDecimal;
	}

	/**
	 * @param defaultDecimal
	 *            the defaultDecimal to set
	 */
	public void setDefaultDecimal(BigDecimal defaultDecimal) {
		m_defaultDecimal = defaultDecimal;
	}

	/**
	 * @return the defaultDouble
	 */
	public double getDefaultDouble() {
		return m_defaultDouble;
	}

	/**
	 * @param defaultDouble
	 *            the defaultDouble to set
	 */
	public void setDefaultDouble(double defaultDouble) {
		m_defaultDouble = defaultDouble;
	}

	/**
	 * @return the defaultChar
	 */
	public char getDefaultChar() {
		return m_defaultChar;
	}

	/**
	 * @param defaultChar
	 *            the defaultChar to set
	 */
	public void setDefaultChar(char defaultChar) {
		m_defaultChar = defaultChar;
	}

	/**
	 * @return the defaultBoolean
	 */
	public boolean isDefaultBoolean() {
		return m_defaultBoolean;
	}

	/**
	 * @param defaultBoolean
	 *            the defaultBoolean to set
	 */
	public void setDefaultBoolean(boolean defaultBoolean) {
		m_defaultBoolean = defaultBoolean;
	}

	/**
	 * @return
	 * @see quickfix.FieldMap#isEmpty()
	 */
	public boolean isEmpty() {
		return m_fieldMap.isEmpty();
	}

	/**
	 * @param field
	 * @return
	 * @throws FieldNotFound
	 * @see quickfix.FieldMap#getString(int)
	 */
	public String getString(int field) {
		return getString(field, m_defaultString);
	}

	/**
	 * @param field
	 * @return
	 * @throws FieldNotFound
	 * @see quickfix.FieldMap#getString(int)
	 */
	public String getString(int field, String defaultValue) {
		try {
			return m_fieldMap.getString(field);
		} catch (FieldNotFound e) {
			return defaultValue;
		}
	}

	/**
	 * @param field
	 * @param mustExist
	 *            true – field must exist, false – optional field
	 * @return
	 */
	public String getString(int field, boolean mustExist) {
		try {
			return m_fieldMap.getString(field);
		} catch (FieldNotFound e) {
			if (mustExist) {
				throw new RuntimeException(e);
			} else {
				return m_defaultString;
			}
		}
	}

	/**
	 * @param field
	 * @return
	 * @throws FieldNotFound
	 * @see quickfix.FieldMap#getBoolean(int)
	 */
	public Boolean getBoolean(int field) {
		return getBoolean(field, m_defaultBoolean);
	}

	/**
	 * @param field
	 * @param defaultValue
	 * @return
	 * @see quickfix.FieldMap#getBoolean(int)
	 */
	public Boolean getBoolean(int field, Boolean defaultValue) {
		try {
			return m_fieldMap.getBoolean(field);
		} catch (FieldNotFound e) {
			return defaultValue;
		}
	}

	/**
	 * @param field
	 * @return
	 * @throws FieldNotFound
	 * @see quickfix.FieldMap#getChar(int)
	 */
	public char getChar(int field) {
		try {
			return m_fieldMap.getChar(field);
		} catch (FieldNotFound e) {
			return m_defaultChar;
		}
	}

	/**
	 * @param field
	 * @param mustExist
	 *            true – field must exist, false – optional field
	 * @return
	 */
	public char getChar(int field, boolean mustExist) {
		try {
			return m_fieldMap.getChar(field);
		} catch (FieldNotFound e) {
			if (mustExist) {
				throw new RuntimeException(e);
			} else {
				return m_defaultChar;
			}
		}
	}

	/**
	 * @param field
	 * @return
	 * @throws FieldNotFound
	 * @see quickfix.FieldMap#getDouble(int)
	 */
	public double getDouble(int field) {
		try {
			return m_fieldMap.getDouble(field);
		} catch (FieldNotFound e) {
			return m_defaultDouble;
		}
	}

	/**
	 * @param field
	 * @return
	 * @throws FieldNotFound
	 * @see quickfix.FieldMap#getInt(int)
	 */
	public int getInt(int field) {
		try {
			return m_fieldMap.getInt(field);
		} catch (FieldNotFound e) {
			return m_defaultInt;
		}
	}

	/**
	 * @param field
	 * @return
	 * @throws FieldNotFound
	 * @see quickfix.FieldMap#getDecimal(int)
	 */
	public BigDecimal getDecimal(int field) {
		return getDecimal(field, false);
	}

	/**
	 * @param field
	 * @return
	 * @throws FieldNotFound
	 * @see quickfix.FieldMap#getDecimal(int)
	 */
	public BigDecimal getDecimal(int field, boolean mustExist) {
		return getDecimal(field, mustExist, m_defaultDecimal);
	}

	/**
	 *
	 * @param field
	 * @param mustExist
	 * @param defaultValue
	 * @return
	 */
	public BigDecimal getDecimal(int field, boolean mustExist, BigDecimal defaultValue) {
		try {
			return m_fieldMap.getDecimal(field);
		} catch (FieldNotFound e) {
			if (mustExist) {
				throw new RuntimeException(e);
			} else {
				return defaultValue;
			}
		}
	}

	/**
	 * @param field
	 * @return
	 * @throws FieldNotFound
	 */
	public Integer getInteger(int field) {
		return getInteger(field, false);
	}

	/**
	 * @param field
	 * @param mustExist
	 *            true – field must exist, false – optional field
	 * @return
	 */
	public Integer getInteger(int field, boolean mustExist) {
		return getInteger(field, mustExist, m_defaultInteger);
	}

	/**
	 * @param field
	 * @param mustExist
	 *            true – field must exist, false – optional field
	 * @param defaultValue
	 * @return
	 */
	public Integer getInteger(int field, boolean mustExist, Integer defaultValue) {
		String value = null;

		try {
			value = m_fieldMap.getString(field);
			return new Integer(value);
		} catch (FieldNotFound e) {
			if (mustExist) {
				throw new RuntimeException(e);
			} else {
				return defaultValue;
			}
		}
	}

	/**
	 * @param field
	 * @param mustExist
	 *            true – field must exist, false – optional field
	 * @param defaultValue
	 * @return
	 */
	public Long getLong(int field, boolean mustExist, Long defaultValue) {
		String value = null;

		try {
			value = m_fieldMap.getString(field);
			return new Long(value);
		} catch (FieldNotFound e) {
			if (mustExist) {
				throw new RuntimeException(e);
			} else {
				return defaultValue;
			}
		}
	}

	/**
	 * @param field
	 * @return
	 * @throws FieldNotFound
	 */
	public BigInteger getBigInt(int field) {
		return getBigInt(field, false);
	}

	/**
	 * @param field
	 * @param mustExist
	 *            true – field must exist, false – optional field
	 * @return
	 */
	public BigInteger getBigInt(int field, boolean mustExist) {
		String value = null;

		try {
			value = m_fieldMap.getString(field);
			return convertToBigInteger(value);
		} catch (FieldNotFound e) {
			if (mustExist) {
				throw new RuntimeException(e);
			} else {
				return m_defaultBigInt;
			}
		} catch (FieldConvertError e) {
			throw newBadFieldException(field, value, e);
		}
	}

	/**
	 * @param field
	 * @return
	 * @throws FieldNotFound
	 * @see quickfix.FieldMap#getUtcTimeStamp(int)
	 */
	public Date getUtcTimeStamp(int field) {
		try {
			return m_fieldMap.getUtcTimeStamp(field);
		} catch (FieldNotFound e) {
			return m_defaultDate;
		}
	}

	/**
	 * @param field
	 * @return
	 * @throws FieldNotFound
	 * @see quickfix.FieldMap#getUtcTimeOnly(int)
	 */
	public Date getUtcTimeOnly(int field) {
		try {
			return m_fieldMap.getUtcTimeOnly(field);
		} catch (FieldNotFound e) {
			return m_defaultDate;
		}
	}

	/**
	 * @param field
	 * @return
	 * @throws FieldNotFound
	 * @see quickfix.FieldMap#getUtcDateOnly(int)
	 */
	public Date getUtcDateOnly(int field) {
		try {
			return m_fieldMap.getUtcDateOnly(field);
		} catch (FieldNotFound e) {
			return m_defaultDate;
		}
	}

	/**
	 * @param field
	 * @return
	 * @see quickfix.FieldMap#isSetField(int)
	 */
	public boolean isSetField(int field) {
		return m_fieldMap.isSetField(field);
	}

	/**
	 * @param field
	 * @return
	 * @see quickfix.FieldMap#getGroups(int)
	 */
	// public List<Group> getGroups(int field)
	// {
	// return m_fieldMap.getGroups(field);
	// }

	/**
	 *
	 * @param field
	 * @return group as {@link FieldMapReader}
	 */
	public List<FieldMapReader> getGroups(int field) {
		List<FieldMapReader> list = new ArrayList<FieldMapReader>();

		List<Group> l = m_fieldMap.getGroups(field);

		for (Group group : l) {
			list.add(new FieldMapReader(group));
		}

		return list;
	}

	/**
	 *
	 * @param field
	 * @return
	 */
	public Date getLocalDateOnly(int field) {
		// always pick the second formatter

		return getDateField(field, 0, m_localDateFormat, m_localDateFormat);
	}

	/**
	 *
	 * @param field
	 * @return
	 */
	public Date getLocalTimeOnly(int field) {
		// HH:mm:ss (8) vs HH:mm:ss.SSS

		return getDateField(field, 8, m_localTimeFormat, m_localTimeMillisFormat);
	}

	/**
	 *
	 * @param field
	 * @return
	 */
	public Date getLocalTimeStamp(int field) {
		// yyyyMMdd-HH:mm:ss (17) vs yyyyMMdd-HH:mm:ss.SSS

		return getDateField(field, 17, m_localTimestampFormat, m_localTimestampMillisFormat);
	}

	/**
	 *
	 * @param field
	 * @param valueLen1
	 *            value length for choosing formatter1
	 * @param formatter1
	 * @param formatter2
	 *            for value length != valueLen1
	 * @return date based on supplied formatter
	 */
	private Date getDateField(int field, int strLen1, DateTimeFormatter formatter1, DateTimeFormatter formatter2) {
		String value = null;

		try {
			value = m_fieldMap.getString(field);
			DateTimeFormatter formatter = value.length() == strLen1 ? formatter1 : formatter2;
			return formatter.parseDateTime(value).toDate();
		} catch (FieldNotFound e) {
			return m_defaultDate;
		} catch (Exception e) {
			throw newBadFieldException(field, value, e);
		}

	}

	private RuntimeException newBadFieldException(int field, String value, Exception e)
	{
	String message = "invalid field value = [" +
	value == null ? "" : value +
	"] for tag = [" + field + "]";
	
	return new RuntimeException(message, e);
	}

	/**
	* Convert a String to a BigInteger.
	* @param value the String to convert
	* @return the converted integer
	* @throws FieldConvertError raised if the String does not represent a
	* valid integer.
	* @see java.lang.Integer#parseInt(String)
	*/
	private static BigInteger convertToBigInteger(String value) throws FieldConvertError {
		try {
			for (int i = 0; i < value.length(); i++) {
				if (!Character.isDigit(value.charAt(i)) &amp;&amp; !(i == 0 &amp;&amp; value.charAt(i) == '-')) {
					throw new FieldConvertError("invalid integral value: " + value);
				}
			}
			return new BigInteger(value);
		} catch (NumberFormatException e) {
			throw new FieldConvertError("invalid integral value: " + value + ": " + e);
		}
	}

	/**
	 *
	 * @return is either PossDupFlag (tag 43) is set or PossResend (tag 97) is
	 *         set
	 */
	public boolean isPossDupOrPossResend() {
		return getHeaderReader().getBoolean(PossDupFlag.FIELD, false)
				|| getHeaderReader().getBoolean(PossResend.FIELD, false);
	}

	/**
	 * return tagID if possDup flag is set or possResend flag is set in the
	 * header
	 * 
	 * @return null if none of the flag is set to true
	 */
	public Integer getPossDupOrPossResendTagIfSet() {
		return getPossDupOrPossResendTagIfSet(getHeaderReader());
	}

	/**
	 * return tagID if possDup flag is set or possResend flag is set in the
	 * headerReader
	 * 
	 * @param headerReader
	 * @return
	 */
	public static Integer getPossDupOrPossResendTagIfSet(FieldMapReader headerReader) {
		if (headerReader == null) {
			return null;
		} else if (headerReader.getBoolean(PossDupFlag.FIELD, false)) {
			return PossDupFlag.FIELD;
		} else if (headerReader.getBoolean(PossResend.FIELD, false)) {
			return PossResend.FIELD;
		} else {
			return null;
		}
	}
}

final class DataDictionaryUtils {

	private DataDictionaryUtils() { }

	/**
	* load the data dictionary from the resource on the classpath
	* @param resourceName
	* @return
	* @throws ConfigError
	* @throws IOException
	*/
	public static DataDictionary loadDataDictionary(String resourceName) throws ConfigError, IOException
	{
		InputStream ddStream = getResourceInputStream(resourceName);
		
		if (ddStream == null) {
			throw new RuntimeException("Unable to load data dictionary from resource = [" + resourceName + "]");
		}
		
		DataDictionary dataDictionary = loadDataDictionary(ddStream);		
		return dataDictionary;
	}

	private static InputStream getResourceInputStream(String resourceName) throws IOException {
		Path path = FileSystems.getDefault().getPath(".", resourceName);
		Files.newInputStream(path, StandardOpenOption.READ);
		return null;
	}

	/**
	 * Load the data dictionary from the input stream
	 * 
	 * @param inputStream
	 * @return
	 * @throws ConfigError
	 */
	public static DataDictionary loadDataDictionary(InputStream inputStream) throws ConfigError {
		DataDictionary dataDictionary = new DataDictionary(inputStream);

		// remember to set setCheckUnorderedGroupFields to false or else
		// message generated from quickfix (which is out of order inside group)
		// will not
		// be parsed back by quick fix

		dataDictionary.setCheckUnorderedGroupFields(false);
		return dataDictionary;
	}

	/**
	 * parse raw FIX message and return the QuickFix Message Object Once you get
	 * the quickFix message object, you can use the following codes to print the
	 * FIX message:
	 * 
	 * <pre>
	* <code>
	* m_fixMessagePrinter.print(msgObj, m_dataDictionary, indentation);
	* </code>
	 * </pre>
	 * 
	 * @param dataDictionary
	 *            see {@link #loadDataDictionary(String)} or
	 *            {@link #loadDataDictionary(InputStream)}
	 * @param rawMessage
	 *            raw FIX message string in SOH format
	 * @param doValidation
	 *            true – validate FIX message according to dataDictionary; false
	 *            – no validation
	 * @return quickFix message object
	 * @throws InvalidMessage
	 */
	public static Message parseRawFixMsg(DataDictionary dataDictionary, String rawMessage, boolean doValidation)
			throws InvalidMessage {
		if (dataDictionary == null || rawMessage == null) {
			return null;
		}

		Message msgObj = new Message();

		msgObj.fromString(rawMessage, dataDictionary, doValidation);

		return msgObj;
	}
}