/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *   or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */

package org.apache.synapse.transport.fix;

import org.apache.axiom.attachments.ByteArrayDataSource;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.transport.base.BaseUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import quickfix.*;
import quickfix.field.*;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.xml.namespace.QName;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.*;

public class FIXUtils {

    private static final Log log = LogFactory.getLog(FIXUtils.class);
    private static FIXUtils _instance = new FIXUtils();

    public static FIXUtils getInstance() {
        return _instance;
    }

    /**
     * FIX messages are non-XML. So convert them into XML using the AXIOM API.
     * Put the FIX message into an Axis2 MessageContext.The basic format of the
     * generated SOAP envelope;
     * <p/>
     * <soapEnvelope>
     * <soapBody>
     * <message>
     * <header> ....</header>
     * <body> .... </body>
     * <trailer> .... </trailer>
     * </message>
     * </soapBody>
     * </soapEnvelope>
     *
     * @param message   the FIX message
     * @param counter   application level sequence number of the message
     * @param sessionID the incoming session
     * @param msgCtx    the Axis2 MessageContext to hold the FIX message
     * @throws AxisFault the exception thrown when invalid soap envelopes are set to the msgCtx
     */
    public void setSOAPEnvelope(Message message, int counter, String sessionID,
                                MessageContext msgCtx) throws AxisFault {

        if (log.isDebugEnabled()) {
            log.debug("Creating SOAP envelope for FIX message...");
        }

        SOAPFactory soapFactory = OMAbstractFactory.getSOAP11Factory();
        OMElement msg = soapFactory.createOMElement(FIXConstants.FIX_MESSAGE, null);
        msg.addAttribute(soapFactory.createOMAttribute(FIXConstants.FIX_MESSAGE_INCOMING_SESSION,
                null, sessionID));
        msg.addAttribute(soapFactory.createOMAttribute
                (FIXConstants.FIX_MESSAGE_COUNTER, null, String.valueOf(counter)));

        OMElement header = soapFactory.createOMElement(FIXConstants.FIX_HEADER, null);
        OMElement body = soapFactory.createOMElement(FIXConstants.FIX_BODY, null);
        OMElement trailer = soapFactory.createOMElement(FIXConstants.FIX_TRAILER, null);

        //process FIX header
        Iterator<Field<?>> iter = message.getHeader().iterator();
        if (iter != null) {
            while (iter.hasNext()) {
                Field<?> field = iter.next();
                OMElement msgField = soapFactory.createOMElement(FIXConstants.FIX_FIELD, null);
                msgField.addAttribute(soapFactory.createOMAttribute(FIXConstants.FIX_FIELD_ID,
                        null, String.valueOf(field.getTag())));
                Object value = field.getObject();

                if (value instanceof byte[]) {
                    DataSource dataSource = new ByteArrayDataSource((byte[]) value);
                    DataHandler dataHandler = new DataHandler(dataSource);
                    String contentID = msgCtx.addAttachment(dataHandler);
                    OMElement binaryData = soapFactory.createOMElement(
                            FIXConstants.FIX_BINARY_FIELD, null);
                    String binaryCID = "cid:" + contentID;
                    binaryData.addAttribute(FIXConstants.FIX_MESSAGE_REFERENCE, binaryCID, null);
                    msgField.addChild(binaryData);
                } else {
                    createOMText(soapFactory, msgField, value.toString());
                }
                header.addChild(msgField);
            }
        }
        //process FIX body
        convertFIXBodyToXML(message, body, soapFactory, msgCtx);

        //process FIX trailer
        iter = message.getTrailer().iterator();
        if (iter != null) {
            while (iter.hasNext()) {
                Field<?> field = iter.next();
                OMElement msgField = soapFactory.createOMElement(FIXConstants.FIX_FIELD, null);
                msgField.addAttribute(soapFactory.
                        createOMAttribute(FIXConstants.FIX_FIELD_ID, null,
                        String.valueOf(field.getTag())));
                Object value = field.getObject();

                if (value instanceof byte[]) {
                    DataSource dataSource = new ByteArrayDataSource((byte[]) value);
                    DataHandler dataHandler = new DataHandler(dataSource);
                    String contentID = msgCtx.addAttachment(dataHandler);
                    OMElement binaryData = soapFactory.createOMElement(
                            FIXConstants.FIX_BINARY_FIELD, null);
                    String binaryCID = "cid:" + contentID;
                    binaryData.addAttribute(FIXConstants.FIX_MESSAGE_REFERENCE, binaryCID, null);
                    msgField.addChild(binaryData);
                } else {
                    createOMText(soapFactory, msgField, value.toString());
                }
                trailer.addChild(msgField);
            }
        }

        msg.addChild(header);
        msg.addChild(body);
        msg.addChild(trailer);
        SOAPEnvelope envelope = soapFactory.getDefaultEnvelope();
        envelope.getBody().addChild(msg);
        msgCtx.setEnvelope(envelope);
    }
    

