/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 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
 *
 * Initial developer(s): Xavier Delplanque
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.generators.wsgen.generator.axis;

import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.wsdl.BindingFault;
import javax.wsdl.BindingOperation;
import javax.wsdl.Definition;
import javax.wsdl.Port;
import javax.wsdl.Service;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.extensions.soap.SOAPBinding;
import javax.wsdl.extensions.soap.SOAPBody;
import javax.wsdl.extensions.soap.SOAPFault;
import javax.wsdl.extensions.soap.SOAPOperation;
import javax.xml.namespace.QName;

import org.ow2.jonas.deployment.common.HandlerDesc;
import org.ow2.jonas.deployment.ejb.SessionStatelessDesc;
import org.ow2.jonas.deployment.ws.PortComponentDesc;
import org.ow2.jonas.deployment.ws.SSBPortComponentDesc;




/**
 * Member of a VelocityContext. Contains information about a
 * PortComponent(Desc/Ref).
 * @author Xavier Delplanque
 */
public class VcPortComponent {

    /** port component name */
    private String name;

    /** the sib if the endpoint is an ejb */
    private VcBean bean = null;

    /** a string containing sei method names separated by ',' */
    private static final String SEP = ",";

    /** a list of method names */
    private String methods = null;

    /** port component handlers */
    private Vector handlers = new Vector();

    /** the JAXRPC class name if the sib is a servlet */
    private String jaxRpcClassName;

    /** the wsdl file to display when ?wsdl is used */
    private String wsdlFilename;

    /**
     * Document style name
     */
    private static final String DOCUMENT_STYLE = "document";

    /**
     * SOAP style
     */
    private String style = null;

    /**
     * SOAP use
     */
    private String use = null;

    /**
     * target Namespace
     */
    private String namespace = null;

    /**
     * SOAP NS URI
     */
    public static final String NS_URI_SOAP = "http://schemas.xmlsoap.org/wsdl/soap/";

    /**
     * soap:binding
     */
    private static final QName SOAP_BINDING_QNAME = new QName(NS_URI_SOAP, "binding");

    /**
     * soap:operation
     */
    private static final QName SOAP_OPERATION_QNAME = new QName(NS_URI_SOAP, "operation");

    /**
     * soap:body
     */
    private static final QName SOAP_BODY_QNAME = new QName(NS_URI_SOAP, "body");

    /**
     * soap:fault
     */
    private static final QName SOAP_FAULT_QNAME = new QName(NS_URI_SOAP, "fault");

    /**
     * Construct a VcPortComponent from a PortComponentDesc. used for
     * server-side configuration files generation. (server-config.wsdd)
     * @param pcd PortComponentDesc to be used
     * @param wsdl wsdl filename of the port
     */
    public VcPortComponent(PortComponentDesc pcd, String wsdl) {

        // set name
        name = pcd.getServiceName();

        wsdlFilename = wsdl;

        // set bean or jaxRpcClassName
        if (pcd.hasBeanImpl()) {
            // set bean
            SessionStatelessDesc ssb = ((SSBPortComponentDesc) pcd).getSessionStatelessDesc();
            bean = new VcBean(ssb);
        } else {
            jaxRpcClassName = pcd.getSIBClassname();
        }
        // set methods
        Method[] m = pcd.getServiceEndpointInterface().getMethods();
        for (int i = 0; i < m.length; i++) {
            if (methods != null) {
                methods += SEP;
            } else {
                methods = "";
            }
            methods += m[i].getName();
        }

        // set style/use/namespace
        Definition def = pcd.getServiceDesc().getWSDL().getDefinition();
        boolean portFound = false;
        for (Iterator i = def.getServices().keySet().iterator(); i.hasNext() && !portFound;) {
            Service s = def.getService((QName) i.next());
            Port p = s.getPort(pcd.getQName().getLocalPart());
            if (p != null) {
                // we found the linked port
                portFound = true;

                // search style attribute
                style = getStyle(p);

                // search use attribute inside port
                use = getUse(p);
            }
        }
        namespace = def.getTargetNamespace();

        // set handlers
        List hs = pcd.getHandlers();
        for (Iterator itH = hs.iterator(); itH.hasNext();) {
            HandlerDesc hd = (HandlerDesc) itH.next();
            handlers.add(new VcHandler(hd));
        }

    }

