package org.marketcetera.util.quickfix;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.marketcetera.util.except.ExceptUtils;
import org.marketcetera.util.file.CloseableRegistry;
import org.marketcetera.util.log.I18NBoundMessage;
import org.marketcetera.util.misc.ClassVersion;
import quickfix.DataDictionary;
import quickfix.Field;
import quickfix.FieldMap;
import quickfix.FieldNotFound;
import quickfix.Message;
import quickfix.field.MsgType;
/**
* Analyzes a QuickFIX/J message, producing a human-readable
* representation of its contents.
*
* @author tlerios@marketcetera.com
* @since 1.0.0
* @version $Id: AnalyzedMessage.java 16154 2012-07-14 16:34:05Z colin $
*/
/* $License$ */
@ClassVersion("$Id: AnalyzedMessage.java 16154 2012-07-14 16:34:05Z colin $")
public class AnalyzedMessage
{
// INSTANCE DATA.
private final List<AnalyzedField> mHeader=
new LinkedList<AnalyzedField>();
private final List<AnalyzedField> mBody=
new LinkedList<AnalyzedField>();
private final List<AnalyzedField> mTrailer=
new LinkedList<AnalyzedField>();
private Exception mValidationException;
// CONSTRUCTOR.
/**
* Creates a new analyzed message for the given QuickFIX/J
* message, interpreted using the given data dictionary.
*
* @param qDict The data dictionary.
* @param qMsg The message.
*/
public AnalyzedMessage
(DataDictionary qDict,
Message qMsg)
{
// Determine message type.
String msgType=null;
try {
msgType=qMsg.getHeader().getField(new MsgType()).getValue();
} catch (FieldNotFound ex) {
Messages.MISSING_TYPE.error(this,ex,qMsg);
return;
}
// Analyze sections.
analyzeFields(qDict,qDict,qMsg,DataDictionary.HEADER_ID,
qMsg.getHeader().iterator(),mHeader);
analyzeFields(qDict,qDict,qMsg,msgType,
qMsg.iterator(),mBody);
analyzeFields(qDict,qDict,qMsg,DataDictionary.TRAILER_ID,
qMsg.getTrailer().iterator(),mTrailer);
// Validate message.
try {
qDict.validate(qMsg);
} catch (Exception ex) {
ExceptUtils.interrupt(ex);
mValidationException=ex;
}
}
// INSTANCE METHODS.
/**
* Analyzes the given QuickFIX/J fields that are part of the given
* field map, and appends the results of the analysis to the given
* list. The map is part of (or same as) the message with the
* given type. One data dictionary is used to translate field tags
* and enumerated values (the <i>name</i> dictionary); another
* (the <i>scope</i> dictionary) is used for scope-dependent
* lookups such as required flags and subgroup analysis.
*
* @param nameQDict The name dictionary.
* @param scopeQDict The scope dictionary.
* @param qMap The field map.
* @param msgType The message type.
* @param qFields The fields.
* @param list The results' list.
*/
static void analyzeFields
(DataDictionary nameQDict,
DataDictionary scopeQDict,
FieldMap qMap,
String msgType,
Iterator<?> qFields,
List<AnalyzedField> list)
{
while (qFields.hasNext()) {
list.add(new AnalyzedField
(nameQDict,scopeQDict,qMap,msgType,
(Field<?>)(qFields.next())));
}
}
/**
* Prints the given field list onto the given stream. Each line
* printed is preceded by the given prefix.
*
* @param stream The stream.
* @param prefix The prefix.
* @param list The field list.
*/
static void printFields
(PrintStream stream,
String prefix,
List<AnalyzedField> list)
{
for (AnalyzedField field:list) {
stream.println();
field.print(stream,prefix);
}
}
/**
* Prints the given section title and fields of the receiver onto
* the given stream. Nothing is printed if the section contains no
* fields.
*
* @param stream The stream.
* @param title The section title.
* @param list The section fields.
*/
private void printSection
(PrintStream stream,
I18NBoundMessage title,
List<AnalyzedField> list)
{
if (list.size()==0) {
return;
}
stream.println();
stream.print(title.getText());
printFields(stream," ",list); //$NON-NLS-1$
}
/**
* Returns the analyzed fields that comprise the receiver's
* header.
*
* @return The fields.
*/
public List<AnalyzedField> getHeader()
{
return mHeader;
}
/**
* Returns the analyzed fields that comprise the receiver's body.
*
* @return The fields.
*/
public List<AnalyzedField> getBody()
{
return mBody;
}
/**
* Returns the analyzed fields that comprise the receiver's
* trailer.
*
* @return The fields.
*/
public List<AnalyzedField> getTrailer()
{
return mTrailer;
}
/**
* Returns the receiver's validation exception.
*
* @return The exception. It is null if the receiver is valid.
*/
public Exception getValidationException()
{
return mValidationException;
}
/**
* Prints the receiver onto the given stream.
*
* @param stream The stream.
*/
public void print
(PrintStream stream)
{
if (getValidationException()!=null) {
stream.println();
stream.println(Messages.VALIDATION_TITLE.getText());
stream.print(" "); //$NON-NLS-1$
stream.print(getValidationException().getLocalizedMessage());
}
printSection(stream,Messages.HEADER_TITLE,getHeader());
printSection(stream,Messages.BODY_TITLE,getBody());
printSection(stream,Messages.TRAILER_TITLE,getTrailer());
}
// Object.
@Override
public String toString()
{
ByteArrayOutputStream outputStream;
CloseableRegistry r=new CloseableRegistry();
try {
outputStream=new ByteArrayOutputStream();
r.register(outputStream);
PrintStream printStream=new PrintStream(outputStream);
r.register(printStream);
print(printStream);
} finally {
r.close();
}
return new String(outputStream.toByteArray());
}
}