    /**
     * Constructs the XML infoset for the FIX message body
     *
     * @param message the FIX message
     * @param body the body element of the XML infoset
     * @param soapFactory the SOAP factory to create XML elements
     * @param msgCtx the Axis2 Message context
     * @throws AxisFault on error
     */
    private void convertFIXBodyToXML(FieldMap message, OMElement body, SOAPFactory soapFactory,
                                        MessageContext msgCtx) throws AxisFault{

        if (log.isDebugEnabled()) {
            log.debug("Generating FIX message body (Message ID: " + msgCtx.getMessageID() + ")");
        }

        Iterator<Field<?>> iter = message.iterator();
        if (iter != null) {
             while (iter.hasNext()) {
                 Field<?> field = iter.next();
                 OMElement msgField = soapFactory.createOMElement(FIXConstants.FIX_FIELD, null);
                 msgField.addAttribute(soapFactory.
                         createOMAttribute(FIXConstants.FIX_FIELD_ID, null,
                         String.valueOf(field.getTag())));
                 Object value = field.getObject();

                 if (value instanceof byte[]) {
                     DataSource dataSource = new ByteArrayDataSource((byte[]) value);
                     DataHandler dataHandler = new DataHandler(dataSource);
                     String contentID = msgCtx.addAttachment(dataHandler);
                     OMElement binaryData = soapFactory.createOMElement(
                             FIXConstants.FIX_BINARY_FIELD, null);
                     String binaryCID = "cid:" + contentID;
                     binaryData.addAttribute(FIXConstants.FIX_MESSAGE_REFERENCE, binaryCID, null);
                     msgField.addChild(binaryData);
                 } else {
                     createOMText(soapFactory, msgField, value.toString());
                 }

                 body.addChild(msgField);
             }
        }
        
        //process FIX repeating groups
        Iterator<Integer> groupKeyItr = message.groupKeyIterator();
        if (groupKeyItr != null) {
            while (groupKeyItr.hasNext()) {
                int groupKey =  groupKeyItr.next();
                OMElement groupsField = soapFactory.createOMElement(FIXConstants.FIX_GROUPS,
                        null);
                groupsField.addAttribute(FIXConstants.FIX_FIELD_ID,
                        String.valueOf(groupKey),null);
                List<Group> groupList = message.getGroups(groupKey);
                Iterator<Group> groupIterator = groupList.iterator();

                while (groupIterator.hasNext()) {
                    Group msgGroup = groupIterator.next();
                    OMElement groupField = soapFactory.createOMElement(FIXConstants.FIX_GROUP, null);
                    // rec. call the method to process the repeating groups
                    convertFIXBodyToXML(msgGroup, groupField, soapFactory, msgCtx);
                    groupsField.addChild(groupField);
                }
                body.addChild(groupsField);
            }
        }
    }


