/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * @(#)ManagementMessageBuilderImpl.java - ver 1.1 - 01/04/2006
 *
 * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved.
 */

package com.sun.enterprise.jbi.serviceengine.util;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;


/**
 * BuildManagementMessageImpl provides utility methods to build messages (as XML string)
 * that are returned to the clients by the JBI Framework.
 *
 * @author bhavanishankar@dev.java.net.
 */
public class ManagementMessageBuilder {
    /**
     * String to represent UNKNOWN elements
     */
    private static final String MISSING_DATA_STRING = "UNKNOWN";

    /**
     * String to represent INVALID input
     */
    private static final String CANNOT_BUILD_MESSAGE_STRING =
            "CANNOT_BUILD_MESSAGE_INVALID_DATA";

    /**
     * Max. nesting level
     */
    private static final int MAX_NESTING_LEVEL = 32;


    /**
     * Creates a new <CODE>BuildManagementMessageImpl</CODE> instance.
     */
    public ManagementMessageBuilder() {
    }

    /**
     * Return an XML string of the task result(either status of exception).  This is the
     * task executed by the component
     *
     * @param cmObj Status details of the component
     * @return XML string with task status
     * @throws Exception If fails to build XML string
     */
    public String buildComponentMessage(ComponentMessageHolder cmObj)
            throws Exception {
        String mMsgTypeToBuild = null;
        String compTaskMsg = null;

        try {
            mMsgTypeToBuild = cmObj.getComponentMessageType();

            if (mMsgTypeToBuild.equalsIgnoreCase("STATUS_MSG")) {
                compTaskMsg = buildComponentTaskStatusMessage(cmObj);
            }

            if (mMsgTypeToBuild.equalsIgnoreCase("EXCEPTION_MSG")) {
                compTaskMsg = buildComponentTaskExceptionMessage(cmObj);
            }

            if (compTaskMsg == null) {
                compTaskMsg = CANNOT_BUILD_MESSAGE_STRING;
            }
        }
        catch (Exception e) {
            throw e;
        }

        return compTaskMsg;
    }

    /**
     * Return an XML string of the task status. This is the task executed by the
     * component
     *
     * @param mmObj Status details of the component
     * @return XML string with task status
     * @throws Exception If fails to build XML string
     */
    public String buildComponentTaskStatusMessage(ComponentMessageHolder mmObj)
            throws Exception {
        String cmpStatMsg = null;
        Document doc = null;
        String locToken = null;
        String locMessage = null;
        String locParam = null;

        try {
            String componentName = mmObj.getComponentName();

            if (componentName == null) {
                componentName = MISSING_DATA_STRING;
            }

            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            doc = db.newDocument();

            Element elem = doc.createElementNS(
                    "http://java.sun.com/xml/ns/jbi/management-message",
                    "component-task-result");
            Element compNameElem = doc.createElement("component-name");
            Element compTaskRsltDtlsElem =
                    doc.createElement("component-task-result-details");

            DOMUtil.UTIL.setTextData(compNameElem, componentName);

            Element taskRsltDtlsElem = buildStatusXMLFragment(doc, mmObj);
            compTaskRsltDtlsElem.appendChild(taskRsltDtlsElem);
            elem.appendChild(compNameElem);
            elem.appendChild(compTaskRsltDtlsElem);
            doc.appendChild(elem);

            StringWriter sw = new StringWriter();
            cmpStatMsg = DOMUtil.UTIL.DOM2String(doc, sw);
        }
        catch (Exception e) {
            e.printStackTrace();
            throw e;
        }

        return cmpStatMsg;
    }

    /**
     * DOCUMENT ME!
     *
     * @param doc   DOCUMENT ME!
     * @param mmObj DOCUMENT ME!
     * @return DOCUMENT ME!
     * @throws Exception DOCUMENT ME!
     */
    private Element buildStatusXMLFragment(
            Document doc,
            ComponentMessageHolder mmObj
    ) throws Exception {
        String cmpStatMsg = null;
        String locToken = null;
        String locMessage = null;
        String[] locParam = null;
        Element taskRsltDtlsElem = null;

        try {
            String taskName = mmObj.getTaskName();

            if (taskName == null) {
                /* Note : Since this is a helper class which can
                 * be instantiated by the component, JBI resource
                 * bundle cannot be used. Hence strings are hard coded.
                */
                String errMsg =
                        "JBIMA0451: BuildManagementMessage : Task name cannot be null ";
                throw new Exception(errMsg);
            }

            String taskResult = mmObj.getTaskResult();

            if (taskResult == null) {
                /* Note : Since this is a helper class which can
                 * be instantiated by the component, JBI resource
                 * bundle cannot be used. Hence strings are hard coded.
                */
                String errMsg =
                        "JBIMA0456: BuildManagementMessage : Task result cannot be null ";
                throw new Exception(errMsg);
            }

            String messageType = mmObj.getStatusMessageType();

            if (messageType != null) {
                locToken = mmObj.getLocToken(1);

                if (locToken == null) {
                    locToken = MISSING_DATA_STRING;
                }

                locMessage = mmObj.getLocMessage(1);

                if (locMessage == null) {
                    locMessage = MISSING_DATA_STRING;
                }

                locParam = (String[]) mmObj.getLocParam(1);
            }

            Element[] msgLocInfoElem = new Element[MAX_NESTING_LEVEL];
            Element[] locTokenElem = new Element[MAX_NESTING_LEVEL];
            Element[] locMessageElem = new Element[MAX_NESTING_LEVEL];
            Element[] taskStatMsgElem = new Element[MAX_NESTING_LEVEL];
            taskRsltDtlsElem = doc.createElement("task-result-details");

            Element taskIdElem = doc.createElement("task-id");
            Element taskRsltStatElem = doc.createElement("task-result");
            Element msgTypeElem = doc.createElement("message-type");
            taskStatMsgElem[0] = doc.createElement("task-status-msg");
            msgLocInfoElem[0] = doc.createElement("msg-loc-info");
            locTokenElem[0] = doc.createElement("loc-token");
            locMessageElem[0] = doc.createElement("loc-message");

            DOMUtil.UTIL.setTextData(taskIdElem, taskName);
            DOMUtil.UTIL.setTextData(taskRsltStatElem, taskResult);
            taskRsltDtlsElem.appendChild(taskIdElem);
            taskRsltDtlsElem.appendChild(taskRsltStatElem);
            int j = 0;

            if (messageType != null) {
                DOMUtil.UTIL.setTextData(msgTypeElem, messageType);

                while ((locToken != null) && (locMessage != null)) {
                    DOMUtil.UTIL.setTextData(locTokenElem[j], locToken);
                    DOMUtil.UTIL.setTextData(locMessageElem[j], locMessage);
                    taskRsltDtlsElem.appendChild(msgTypeElem);
                    msgLocInfoElem[j].appendChild(locTokenElem[j]);
                    msgLocInfoElem[j].appendChild(locMessageElem[j]);

                    if (locParam != null) {
                        Element[] locParamElem = new Element[MAX_NESTING_LEVEL];

                        for (int k = 0; k < locParam.length; k++) {
                            locParamElem[k] = doc.createElement("loc-param");
                            DOMUtil.UTIL
                                    .setTextData(locParamElem[k], locParam[k]);
                            msgLocInfoElem[j].appendChild(locParamElem[k]);
                        }
                    }

                    taskStatMsgElem[j].appendChild(msgLocInfoElem[j]);
                    taskRsltDtlsElem.appendChild(taskStatMsgElem[j]);

                    j++;
                    locToken = mmObj.getLocToken(j + 1);
                    locMessage = mmObj.getLocMessage(j + 1);
                    locParam = mmObj.getLocParam(j + 1);
                    taskStatMsgElem[j] = doc.createElement("task-status-msg");
                    msgLocInfoElem[j] = doc.createElement("msg-loc-info");
                    locTokenElem[j] = doc.createElement("loc-token");
                    locMessageElem[j] = doc.createElement("loc-message");
                }

            }
        }
        catch (Exception e) {
            e.printStackTrace();
            throw e;
        }

        return taskRsltDtlsElem;
    }

    /**
     * DOCUMENT ME!
     *
     * @param doc   DOCUMENT ME!
     * @param mmObj DOCUMENT ME!
     * @return DOCUMENT ME!
     * @throws Exception DOCUMENT ME!
     */
    public Element buildExceptionXMLFragment(
            Document doc,
            ComponentMessageHolder mmObj
    ) throws Exception {
        String cmpStatMsg = null;
        String locToken = null;
        String locMessage = null;
        String[] locParam = null;
        Throwable exObj = null;
        Element taskRsltDtlsElem = null;

        try {
            String taskName = mmObj.getTaskName();

            if (taskName == null) {
                /* Note : Since this is a helper class which can
                 * be instantiated by the component, JBI resource
                 * bundle cannot be used. Hence strings are hard coded.
                */
                String errMsg =
                        "JBIMA0452: BuildManagementMessage : Task name cannot be null ";
                throw new Exception(errMsg);
            }

            String taskResult = mmObj.getTaskResult();

            if (taskResult == null) {
                /* Note : Since this is a helper class which can
                 * be instantiated by the component, JBI resource
                 * bundle cannot be used. Hence strings are hard coded.
                 *
                */
                String errMsg =
                        "JBIMA0453: BuildManagementMessage : Task result cannot be null ";
                throw new Exception(errMsg);
            }

            String messageType = mmObj.getExceptionMessageType();

            locToken = mmObj.getLocToken(1);

            if (locToken == null) {
                locToken = MISSING_DATA_STRING;
            }

            locMessage = mmObj.getLocMessage(1);

            locParam = mmObj.getLocParam(1);

            exObj = mmObj.getExceptionObject();

            taskRsltDtlsElem = doc.createElement("task-result-details");

            Element taskIdElem = doc.createElement("task-id");
            Element taskRsltStatElem = doc.createElement("task-result");
            Element msgTypeElem = doc.createElement("message-type");
            DOMUtil.UTIL.setTextData(taskIdElem, taskName);
            DOMUtil.UTIL.setTextData(taskRsltStatElem, taskResult);

            if (messageType != null) {
                DOMUtil.UTIL.setTextData(msgTypeElem, messageType);
                taskRsltDtlsElem.appendChild(msgTypeElem);
            }

            taskRsltDtlsElem.appendChild(taskIdElem);
            taskRsltDtlsElem.appendChild(taskRsltStatElem);

            if (messageType != null) {
                DOMUtil.UTIL.setTextData(msgTypeElem, messageType);
                taskRsltDtlsElem.appendChild(msgTypeElem);
            }

            if (exObj == null) {
                Element exInfoElem = doc.createElement("exception-info");
                Element msgLocInfoElem = doc.createElement("msg-loc-info");
                Element locTokenElem = doc.createElement("loc-token");
                Element locMessageElem = doc.createElement("loc-message");
                Element nestingLevelElem = doc.createElement("nesting-level");
                Element stckTraceElem = doc.createElement("stack-trace");

                DOMUtil.UTIL.setTextData(locTokenElem, locToken);

                if (locMessage != null) {
                    DOMUtil.UTIL.setTextData(locMessageElem, locMessage);
                } else {
                    DOMUtil.UTIL
                            .setTextData(locMessageElem, MISSING_DATA_STRING);
                }

                DOMUtil.UTIL.setTextData(nestingLevelElem, MISSING_DATA_STRING);
                DOMUtil.UTIL.setTextData(stckTraceElem, MISSING_DATA_STRING);

                msgLocInfoElem.appendChild(locTokenElem);
                msgLocInfoElem.appendChild(locMessageElem);

                if (locParam != null) {
                    Element[] locParamElem = new Element[MAX_NESTING_LEVEL];

                    for (int i = 0; i < locParam.length; i++) {
                        locParamElem[i] = doc.createElement("loc-param");
                        DOMUtil.UTIL.setTextData(locParamElem[i], locParam[i]);
                        msgLocInfoElem.appendChild(locParamElem[i]);
                    }
                }

                exInfoElem.appendChild(nestingLevelElem);
                exInfoElem.appendChild(msgLocInfoElem);
                exInfoElem.appendChild(stckTraceElem);
                taskRsltDtlsElem.appendChild(exInfoElem);
            } else {
                int nestingLevel = 1;
                Element[] exInfoElem = new Element[MAX_NESTING_LEVEL];
                Element[] msgLocInfoElem = new Element[MAX_NESTING_LEVEL];
                Element[] locTokenElem = new Element[MAX_NESTING_LEVEL];
                Element[] locMessageElem = new Element[MAX_NESTING_LEVEL];
                Element[] nestingLevelElem = new Element[MAX_NESTING_LEVEL];
                Element[] stckTraceElem = new Element[MAX_NESTING_LEVEL];

                while (exObj != null) {
                    exInfoElem[nestingLevel] =
                            doc.createElement("exception-info");
                    msgLocInfoElem[nestingLevel] =
                            doc.createElement("msg-loc-info");
                    locTokenElem[nestingLevel] = doc.createElement("loc-token");
                    locMessageElem[nestingLevel] =
                            doc.createElement("loc-message");
                    nestingLevelElem[nestingLevel] =
                            doc.createElement("nesting-level");
                    stckTraceElem[nestingLevel] =
                            doc.createElement("stack-trace");

                    StackTraceElement[] stckTrElem = exObj.getStackTrace();
                    StringBuffer sb = new StringBuffer("");

                    if (stckTrElem != null) {
                        for (int i = 0; i < stckTrElem.length; i++) {
                            String stckTrace = stckTrElem[i].toString();
                            sb.append(stckTrace);
                            sb.append("\n");
                        }
                    }

                    if (nestingLevel == 1) {
                        DOMUtil.UTIL
                                .setTextData(locTokenElem[nestingLevel],
                                        locToken);

                        if (locMessage == null) {
                            DOMUtil.UTIL.setTextData(
                                    locMessageElem[nestingLevel],
                                    exObj.getMessage()
                            );
                        } else {
                            DOMUtil.UTIL.setTextData(
                                    locMessageElem[nestingLevel],
                                    locMessage
                            );
                        }
                    } else {
                        String errMsgWithToken = exObj.getMessage();
                        String token = errMsgWithToken.substring(0, 9);
                        String errMsg = null;

                        if (token.startsWith("JBI")) {
                            errMsg = errMsgWithToken.substring(11);
                        } else {
                            token = MISSING_DATA_STRING;
                            errMsg = errMsgWithToken;
                        }

                        DOMUtil.UTIL
                                .setTextData(locTokenElem[nestingLevel], token);
                        DOMUtil.UTIL
                                .setTextData(locMessageElem[nestingLevel],
                                        errMsg);

                    }

                    DOMUtil.UTIL.setTextData(
                            nestingLevelElem[nestingLevel],
                            Integer.toString(nestingLevel)
                    );
                    DOMUtil.UTIL
                            .setTextData(stckTraceElem[nestingLevel],
                                    sb.toString());

                    msgLocInfoElem[nestingLevel].appendChild(locTokenElem[nestingLevel]);
                    msgLocInfoElem[nestingLevel].appendChild(
                            locMessageElem[nestingLevel]
                    );

                    if (nestingLevel == 1) {
                        if (locParam != null) {
                            Element[] locParamElem =
                                    new Element[MAX_NESTING_LEVEL];

                            for (int i = 0; i < locParam.length; i++) {
                                locParamElem[i] =
                                        doc.createElement("loc-param");
                                DOMUtil.UTIL
                                        .setTextData(locParamElem[i],
                                                locParam[i]);
                                msgLocInfoElem[nestingLevel].appendChild(
                                        locParamElem[i]);
                            }
                        }
                    } else {
                        Element locParamElement =
                                doc.createElement("loc-param");
                        DOMUtil.UTIL
                                .setTextData(locParamElement,
                                        MISSING_DATA_STRING);
                        msgLocInfoElem[nestingLevel].appendChild(locParamElement);
                    }

                    exInfoElem[nestingLevel].appendChild(nestingLevelElem[nestingLevel]);
                    exInfoElem[nestingLevel].appendChild(msgLocInfoElem[nestingLevel]);
                    exInfoElem[nestingLevel].appendChild(stckTraceElem[nestingLevel]);
                    taskRsltDtlsElem.appendChild(exInfoElem[nestingLevel]);

                    nestingLevel++;
                    exObj = exObj.getCause();
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            throw e;
        }

        return taskRsltDtlsElem;
    }

    /**
     * Constructs an XML string of the component exception.
     *
     * @param mmObj HashMap containing component exception information
     * @return XML string representing component exception
     * @throws Exception If fails to build component exception message
     */
    public String buildComponentTaskExceptionMessage(ComponentMessageHolder mmObj)
            throws Exception {
        String cmpStatMsg = null;
        Document doc = null;

        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            doc = db.newDocument();

            Element elem = doc.createElementNS(
                    "http://java.sun.com/xml/ns/jbi/management-message",
                    "component-task-result");
            Element compNameElem = doc.createElement("component-name");
            Element compTaskRsltDtlsElem =
                    doc.createElement("component-task-result-details");

            String componentName = mmObj.getComponentName();

            if (componentName == null) {
                String errMsg =
                        "JBIMA0457: BuildManagementMessage : Component name cannot be null ";
                throw new Exception(errMsg);
            }

            DOMUtil.UTIL.setTextData(compNameElem, componentName);

            Element taskRsltDtlsElem = buildExceptionXMLFragment(doc, mmObj);

            compTaskRsltDtlsElem.appendChild(taskRsltDtlsElem);
            elem.appendChild(compNameElem);
            elem.appendChild(compTaskRsltDtlsElem);
            doc.appendChild(elem);

            StringWriter sw = new StringWriter();
            cmpStatMsg = DOMUtil.UTIL.DOM2String(doc, sw);
        }
        catch (Exception e) {
            throw e;
        }

        return cmpStatMsg;
    }

}
