package org.marketcetera.quickfix;
import static org.marketcetera.quickfix.Messages.CANNOT_CREATE_FIX_FIELD;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.regex.Pattern;
import org.marketcetera.core.ClassVersion;
import org.marketcetera.core.CoreException;
import org.marketcetera.quickfix.cficode.OptionCFICode;
import org.marketcetera.util.log.I18NBoundMessage1P;
import quickfix.DataDictionary;
import quickfix.Field;
import quickfix.FieldMap;
import quickfix.FieldNotFound;
import quickfix.Group;
import quickfix.Message;
import quickfix.StringField;
import quickfix.Message.Header;
import quickfix.field.CFICode;
import quickfix.field.CollReqID;
import quickfix.field.ConfirmReqID;
import quickfix.field.EncodedText;
import quickfix.field.MDEntryType;
import quickfix.field.MDReqID;
import quickfix.field.MarketDepth;
import quickfix.field.MsgType;
import quickfix.field.NetworkRequestID;
import quickfix.field.NoMDEntries;
import quickfix.field.OrdStatus;
import quickfix.field.PosReqID;
import quickfix.field.PutOrCall;
import quickfix.field.QuoteID;
import quickfix.field.QuoteReqID;
import quickfix.field.QuoteStatusReqID;
import quickfix.field.RFQReqID;
import quickfix.field.SecurityReqID;
import quickfix.field.SecurityStatusReqID;
import quickfix.field.SettlInstReqID;
import quickfix.field.Symbol;
import quickfix.field.Text;
import quickfix.field.TradSesReqID;
import quickfix.field.TradeRequestID;
import quickfix.field.UserRequestID;
/**
* Collection of utilities to create work with FIX messages
*
* @author gmiller
* $Id: FIXMessageUtil.java 16154 2012-07-14 16:34:05Z colin $
*/
@ClassVersion("$Id: FIXMessageUtil.java 16154 2012-07-14 16:34:05Z colin $") //$NON-NLS-1$
public class FIXMessageUtil {
private static final String LOGGER_NAME = FIXMessageUtil.class.getName();
private static final int MAX_FIX_FIELDS = 2000; // What we think the ID of the last fix field is
public static final Pattern optionSymbolPattern = Pattern.compile("(\\w{1,3})\\+(\\w)(\\w)"); //$NON-NLS-1$
/**
* Creates a new instance of FIXMessageUtil
*/
public FIXMessageUtil() {
}
public static int getMaxFIXFields() {
return MAX_FIX_FIELDS;
}
private static boolean msgTypeHelper(Message fixMessage, String msgType) {
if (fixMessage != null){
try {
MsgType msgTypeField = new MsgType();
Header header = fixMessage.getHeader();
if (header.isSetField(msgTypeField)){
header.getField(msgTypeField);
return msgType.equals(msgTypeField.getValue());
}
} catch (Exception ignored) {
// ignored
}
}
return false;
}
public static boolean isExecutionReport(Message message) {
return msgTypeHelper(message, MsgType.EXECUTION_REPORT);
}
public static boolean isOrderSingle(Message message) {
return msgTypeHelper(message, MsgType.ORDER_SINGLE);
}
public static boolean isReject(Message message) {
return msgTypeHelper(message, MsgType.REJECT);
}
public static boolean isBusinessMessageReject(Message message) {
return msgTypeHelper(message, MsgType.BUSINESS_MESSAGE_REJECT);
}
public static boolean isCancelReject(Message message) {
return msgTypeHelper(message, MsgType.ORDER_CANCEL_REJECT);
}
public static boolean isStatusRequest(Message message) {
return msgTypeHelper(message, MsgType.ORDER_STATUS_REQUEST);
}
public static boolean isCancelRequest(Message message) {
return msgTypeHelper(message, MsgType.ORDER_CANCEL_REQUEST);
}
public static boolean isCancelReplaceRequest(Message message) {
return msgTypeHelper(message, MsgType.ORDER_CANCEL_REPLACE_REQUEST);
}
public static boolean isOrderList(Message message) {
return msgTypeHelper(message, MsgType.ORDER_LIST);
}
public static boolean isLogon(Message message){
return msgTypeHelper(message, MsgType.LOGON);
}
public static boolean isLogout(Message message){
return msgTypeHelper(message, MsgType.LOGOUT);
}
public static boolean isMarketDataRequest(Message message) {
return msgTypeHelper(message, MsgType.MARKET_DATA_REQUEST);
}
public static boolean isMarketDataSnapshotFullRefresh(Message message) {
return msgTypeHelper(message, MsgType.MARKET_DATA_SNAPSHOT_FULL_REFRESH);
}
public static boolean isMarketDataIncrementalRefresh(Message message) {
return msgTypeHelper(message, MsgType.MARKET_DATA_INCREMENTAL_REFRESH);
}
public static boolean isResendRequest(Message message) {
return msgTypeHelper(message, MsgType.RESEND_REQUEST);
}
public static boolean isDerivativeSecurityList(
Message message) {
return msgTypeHelper(message, MsgType.DERIVATIVE_SECURITY_LIST);
}
public static boolean isDerivativeSecurityListRequest(Message message) {
return msgTypeHelper(message, MsgType.DERIVATIVE_SECURITY_LIST_REQUEST);
}
public static boolean isSecurityDefinitionRequest(Message message) {
return msgTypeHelper(message, MsgType.SECURITY_DEFINITION_REQUEST);
}
public static boolean isSecurityDefinition(Message message) {
return msgTypeHelper(message, MsgType.SECURITY_DEFINITION);
}
public static boolean isTradingSessionStatus(Message message) {
return msgTypeHelper(message, MsgType.TRADING_SESSION_STATUS);
}
public static boolean isHeartbeat(Message message) {
return msgTypeHelper(message, MsgType.HEARTBEAT);
}
public static boolean isSecurityListRequest(Message inMessage)
{
return msgTypeHelper(inMessage,
MsgType.SECURITY_LIST_REQUEST);
}
@Deprecated
public static boolean isEquityOptionOrder(Message message)
{
try {
return isOrderSingle(message)
&& (
message.isSetField(PutOrCall.FIELD) ||
(message.isSetField(Symbol.FIELD) && optionSymbolPattern.matcher(message.getString(Symbol.FIELD)).matches()) ||
message.isSetField(CFICode.FIELD) && OptionCFICode.isOptionCFICode(message.getString(CFICode.FIELD))
);
} catch (FieldNotFound e) {
// should never happen
return false;
}
}
public static boolean isCancellable(char ordStatus) {
switch (ordStatus){
case OrdStatus.ACCEPTED_FOR_BIDDING:
case OrdStatus.CALCULATED:
case OrdStatus.NEW:
case OrdStatus.PARTIALLY_FILLED:
case OrdStatus.PENDING_CANCEL:
case OrdStatus.PENDING_NEW:
case OrdStatus.PENDING_REPLACE:
case OrdStatus.STOPPED:
case OrdStatus.SUSPENDED:
case OrdStatus.REPLACED:
return true;
case OrdStatus.CANCELED:
case OrdStatus.DONE_FOR_DAY:
case OrdStatus.EXPIRED:
case OrdStatus.FILLED:
case OrdStatus.REJECTED:
default:
return false;
}
}
public static boolean isCancellable(Message executionReport){
if (isExecutionReport(executionReport)){
try {
return isCancellable(executionReport.getChar(OrdStatus.FIELD));
} catch (FieldNotFound e) {
return false;
}
}
return false;
}
/**
* FIX market depth constant for "best bid or offer" or Level I
*/
public static final int TOP_OF_BOOK_DEPTH = 1;
/**
* FIX market depth constant for "full book" or Level II
*/
public static final int FULL_BOOK_DEPTH = 0;
/**
* Determines if the given message is a Level I or "top of book" message.
*
* @param inMessage a <code>Message</code> value
* @return a <code>boolean</code> value
*/
public static boolean isLevelOne(Message inMessage)
{
int depth;
try {
depth = getMarketDepth(inMessage);
} catch (FieldNotFound e) {
return false;
}
return depth == TOP_OF_BOOK_DEPTH;
}
/**
* Determines if the given message is a Level II or "depth of book" message.
*
* @param inMessage a <code>Message</code> value
* @return a <code>boolean</code> value
*/
public static boolean isLevelTwo(Message inMessage)
{
int depth;
try {
depth = getMarketDepth(inMessage);
} catch (FieldNotFound e) {
return false;
}
return depth == FULL_BOOK_DEPTH;
}
/**
* Determines if the given message is a Level II or "full book" message.
*
* @param inMessage a <code>Message</code> value
* @return a <code>boolean</code> value
*/
public static boolean isFullBook(Message inMessage)
{
int depth;
try {
depth = getMarketDepth(inMessage);
} catch (FieldNotFound e) {
return false;
}
return depth == FULL_BOOK_DEPTH;
}
/**
* Gets the market depth for the given message.
*
* @param inMessage a <code>Message</code> value
* @return an <code>int</code>
* @throws FieldNotFound if the given message does not contain market depth information
*/
private static int getMarketDepth(Message inMessage)
throws FieldNotFound
{
return inMessage.getInt(MarketDepth.FIELD);
}
/**
* See {@link #fillFieldsFromExistingMessage(quickfix.Message, quickfix.Message, boolean, java.util.Set)}.
* Invokes this api with a null inclusion set.
*
* @param outgoingMessage The message whose fields need to be filled.
* @param existingMessage The message whose fields need to be copied.
* @param onlyCopyRequiredFields if only required fields should be copied.
*/
public static void fillFieldsFromExistingMessage(Message outgoingMessage,
Message existingMessage,
boolean onlyCopyRequiredFields)
{
fillFieldsFromExistingMessage(outgoingMessage, existingMessage, onlyCopyRequiredFields, null);
}
/**
* See {@link #fillFieldsFromExistingMessage(quickfix.Message, quickfix.Message, DataDictionary, boolean, java.util.Set)}.
* Invokes this api with a null inclusion set.
*
* @param outgoingMessage The message whose fields need to be filled.
* @param existingMessage The message whose fields need to be copied.
* @param dict The data dictionary.
* @param onlyCopyRequiredFields if only required fields should be copied.
*/
public static void fillFieldsFromExistingMessage(Message outgoingMessage,
Message existingMessage,
DataDictionary dict,
boolean onlyCopyRequiredFields)
{
fillFieldsFromExistingMessage(outgoingMessage, existingMessage, dict, onlyCopyRequiredFields, null);
}
/**
* Helper method to extract all useful fields from an existing message into another message
* This is usually called when the "existing" message is malformed and is missing some fields,
* and an appropriate "reject" message needs to be sent.
* Can't say we are proud of this method - it's rather a kludge.
* Goes through all the required fields in "outgoing" message, and ignores any missing ones
* Skips over any of the outgoing fields that have already been set
*
* Use cases: an order comes in missing a Side, so we need to create an ExecutionReport
* that's a rejection, and need to extract all the other fields (ClOrdId, size, etc)
* which may or may not be present since the order is malformed
* <strong>warning: ignores groups</strong>
*
* @param outgoingMessage The message whose fields need to be filled.
* @param existingMessage The message whose fields need to be copied.
* @param onlyCopyRequiredFields if only required fields should be copied.
* @param inclusionSet the set of fields that should be copied. Can be null.
* If not null, only the fields present in this set are copied.
*/
public static void fillFieldsFromExistingMessage(Message outgoingMessage,
Message existingMessage,
boolean onlyCopyRequiredFields,
Set<Integer> inclusionSet)
{
fillFieldsFromExistingMessage
(outgoingMessage,
existingMessage,
CurrentFIXDataDictionary.getCurrentFIXDataDictionary().
getDictionary(),
onlyCopyRequiredFields,
inclusionSet);
}
/**
* Helper method to extract all useful fields from an existing message into another message
* This is usually called when the "existing" message is malformed and is missing some fields,
* and an appropriate "reject" message needs to be sent.
* Can't say we are proud of this method - it's rather a kludge.
* Goes through all the required fields in "outgoing" message, and ignores any missing ones
* Skips over any of the outgoing fields that have already been set
*
* Use cases: an order comes in missing a Side, so we need to create an ExecutionReport
* that's a rejection, and need to extract all the other fields (ClOrdId, size, etc)
* which may or may not be present since the order is malformed
* <strong>warning: ignores groups</strong>
*
* @param outgoingMessage The message whose fields need to be filled.
* @param existingMessage The message whose fields need to be copied.
* @param dict The data dictionary.
* @param onlyCopyRequiredFields if only required fields should be copied.
* @param inclusionSet the set of fields that should be copied. Can be null.
* If not null, only the fields present in this set are copied.
*/
public static void fillFieldsFromExistingMessage(Message outgoingMessage,
Message existingMessage,
DataDictionary dict,
boolean onlyCopyRequiredFields,
Set<Integer> inclusionSet)
{
String msgType=null;
try {
msgType=outgoingMessage.getHeader().getString(MsgType.FIELD);
} catch (FieldNotFound ex) {
Messages.FIX_OUTGOING_NO_MSGTYPE.error(LOGGER_NAME, ex);
return;
}
Iterator<Field<?>> fieldItr=existingMessage.iterator();
while (fieldItr.hasNext()) {
Field<?> field = fieldItr.next();
int fieldInt=field.getTag();
if(inclusionSet != null && !(inclusionSet.contains(fieldInt))) {
continue;
}
if ((!onlyCopyRequiredFields || dict.isRequiredField(msgType,
fieldInt))
&& !outgoingMessage.isSetField(fieldInt)
&& dict.isMsgField(msgType, fieldInt)) {
try {
outgoingMessage.setField(existingMessage
.getField(new StringField(fieldInt)));
} catch (FieldNotFound e) {
// do nothing and ignore
}
}
}
}
/**
* Warning! This method does not handle groups.
* @param copyTo
* @param copyFrom
*/
public static void copyFields(FieldMap copyTo,
FieldMap copyFrom)
{
Iterator<Field<?>> iter = copyFrom.iterator();
while (iter.hasNext()){
Field<?> field = iter.next();
try {
copyTo.setField(copyFrom.getField(new StringField(field.getTag())));
} catch (FieldNotFound e) {
// do nothing
}
}
}
public static boolean isRequiredField(Message message, int whichField) {
boolean required = false;
try {
String msgType;
msgType = message.getHeader().getString(MsgType.FIELD);
return isRequiredField(msgType, whichField);
} catch (Exception e) {
// Ignore
}
return required;
}
public static boolean isRequiredField(String msgType, int whichField){
boolean required = false;
try {
DataDictionary dictionary = CurrentFIXDataDictionary
.getCurrentFIXDataDictionary().getDictionary();
required = dictionary.isRequiredField(msgType, whichField);
} catch (Exception anyException) {
// Ignore
}
return required;
}
//cl todo:need to check if this take care of custom fields
public static boolean isValidField(int whichField) {
boolean valid = false;
try {
DataDictionary dictionary = CurrentFIXDataDictionary
.getCurrentFIXDataDictionary().getDictionary();
valid = dictionary.isField(whichField);
} catch (Exception anyException) {
// Ignore
}
return valid;
}
/**
* Copy only required fields.
*/
public static void fillFieldsFromExistingMessage(Message outgoingMessage, Message existingMessage) {
fillFieldsFromExistingMessage(outgoingMessage, existingMessage, true);
}
public static void insertFieldIfMissing(int fieldNumber, String value, FieldMap fieldMap) throws CoreException {
if (fieldMap.isSetField(fieldNumber)){
StringField testField = new StringField(fieldNumber);
try {
fieldMap.getField(testField);
if(testField.getValue().equals(value)){
return;
}
} catch (FieldNotFound ignored) {
//Unexpected as isSetField() returned true
//Don't do anything so that we set the field before we return.
}
}
fieldMap.setField(new StringField(fieldNumber, value));
}
public static String getTextOrEncodedText(Message aMessage, String defaultString) {
String text = defaultString;
if (aMessage.isSetField(Text.FIELD)){
try {
text = aMessage.getString(Text.FIELD);
} catch (FieldNotFound ignored) {
}
} else {
try {
text = aMessage.getString(EncodedText.FIELD); //i18n_string todo use the correct MessageEncoding value
} catch (FieldNotFound ignored) {
}
}
return text;
}
/**
*
* @param msgType the msgType of the request message
* @return the field that represents the request/response correlation ID in the request message
*/
public static StringField getCorrelationField(FIXVersion version, String msgType) {
StringField reqIDField = null;
if (MsgType.COLLATERAL_REQUEST.equals(msgType) || MsgType.COLLATERAL_RESPONSE.equals(msgType)){
reqIDField = new CollReqID();
} else if (MsgType.CONFIRMATION_REQUEST.equals(msgType) || MsgType.CONFIRMATION.equals(msgType)){
reqIDField = new ConfirmReqID();
} else if (MsgType.DERIVATIVE_SECURITY_LIST_REQUEST.equals(msgType) || MsgType.DERIVATIVE_SECURITY_LIST.equals(msgType)){
reqIDField = new SecurityReqID();
} else if (MsgType.MARKET_DATA_REQUEST.equals(msgType) || MsgType.MARKET_DATA_INCREMENTAL_REFRESH.equals(msgType) || MsgType.MARKET_DATA_REQUEST_REJECT.equals(msgType) || MsgType.MARKET_DATA_SNAPSHOT_FULL_REFRESH.equals(msgType)){
reqIDField = new MDReqID();
} else if (MsgType.NETWORK_STATUS_REQUEST.equals(msgType) || MsgType.NETWORK_STATUS_RESPONSE.equals(msgType)){
reqIDField = new NetworkRequestID();
} else if (MsgType.POSITION_MAINTENANCE_REQUEST.equals(msgType) || MsgType.POSITION_MAINTENANCE_REPORT.equals(msgType)
||MsgType.REQUEST_FOR_POSITIONS.equals(msgType) || MsgType.REQUEST_FOR_POSITIONS_ACK.equals(msgType) || MsgType.POSITION_REPORT.equals(msgType)){
reqIDField = new PosReqID();
} else if (MsgType.QUOTE_REQUEST.equals(msgType) || MsgType.QUOTE_REQUEST_REJECT.equals(msgType) || MsgType.QUOTE.equals(msgType)){
reqIDField = new QuoteReqID();
} else if (MsgType.QUOTE_STATUS_REQUEST.equals(msgType) || MsgType.QUOTE_STATUS_REPORT.equals(msgType)){
if (FIXVersion.FIX42.equals(version)){
reqIDField = new QuoteID();
} else {
reqIDField = new QuoteStatusReqID();
}
} else if (MsgType.RFQ_REQUEST.equals(msgType)){
reqIDField = new RFQReqID();
} else if (MsgType.SECURITY_DEFINITION_REQUEST.equals(msgType) || MsgType.SECURITY_DEFINITION.equals(msgType)
|| MsgType.SECURITY_LIST_REQUEST.equals(msgType) || MsgType.SECURITY_LIST.equals(msgType)
|| MsgType.SECURITY_TYPE_REQUEST.equals(msgType) || MsgType.SECURITY_TYPES.equals(msgType)){
reqIDField = new SecurityReqID();
} else if (MsgType.SECURITY_STATUS_REQUEST.equals(msgType) || MsgType.SECURITY_STATUS.equals(msgType)){
reqIDField = new SecurityStatusReqID();
} else if (MsgType.SETTLEMENT_INSTRUCTION_REQUEST.equals(msgType) || MsgType.SETTLEMENT_INSTRUCTIONS.equals(msgType)){
reqIDField = new SettlInstReqID();
} else if (MsgType.TRADE_CAPTURE_REPORT_REQUEST.equals(msgType) || MsgType.TRADE_CAPTURE_REPORT.equals(msgType) || MsgType.TRADE_CAPTURE_REPORT_REQUEST_ACK.equals(msgType)){
reqIDField = new TradeRequestID();
} else if (MsgType.TRADING_SESSION_STATUS_REQUEST.equals(msgType) || MsgType.TRADING_SESSION_STATUS.equals(msgType)){
reqIDField = new TradSesReqID();
} else if (MsgType.USER_REQUEST.equals(msgType) || MsgType.USER_RESPONSE.equals(msgType)){
reqIDField = new UserRequestID();
}
return reqIDField;
}
public static void mergeMarketDataMessages(Message marketDataSnapshotFullRefresh, Message marketDataIncrementalRefresh, FIXMessageFactory factory){
if (!isMarketDataSnapshotFullRefresh(marketDataSnapshotFullRefresh)){
throw new IllegalArgumentException(Messages.FIX_MD_MERGE_INVALID_INCOMING_SNAPSHOT.getText());
}
if (!isMarketDataIncrementalRefresh(marketDataIncrementalRefresh)){
throw new IllegalArgumentException(Messages.FIX_MD_MERGE_INVALID_INCOMING_INCREMENTAL.getText());
}
HashMap<Character, Group> consolidatingSet = new HashMap<Character, Group>();
addGroupsToMap(marketDataSnapshotFullRefresh, factory, consolidatingSet);
addGroupsToMap(marketDataIncrementalRefresh, factory, consolidatingSet);
marketDataSnapshotFullRefresh.removeGroup(NoMDEntries.FIELD);
for (Group aGroup : consolidatingSet.values()) {
Group group = factory.createGroup(MsgType.MARKET_DATA_SNAPSHOT_FULL_REFRESH, NoMDEntries.FIELD);
group.setFields(aGroup);
marketDataSnapshotFullRefresh.addGroup(group);
}
}
private static void addGroupsToMap(Message marketDataMessage, FIXMessageFactory factory, HashMap<Character, Group> consolidatingSet){
try {
int noMDEntries = marketDataMessage.getInt(NoMDEntries.FIELD);
String msgType = marketDataMessage.getHeader().getString(MsgType.FIELD);
for (int i = 1; i <= noMDEntries; i++){
Group group = factory.createGroup(msgType, NoMDEntries.FIELD);
try {
marketDataMessage.getGroup(i, group);
consolidatingSet.put(group.getChar(MDEntryType.FIELD), group);
} catch (FieldNotFound e) {
//just continue
}
}
} catch (FieldNotFound e) {
// ignore
}
}
/**
* package name for the quickfix fields
*/
private static final String QUICKFIX_PACKAGE = "quickfix.field."; //$NON-NLS-1$
/**
* Create a <code>FIX</code> field of the given name.
*
* @param inFieldName a <code>String</code> value to containing the name of the field to create
* @return a <code>Field<?></code> value
* @throws NullPointerException if <code>inFieldName</code> is null
* @throws CoreException if the value cannot be converted to a field
*/
public static Field<?> getQuickFixFieldFromName(String inFieldName)
throws CoreException
{
if(inFieldName == null) {
throw new NullPointerException();
}
Throwable error = null;
try {
// try the easy case first: the given field might be one of the pre-packaged quickfix fields
return (Field<?>)Class.forName(QUICKFIX_PACKAGE + inFieldName).newInstance();
} catch(ClassNotFoundException cnfe) {
// if this exception is thrown, that means that the field does not correspond to a pre-packaged quickfix field
// see if we can create a custom field - this means that the field name has to be parseable as an int
try {
int fieldInt = Integer.parseInt(inFieldName);
return new CustomField<Integer>(fieldInt,
null);
} catch (Throwable t) {
error = t;
}
} catch (Throwable t) {
error = t;
}
// bah, can't create a field with the stuff we're given
throw new CoreException(error,
new I18NBoundMessage1P(CANNOT_CREATE_FIX_FIELD,
inFieldName));
}
/**
* Converts the supplied fix message to a pretty string that can be logged.
* The returned string prints the human readable representations of field
* tags and enumeration type field values to make them easily readable.
* <p>
* This method ignores any repeating groups in the message.
*
* @param msg the FIX Message.
* @param inDict the data dictionary for the message
*
* @return the string form of the message.
*/
public static String toPrettyString(Message msg, FIXDataDictionary inDict) {
HashMap<String, String> fields = fieldsToMap(msg, inDict);
fields.put("HEADER", fieldsToMap(msg.getHeader(), //$NON-NLS-1$
inDict).toString());
fields.put("TRAILER", fieldsToMap(msg.getTrailer(), //$NON-NLS-1$
inDict).toString());
return fields.toString();
}
/**
* Converts the supplied FieldMap to a map with human readable field
* names (based on the supplied dictionary) as keys and field values
* as string values.
* <p>
* This method ignores any repeating groups present in the fieldMap.
*
* @param inMap The FIX FieldMap.
* @param inDict The FIX data dictionary.
*
* @return The map containing supplied fieldMap's keys & values.
*/
private static HashMap<String, String> fieldsToMap(FieldMap inMap,
FIXDataDictionary inDict) {
HashMap<String, String> fields = new HashMap<String, String>();
Iterator<Field<?>> iterator = inMap.iterator();
while(iterator.hasNext()) {
Field<?> f = iterator.next();
String value;
if(f instanceof StringField) {
value = ((StringField)f).getValue();
if (inDict != null) {
String humanValue = inDict.getHumanFieldValue(f.getTag(),value);
if(humanValue != null) {
value = new StringBuilder().append(humanValue).
append("(").append(value). //$NON-NLS-1$
append(")").toString(); //$NON-NLS-1$
}
}
} else {
value = String.valueOf(f.getObject());
}
String name = null;
if (inDict != null) {
name = inDict.getHumanFieldName(f.getTag());
}
if(name == null) {
name = String.valueOf(f.getTag());
} else {
name = new StringBuilder().append(name).
append("(").append(f.getTag()). //$NON-NLS-1$
append(")").toString(); //$NON-NLS-1$
}
fields.put(name,value);
}
return fields;
}
}