    private void generateFIXBody(OMElement node, FieldMap message, MessageContext msgCtx,
                                 boolean withNs, String nsURI, String nsPrefix) throws IOException {

        Iterator bodyElements = node.getChildElements();
        while (bodyElements.hasNext()) {
            OMElement bodyNode = (OMElement) bodyElements.next();
            String nodeLocalName = bodyNode.getLocalName();

            //handle repeating groups
            if (nodeLocalName.equals(FIXConstants.FIX_GROUPS)){
                int groupsKey = Integer.parseInt(bodyNode.getAttributeValue(
                        new QName(FIXConstants.FIX_FIELD_ID)));
                Group group;
                Iterator groupElements = bodyNode.getChildElements();
                while (groupElements.hasNext()){
                    OMElement groupNode = (OMElement) groupElements.next();
                    Iterator groupFields = groupNode.getChildrenWithName(new QName(FIXConstants.FIX_FIELD));
                    List<Integer> idList = new ArrayList<Integer>();
                    while (groupFields.hasNext()) {
                        OMElement fieldNode = (OMElement) groupFields.next();
                        idList.add(Integer.parseInt(fieldNode.getAttributeValue(
                                new QName(FIXConstants.FIX_FIELD_ID))));
                    }

                    int[] order = new int[idList.size()];
                    for (int i = 0; i < order.length; i++) {
                        order[i] = idList.get(i);
                    }

                    group = new Group(groupsKey, order[0], order);
                    generateFIXBody(groupNode, group, msgCtx, withNs, nsURI, nsPrefix);
                    message.addGroup(group);
                }

            } else {
                String tag;
                if (withNs) {
                    tag = bodyNode.getAttributeValue(new QName(nsURI, FIXConstants.FIX_FIELD_ID,
                            nsPrefix));
                } else {
                    tag = bodyNode.getAttributeValue(new QName(FIXConstants.FIX_FIELD_ID));
                }

                 String value = null;
                 OMElement child = bodyNode.getFirstElement();
                 if (child != null) {
                     String href;
                     if (withNs) {
                         href = bodyNode.getFirstElement().
                                    getAttributeValue(new QName(nsURI, FIXConstants.FIX_FIELD_ID,
                                            nsPrefix)) ;
                     } else {
                         href = bodyNode.getFirstElement().
                                    getAttributeValue(new QName(FIXConstants.FIX_MESSAGE_REFERENCE));
                     }

                     if (href != null) {
                         DataHandler binaryDataHandler = msgCtx.getAttachment(href.substring(4));
                         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                         binaryDataHandler.writeTo(outputStream);
                         value = new String(outputStream.toByteArray());
                     }
                }
                else {
                     value = bodyNode.getText();
                }

                if (value != null) {
                    message.setString(Integer.parseInt(tag), value);
                }
            }
         }
    }


