/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2004 Bull S.A.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.deployment.ws;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import org.ow2.jonas.deployment.common.AbsDeploymentDesc;
import org.ow2.jonas.deployment.ws.xml.JonasWebserviceDescription;
import org.ow2.jonas.deployment.ws.xml.JonasWebservices;
import org.ow2.jonas.deployment.ws.xml.WebserviceDescription;
import org.ow2.jonas.deployment.ws.xml.Webservices;
import org.ow2.jonas.lib.util.I18n;




import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

/**
 * This Classes is a data structure that encapsulate informations contained in
 * <code>webservices.xml</code> deployment descriptor. It provides methods to
 * manipulate these informations.
 * @author Guillaume Sauthier
 * @author Xavier Delplanque
 * @author Helene Joanin
 */
public class WSDeploymentDesc extends AbsDeploymentDesc {

    /** The list of ServiceDesc described in webservices.xml */
    private Vector services = new Vector();

    /** The Webservices display name */
    private String displayName;

    /** The WebApp dispatching SOAP request to the PortComponents */
    private String warFile = null;

    /** The Webservices logger */
    private Logger logger;

    /**
     * Internationalization
     */
    private static I18n i18n = I18n.getInstance(WSDeploymentDesc.class);

    /**
     * ContextRoot for this group of WebServices
     */
    private String contextRoot = null;

    /**
     * Constructor : creates a WSDeploymentDesc object
     * @param jarCL module (war or ejbjar) class loader.
     * @param log the logger to use.
     * @param ws Zeus object containing webservices informations
     * @param jws Zeus object containing jonas-webservices informations
     * @throws WSDeploymentDescException if in the Webservices file : - each
     *         service haven't got an unique name. - each port component haven't
     *         got an unique name. - each handler haven't got an unique name. -
     *         wsdl ports aren't inclued in portComponents.
     */
    public WSDeploymentDesc(ClassLoader jarCL, Logger log, Webservices ws, JonasWebservices jws)
            throws WSDeploymentDescException {
        this.logger = log;

        // set displayName
        displayName = ws.getDisplayName();

        // Store the WebApp filename
        if (jws != null) {
            if (jws.getWar() != null) {
                warFile = jws.getWar();
            }
            // set contextRoot
            String ctx = jws.getContextRoot();
            if (ctx != null && !"".equals(ctx)) {
                contextRoot = ctx;
            }

        }

        // set services
        List wdl = ws.getWebserviceDescriptionList();
        Map links = associateWDAndJWD(ws, jws);

        for (int i = 0; i < wdl.size(); i++) {
            WebserviceDescription wd = (WebserviceDescription) wdl.get(i);
            JonasWebserviceDescription jwd = (JonasWebserviceDescription) links.get(wd.getWebserviceDescriptionName());
            services.add(new ServiceDesc(jarCL, wd, jwd));
        }

        // validation
        // services names are unique
        // fill the list of service names
        Vector serviceNames = new Vector();

        for (int i = 0; i < services.size(); i++) {
            String sn = ((ServiceDesc) services.get(i)).getName();

            if (serviceNames.contains(sn)) {
                // if the service name is already contained, it isn't unique
                String err = getI18n().getMessage("WSDeploymentDesc.serviceNameNotUnique", sn); //$NON-NLS-1$
                logger.log(BasicLevel.ERROR, err);
                throw new WSDeploymentDescException(err);
            }

            serviceNames.add(sn);
        }

        // port components names are unique
        // fill the list of port component names
        Vector pcNames = new Vector();

        for (int i = 0; i < services.size(); i++) {
            ServiceDesc s = (ServiceDesc) services.get(i);
            List pcl = s.getPortComponents();

            for (int j = 0; j < pcl.size(); j++) {
                PortComponentDesc pcd = (PortComponentDesc) pcl.get(j);
                String pcn = pcd.getName();

                if (pcNames.contains(pcn)) {
                    // if the port component name is already contained, it isn't
                    // unique
                    String err = getI18n().getMessage("WSDeploymentDesc.portCompNameNotUnique", pcn); //$NON-NLS-1$
                    logger.log(BasicLevel.ERROR, err);
                    throw new WSDeploymentDescException(err);
                }

                pcNames.add(pcn);
            }
        }

        // verify that all wsdl ports are included inside portComponents
        // build the list of port component QNames
        Vector pcQNames = new Vector();

        for (int i = 0; i < services.size(); i++) {
            List pcl = ((ServiceDesc) services.get(i)).getPortComponents();

            for (int j = 0; j < pcl.size(); j++) {
                PortComponentDesc pc = (PortComponentDesc) pcl.get(j);
                pcQNames.add(pc.getQName());
            }
        }

        // verify for each WSDLs that port are inside pcQNames
        for (int i = 0; i < services.size(); i++) {
            WSDLFile wsdlf = ((ServiceDesc) services.get(i)).getWSDL();

            if (!wsdlf.hasPortsIncludedIn(pcQNames)) {
                String err = getI18n().getMessage("WSDeploymentDesc.wsdlDeclareUnknownPort", wsdlf.getName()); //$NON-NLS-1$
                logger.log(BasicLevel.ERROR, err);
                throw new WSDeploymentDescException(err);
            }
        }
    }