    /**
     * @param p the wsdl port
     * @return Returns the <code>use</code> to use with the port
     */
    private static String getUse(Port p) {
        String use = null;

        // use is declared inside
        // - wsdl:definition/wsdl:binding/wsdl:operation/wsdl:input/soap:body@use
        // - wsdl:definition/wsdl:binding/wsdl:operation/wsdl:output/soap:body@use
        // - wsdl:definition/wsdl:binding/wsdl:operation/wsdl:fault/soap:fault@use
        // take the first one.
        List ops = p.getBinding().getBindingOperations();
        for (Iterator j = ops.iterator(); j.hasNext() && (use == null);) {
            BindingOperation bop = (BindingOperation) j.next();

            // search input
            List inputExt = bop.getBindingInput().getExtensibilityElements();
            for (Iterator k = inputExt.iterator(); k.hasNext() && (use == null);) {
                ExtensibilityElement ext = (ExtensibilityElement) k.next();
                if (ext.getElementType().equals(SOAP_BODY_QNAME)) {
                    SOAPBody sb = (SOAPBody) ext;
                    use = sb.getUse();
                }
            }

            // search output
            List outputExt = bop.getBindingOutput().getExtensibilityElements();
            for (Iterator k = outputExt.iterator(); k.hasNext() && (use == null);) {
                ExtensibilityElement ext = (ExtensibilityElement) k.next();
                if (ext.getElementType().equals(SOAP_BODY_QNAME)) {
                    SOAPBody sb = (SOAPBody) ext;
                    use = sb.getUse();
                }
            }

            // search faults
            for (Iterator k = bop.getBindingFaults().keySet().iterator(); k.hasNext() && (use == null);) {
                BindingFault bf = bop.getBindingFault((String) k.next());

                // search soap:fault
                List faultExt = bf.getExtensibilityElements();
                for (Iterator i = faultExt.iterator(); i.hasNext() && (use == null);) {
                    ExtensibilityElement ext = (ExtensibilityElement) i.next();
                    if (ext.getElementType().equals(SOAP_FAULT_QNAME)) {
                        SOAPFault sf = (SOAPFault) ext;
                        use = sf.getUse();
                    }
                }
            }
        }
        // use cannot be null as it is required !

        return use;
    }

    /**
     * @param p the wsdl port
     * @return Returns the <code>style</code> to use with the port
     */
    private static String getStyle(Port p) {
        String style = null;

        // we need to explore soap:operation and get the first that set a style attribute
        List bindingOps = p.getBinding().getBindingOperations();
        for (Iterator i = bindingOps.iterator(); i.hasNext() && (style == null);) {

            BindingOperation bop = (BindingOperation) i.next();
            // browse soap:operation
            List extElements = bop.getExtensibilityElements();
            for (Iterator j = extElements.iterator(); j.hasNext();) {
                ExtensibilityElement ext = (ExtensibilityElement) j.next();
                if (ext.getElementType().equals(SOAP_OPERATION_QNAME)) {

                    // Got a soap:operation element
                    SOAPOperation sop = (SOAPOperation) ext;
                    style = sop.getStyle();
                }
            }
        }

        // browse extensibility elements of port's binding
        List soapElements = p.getBinding().getExtensibilityElements();
        for (Iterator i = soapElements.iterator(); i.hasNext() && (style == null);) {

            ExtensibilityElement ext = (ExtensibilityElement) i.next();
            if (ext.getElementType().equals(SOAP_BINDING_QNAME)) {

                // Got a soap:binding element
                SOAPBinding soapb = (SOAPBinding) ext;
                style = soapb.getStyle();
            }

        }

        if (style == null) {
            style = DOCUMENT_STYLE;
        }

        return style;
    }

    /**
     * Construct a VcPortComponent from a PortComponentRef. used for client-side
     * configuration files generation. (client-config.wsdd)
     * @param name Port Ref name
     * @param hrs HandlerRef list
     */
    public VcPortComponent(String name, List hrs) {

        // set name
        this.name = name;

        // set handlers
        for (Iterator itH = hrs.iterator(); itH.hasNext();) {
            HandlerDesc hr = (HandlerDesc) itH.next();
            handlers.add(new VcHandler(hr));
        }

    }

    /**
     * @return Returns the PortComponent name
     */
    public String getName() {
        return name;
    }

    /**
     * @return Returns the VcBean of this PortComponent
     */
    public VcBean getBean() {
        return bean;
    }

    /**
     * @return Returns the WSDL filename
     */
    public String getWSDLFilename() {
        return wsdlFilename;
    }

    /**
     * @return returns the exposed methods as a comma separated list
     */
    public String getMethods() {
        return methods;
    }

    /**
     * @return Returns a list of Handler/HandlerRef
     */
    public Vector getHandlers() {
        return handlers;
    }

    /**
     * @return Returns the JaxRpc implementation classname
     */
    public String getJaxRpcClassName() {
        return jaxRpcClassName;
    }

    /**
     * @return Returns the style.
     */
    public String getStyle() {
        return style;
    }

    /**
     * @return Returns the use.
     */
    public String getUse() {
        return use;
    }

    /**
     * @return Returns the namespace.
     */
    public String getNamespace() {
        return namespace;
    }
}