    /**
     * Extract the FIX message embedded in an Axis2 MessageContext
     *
     * @param msgCtx the Axis2 MessageContext
     * @return a FIX message
     * @throws java.io.IOException the exception thrown when handling erroneous binary content
     */
    public Message createFIXMessage(MessageContext msgCtx) throws IOException {
        if (log.isDebugEnabled()) {
            log.debug("Extracting FIX message from the message context (Message ID: " +
                    msgCtx.getMessageID() + ")");
        }

        boolean withNs = false;
        String nsPrefix = null;
        String nsURI = null;
        
        Message message = new Message();
        SOAPBody soapBody = msgCtx.getEnvelope().getBody();

        //find namespace information embedded in the FIX payload
        OMNamespace ns = getNamespaceOfFIXPayload(soapBody);
        if (ns != null) {
            withNs = true;
            nsPrefix = ns.getPrefix();
            nsURI = ns.getNamespaceURI();
        }

        OMElement messageNode;
        if (withNs) {
            messageNode = soapBody.getFirstChildWithName(new QName(nsURI, FIXConstants.FIX_MESSAGE,
                    nsPrefix));
        } else {
            messageNode = soapBody.getFirstChildWithName(new QName(FIXConstants.FIX_MESSAGE));
        }

        Iterator messageElements = messageNode.getChildElements();

        while (messageElements.hasNext()) {
            OMElement node = (OMElement) messageElements.next();
            //create FIX header
            if (node.getQName().getLocalPart().equals(FIXConstants.FIX_HEADER)) {
                Iterator headerElements = node.getChildElements();
                while (headerElements.hasNext()) {
                    OMElement headerNode = (OMElement) headerElements.next();
                    String tag;
                    if (withNs) {
                        tag = headerNode.getAttributeValue(new QName(nsURI,
                                FIXConstants.FIX_FIELD_ID, nsPrefix));
                    } else {
                        tag = headerNode.getAttributeValue(new QName(FIXConstants.FIX_FIELD_ID));
                    }
                    String value = null;

                    OMElement child = headerNode.getFirstElement();
                    if (child != null) {
                        String href;
                        if (withNs) {
                            href = headerNode.getFirstElement().getAttributeValue(
                                    new QName(nsURI, FIXConstants.FIX_MESSAGE_REFERENCE, nsPrefix));
                        } else {
                            href = headerNode.getFirstElement().
                                getAttributeValue(new QName(FIXConstants.FIX_MESSAGE_REFERENCE));
                        }

                        if (href != null) {
                            DataHandler binaryDataHandler = msgCtx.getAttachment(href.substring(4));
                            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                            binaryDataHandler.writeTo(outputStream);
                            value = new String(outputStream.toByteArray());
                        }
                    } else {
                        value = headerNode.getText();
                    }

                    if (value != null) {
                        message.getHeader().setString(Integer.parseInt(tag), value);
                    }
                }

            } else if (node.getQName().getLocalPart().equals(FIXConstants.FIX_BODY)) {
                //create FIX body
                generateFIXBody(node, message, msgCtx, withNs, nsURI, nsPrefix);
                
            } else if (node.getQName().getLocalPart().equals(FIXConstants.FIX_TRAILER)) {
                //create FIX trailer
                Iterator trailerElements = node.getChildElements();
                while (trailerElements.hasNext()) {
                    OMElement trailerNode = (OMElement) trailerElements.next();
                    String tag;
                    if (withNs) {
                        tag = trailerNode.getAttributeValue(new QName(nsURI,
                                FIXConstants.FIX_FIELD_ID, nsPrefix));
                    } else {
                        tag = trailerNode.getAttributeValue(new QName(FIXConstants.FIX_FIELD_ID));
                    }
                    String value = null;

                    OMElement child = trailerNode.getFirstElement();
                    if (child != null) {
                        String href;
                        if (withNs) {
                            href = trailerNode.getFirstElement().getAttributeValue(
                                    new QName(nsURI, FIXConstants.FIX_FIELD_ID, nsPrefix));
                        } else {
                             href = trailerNode.getFirstElement().
                                getAttributeValue(new QName(FIXConstants.FIX_MESSAGE_REFERENCE));
                        }
                        if (href != null) {
                            DataHandler binaryDataHandler = msgCtx.getAttachment(href.substring(4));
                            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                            binaryDataHandler.writeTo(outputStream);
                            value = new String(outputStream.toByteArray());
                        }
                    } else {
                        value = trailerNode.getText();
                    }

                    if (value != null) {
                        message.getTrailer().setString(Integer.parseInt(tag), value);
                    }
                }
            }
        }
        return message;
    }