    /**
     * Associate WebserviceDescription.name to JonasWebserviceDescription
     * @param jws JonasWebservices instance
     * @param ws Webservices instance
     * @return Returns the JonasWebserviceDescription with the given name or null if not found.
     */
    private Map associateWDAndJWD(Webservices ws, JonasWebservices jws) {
        Map res = new HashMap();
        // for each wsd
        for (Iterator i = ws.getWebserviceDescriptionList().iterator(); i.hasNext();) {
            WebserviceDescription wsd = (WebserviceDescription) i.next();
            res.put(wsd.getWebserviceDescriptionName(), null);
        }
        // jonas-webservices.xml
        if (jws != null) {

            // get all wsd.name
            Set keys = res.keySet();

            // for each jwsd
            for (Iterator i = jws.getJonasWebserviceDescriptionList().iterator(); i.hasNext();) {
                JonasWebserviceDescription jwsd = (JonasWebserviceDescription) i.next();
                String wsdName = jwsd.getWebserviceDescriptionName();

                if (keys.contains(wsdName)) {
                    // jonas-webservice-description linked to webservice-description
                    res.put(wsdName, jwsd);
                } else {
                    String err = "jonas-webservice-description '" + wsdName + "' is not linked to any webservice-description. It will be ignored."; //getI18n().getMessage("WSDeploymentDesc.wsdlDeclareUnknownPort", wsdlf.getName());
                    logger.log(BasicLevel.WARN, err);
                }
            }
        }
        return res;
    }

    /**
     * Return the list of ServiceDesc.
     * @return the list of ServiceDesc.
     */
    public List getServiceDescs() {
        return services;
    }

    /**
     * Return the Webservices displayName.
     * @return the Webservices displayName.
     */
    public String getDisplayName() {
        return displayName;
    }

    /**
     * @return Returns the contextRoot.
     */
    public String getContextRoot() {
        return contextRoot;
    }

    /**
     * Return a String representation of the WSDeploymentDesc.
     * @return a String representation of the WSDeploymentDesc.
     */
    public String toString() {
        StringBuffer sb = new StringBuffer();

        sb.append("\nWSDeploymentDesc :"); //$NON-NLS-1$
        sb.append("\ngetDisplayName()=" + getDisplayName()); //$NON-NLS-1$

        for (Iterator i = getServiceDescs().iterator(); i.hasNext();) {
            sb.append("\ngetServiceDesc()=" + ((ServiceDesc) i.next()).toString()); //$NON-NLS-1$
        }

        sb.append("\ngetWarFile()=" + getWarFile()); //$NON-NLS-1$

        return sb.toString();
    }

    /**
     * Return the filename of the WebApp dispatching SOAP requests to the
     * components (can be null if no filename specified in
     * jonas-webservices.xml).
     * @return the filename of the WebApp dispatching SOAP requests to the
     *         components.
     */
    public String getWarFile() {
        return warFile;
    }

    /**
     * @return Returns the i18n.
     */
    protected static I18n getI18n() {
        return i18n;
    }
}