    /**
     * Generate EPRs for the specified FIX service. A FIX end point can be uniquely
     * identified by a <host(IP), port> pair. Add some additional FIX session details
     * so the EPRs are more self descriptive.
     * A FIX EPR generated here looks like;
     * fix://10.100.1.80:9898?BeginString=FIX.4.4&SenderCompID=BANZAI&TargetCompID=EXEC&
     * SessionQualifier=mySession&Serviec=StockQuoteProxy
     *
     * @param acceptor    the SocketAcceptor associated with the service
     * @param serviceName the name of the service
     * @param ip          the IP address of the host
     * @return an array of EPRs for the specified service in String format
     */
    public static String[] generateEPRs(SocketAcceptor acceptor, String serviceName, String ip) {
        //Get all the addresses associated with the acceptor
        Map<SessionID, SocketAddress> socketAddresses = acceptor.getAcceptorAddresses();
        //Get all the sessions (SessionIDs) associated with the acceptor
        ArrayList<SessionID> sessions = acceptor.getSessions();
        String[] EPRList = new String[sessions.size()];

        //Generate an EPR for each session/socket address
        for (int i = 0; i < sessions.size(); i++) {
            SessionID sessionID = sessions.get(i);
            InetSocketAddress socketAddress = (InetSocketAddress) socketAddresses.get(sessionID);
            EPRList[i] = FIXConstants.FIX_PREFIX + ip + ":" + socketAddress.getPort() +
                    "?" + FIXConstants.BEGIN_STRING + "=" + sessionID.getBeginString() +
                    "&" + FIXConstants.SENDER_COMP_ID + "=" + sessionID.getTargetCompID() +
                    "&" + FIXConstants.TARGET_COMP_ID + "=" + sessionID.getSenderCompID();

            String sessionQualifier = sessionID.getSessionQualifier();
            if (sessionQualifier != null && !sessionQualifier.equals("")) {
                EPRList[i] += "&" + FIXConstants.SESSION_QUALIFIER + "=" + sessionQualifier;
            }

            String senderSubID = sessionID.getSenderSubID();
            if (senderSubID != null && !senderSubID.equals("")) {
                EPRList[i] += "&" + FIXConstants.SENDER_SUB_ID + "=" + senderSubID;
            }

            String targetSubID = sessionID.getTargetSubID();
            if (targetSubID != null && !targetSubID.equals("")) {
                EPRList[i] += "&" + FIXConstants.TARGET_SUB_ID + "=" + targetSubID;
            }

            String senderLocationID = sessionID.getSenderLocationID();
            if (senderLocationID != null && !senderLocationID.equals("")) {
                EPRList[i] += "&" + FIXConstants.SENDER_LOCATION_ID + "=" + senderLocationID;
            }

            String targetLocationID = sessionID.getTargetLocationID();
            if (targetLocationID != null && !targetLocationID.equals("")) {
                EPRList[i] += "&" + FIXConstants.TARGET_LOCATION_ID + "=" + targetLocationID;
            }

            EPRList[i] += "&Service=" + serviceName;
        }
        return EPRList;
    }

    public static String[] getEPRs(SessionSettings settings) throws FieldConvertError, ConfigError {
        Iterator<SessionID> sessions = settings.sectionIterator();
        String[] EPRs = new String[settings.size()];
        int i = 0;
        while (sessions.hasNext()) {
            SessionID session = sessions.next();
            String EPR = FIXConstants.FIX_PREFIX;
            String paramValue;

            EPR += settings.getString(session, FIXConstants.SOCKET_CONNECT_HOST);
            EPR += ":" + settings.getString(session, FIXConstants.SOCKET_CONNECT_PORT);
            EPR += "?" + FIXConstants.BEGIN_STRING + "=";
            EPR += settings.getString(session, FIXConstants.BEGIN_STRING);
            EPR += "&" + FIXConstants.SENDER_COMP_ID + "=";
            EPR += settings.getString(session, FIXConstants.SENDER_COMP_ID);
            EPR += "&" + FIXConstants.TARGET_COMP_ID + "=";
            EPR += settings.getString(session, FIXConstants.TARGET_COMP_ID);

            try {
                paramValue = settings.getString(session, FIXConstants.SENDER_SUB_ID);
                if (paramValue != null) {
                   EPR += "&" + FIXConstants.SENDER_SUB_ID + "=";
                   EPR += paramValue;
                }
            }
            catch (ConfigError ignore) { }

            try {
                paramValue = settings.getString(session, FIXConstants.SENDER_LOCATION_ID);
                if (paramValue != null) {
                   EPR += "&" + FIXConstants.SENDER_LOCATION_ID + "=";
                   EPR += paramValue;
                }
            }
            catch (ConfigError ignore) { }

            try {
                paramValue = settings.getString(session, FIXConstants.TARGET_SUB_ID);
                if (paramValue != null) {
                   EPR += "&" + FIXConstants.TARGET_SUB_ID + "=";
                   EPR += paramValue;
                }
            }
            catch (ConfigError ignore) { }

            try {
                paramValue = settings.getString(session, FIXConstants.TARGET_LOCATION_ID);
                if (paramValue != null) {
                   EPR += "&" + FIXConstants.TARGET_LOCATION_ID + "=";
                   EPR += paramValue;
                }
            }
            catch (ConfigError ignore) { }

            EPRs[i] = EPR;
        }
        return EPRs;
    }

    /**
     * Compares two given FIX URL strings. The second URL is considered equal to the
     * first URL if all the properties in the first URL also exist in the second URL
     * and if they have equals values.
     *
     * @param url1 a FIX URL String
     * @param url2 a FIX URL String
     * @return a boolean value
     */
    public static boolean compareURLs(String url1, String url2) {
        if (!url1.substring(0, url1.indexOf("?")).equals(url2.substring(0, url2.indexOf("?")))) {
             return false;
        } else {
            Hashtable<String,String> properties1 = BaseUtils.getEPRProperties(url1);
            Hashtable<String, String> properties2 = BaseUtils.getEPRProperties(url2);
            for (Map.Entry<String,String> entry : properties1.entrySet()) {
                if (!properties2.containsKey(entry.getKey())) {
                    return false;
                } else if (!properties1.get(entry.getKey()).equals(entry.getValue())) {
                    return false;
                }
            }
        }
        return true;
    }

    /*
     * This is here because AXIOM does not support removing CDATA tags yet. Given a String embedded in
     * CDATA tags this method will return the String element only.
     *
     * @param str the String with CDATA tags
     * @return String with CDATA tags stripped
     *
    private static String removeCDATA(String str) {
        if (str.indexOf("<![CDATA[") != -1) {
            str = str.split("CDATA")[1].split("]></field>")[0];
		    str= str.substring(1, str.length()-1);
		    return str;
        } else {
            return str;
        }
    }*/

    /**
     * Extracts the fields related to message forwarding (third party routing) from
     * the FIX header.
     *
     * @param message the FIX message
     * @return a Map of forwarding parameters
     */
    public static Map<String, String> getMessageForwardingParameters(Message message) {

        Map<String, String> map = new HashMap<String, String>();
        String value = getHeaderFieldValue(message, BeginString.FIELD);
        map.put(FIXConstants.BEGIN_STRING, value);
        value = getHeaderFieldValue(message, SenderCompID.FIELD);
        map.put(FIXConstants.SENDER_COMP_ID, value);
        value = getHeaderFieldValue(message, SenderSubID.FIELD);
        map.put(FIXConstants.SENDER_SUB_ID, value);
        value = getHeaderFieldValue(message, SenderLocationID.FIELD);
        map.put(FIXConstants.SENDER_LOCATION_ID, value);
        value = getHeaderFieldValue(message, TargetCompID.FIELD);
        map.put(FIXConstants.TARGET_COMP_ID, value);
        value = getHeaderFieldValue(message, DeliverToCompID.FIELD);
        map.put(FIXConstants.DELIVER_TO_COMP_ID, value);
        value = getHeaderFieldValue(message, DeliverToSubID.FIELD);
        map.put(FIXConstants.DELIVER_TO_SUB_ID, value);
        value = getHeaderFieldValue(message, DeliverToLocationID.FIELD);
        map.put(FIXConstants.DELIVER_TO_LOCATION_ID, value);
        value = getHeaderFieldValue(message, OnBehalfOfCompID.FIELD);
        map.put(FIXConstants.ON_BEHALF_OF_COMP_ID, value);
        value = getHeaderFieldValue(message, OnBehalfOfSubID.FIELD);
        map.put(FIXConstants.ON_BEHALF_OF_SUB_ID, value);
        value = getHeaderFieldValue(message, OnBehalfOfLocationID.FIELD);
        map.put(FIXConstants.ON_BEHALF_OF_LOCATION_ID, value);
        return map;
    }

    private static String getHeaderFieldValue(Message message, int tag) {
        try {
            return message.getHeader().getString(tag);
        } catch (FieldNotFound fieldNotFound) {
            return null;
        }
    }

    /**
     * Extracts the name of the service which processed the message from the MessageContext
     *
     * @param msgCtx Axis2 MessageContext of a message
     * @return name of the AxisService
     * @throws org.apache.axis2.AxisFault on error
     */
    public static String getServiceName(MessageContext msgCtx) throws AxisFault {

        Object serviceParam = msgCtx.getProperty(FIXConstants.FIX_SERVICE_NAME);
        if (serviceParam != null) {
            String serviceName = serviceParam.toString();
            if (serviceName != null && !serviceName.equals("")) {
                return serviceName;
            }
        }

        Map trpHeaders = (Map) msgCtx.getProperty(MessageContext.TRANSPORT_HEADERS);
        //try to get the service from the transport headers
        if (trpHeaders != null) {
            String serviceName = (String) trpHeaders.get(FIXConstants.FIX_MESSAGE_SERVICE);
            if (serviceName != null) {
                return serviceName;
            }
        }
        throw new AxisFault("Unable to find a valid service for the message");
    }

    /**
     * Extracts the application type for the message from the message context
     *
     * @param msgCtx Axis2 Message Context
     * @return application type of the message
     */
    public static String getFixApplication(MessageContext msgCtx) {
        Map trpHeaders = (Map) msgCtx.getProperty(MessageContext.TRANSPORT_HEADERS);
        //try to get the application type from the transport headers
        String fixApplication = null;
        if (trpHeaders != null) {
            fixApplication = (String) trpHeaders.get(FIXConstants.FIX_MESSAGE_APPLICATION);
        }
        return fixApplication;
    }

    /**
     * Creates a Map of transport headers for a message
     *
     * @param serviceName    name of the service to which the message belongs to
     * @param fixApplication FIX application type
     * @return a Map of transport headers
     */
    public static Map<String, String> getTransportHeaders(String serviceName,
                                                          String fixApplication) {

        Map<String, String> trpHeaders = new HashMap<String, String>();
        trpHeaders.put(FIXConstants.FIX_MESSAGE_SERVICE, serviceName);
        trpHeaders.put(FIXConstants.FIX_MESSAGE_APPLICATION, fixApplication);
        return trpHeaders;
    }

    /**
     * Reads a FIX EPR and returns the host and port on a String array
     *
     * @param fixEPR a FIX EPR
     * @return an array of Strings containing addressing elements
     * @throws AxisFault on error
     */
    public static String[] getSocketAddressElements(String fixEPR) throws AxisFault {
        int propPos = fixEPR.indexOf("?");
        if (propPos != -1 && fixEPR.startsWith(FIXConstants.FIX_PREFIX)) {
            String address = fixEPR.substring(FIXConstants.FIX_PREFIX.length(), propPos);
            String[] socketAddressElemets = address.split(":");
            if (socketAddressElemets.length == 2) {
                return socketAddressElemets;
            }
        }
        throw new AxisFault("Malformed FIX EPR: " + fixEPR);
    }

    /**
     * Reads the SOAP body of a message and attempts to retreive the application level
     * sequence number
     *
     * @param msgCtx Axis2 MessageContext
     * @return application level sequence number or -1
     */
    public static int getSequenceNumber(MessageContext msgCtx) {
        int seqNum;
        SOAPBody body = msgCtx.getEnvelope().getBody();
        OMNamespace ns = getNamespaceOfFIXPayload(body);
        if (ns == null) {
            OMElement messageNode = body.getFirstChildWithName(new QName(FIXConstants.FIX_MESSAGE));
            String value = messageNode.getAttributeValue(new QName(FIXConstants.FIX_MESSAGE_COUNTER));
            if (value != null) {
                seqNum = Integer.parseInt(value);
            } else {
                seqNum = -1;
            }
        }
        else {
            seqNum = getSequenceNumber(body, ns);
        }
        return seqNum;
    }

    /**
     * Reads the SOAP body of a message and attempts to retreive the application level
     * sequence number
     *
     * @param body Body of the SOAP message
     * @param ns Namespace
     * @return application level sequence number or -1
     */
    private static int getSequenceNumber(SOAPBody body, OMNamespace ns) {
        OMElement messageNode = body.getFirstChildWithName(new QName(ns.getNamespaceURI(),
                FIXConstants.FIX_MESSAGE, ns.getPrefix()));
        String value = messageNode.getAttributeValue(new QName(ns.getNamespaceURI(),
                FIXConstants.FIX_MESSAGE_COUNTER, ns.getPrefix()));
        if (value != null) {
             return Integer.parseInt(value);
        } else {
             return -1;
        }
    }

    /**
     * Reads the SOAP body of a message and attempts to retreive the session identifier string
     *
     * @param msgCtx Axis2 MessageContext
     * @return a String uniquely identifying a session or null
     */
    public static String getSourceSession(MessageContext msgCtx) {
        String srcSession;
        SOAPBody body = msgCtx.getEnvelope().getBody();
        OMNamespace ns = getNamespaceOfFIXPayload(body);
        if (ns == null) {
            OMElement messageNode = body.getFirstChildWithName(new QName(FIXConstants.FIX_MESSAGE));
            srcSession = messageNode.getAttributeValue(new QName(
                    FIXConstants.FIX_MESSAGE_INCOMING_SESSION));
        } else {
            srcSession = getSourceSession(body, ns);
        }
        return srcSession;
    }

    /**
     * Reads the SOAP body of a message and attempts to retrieve the session identifier string
     * with a namesapce
     *
     * @param body Body of the SOAP message
     * @param ns Namespace
     * @return a String uniquely identifying a session or null
     */
    private static String getSourceSession(SOAPBody body, OMNamespace ns) {
           OMElement messageNode = body.getFirstChildWithName(new QName(ns.getNamespaceURI(),
                   FIXConstants.FIX_MESSAGE, ns.getPrefix()));
           return messageNode.getAttributeValue(new QName(ns.getNamespaceURI(),
                   FIXConstants.FIX_MESSAGE_INCOMING_SESSION, ns.getPrefix()));
    }

    /**
     * Creates a text node within a CDATA section selectively by looking at the enclosing text.
     *
     * @param soapFactory SOAPFactory instance used to create the OMText object
     * @param field Parent OMElement (field element)
     * @param text String text to be added to the field element
     */
    private static void createOMText(SOAPFactory soapFactory, OMElement field, String text) {
        if (text == null) {
            return;
        }
        if (text.indexOf('<') == -1 && text.indexOf('&') == -1 && text.indexOf('>') == -1) {
            soapFactory.createOMText(field, text);
        } else {
            soapFactory.createOMText(field, text, OMElement.CDATA_SECTION_NODE);
        }
    }

    /**
     * Read the FIX message payload and identify the namespace if exists
     *
     * @param fixBody FIX message payload
     * @return  namespace as a OMNamespace
     */
    public static OMNamespace getNamespaceOfFIXPayload(SOAPBody fixBody){
        return fixBody.getFirstElementNS();
    }
}