/*
 * 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.axis2.description;

import com.ibm.wsdl.util.xml.DOM2Writer;
import org.apache.axiom.soap.SOAP11Constants;
import org.apache.axiom.soap.SOAP12Constants;
import org.apache.axis2.AxisFault;
import org.apache.axis2.namespace.Constants;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.axis2.wsdl.HTTPHeaderMessage;
import org.apache.axis2.wsdl.SOAPHeaderMessage;
import org.apache.axis2.wsdl.SOAPModuleMessage;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.axis2.wsdl.WSDLUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.woden.WSDLException;
import org.apache.woden.WSDLFactory;
import org.apache.woden.WSDLReader;
import org.apache.woden.WSDLSource;
import org.apache.woden.XMLElement;
import org.apache.woden.resolver.URIResolver;
import org.apache.woden.schema.Schema;
import org.apache.woden.types.NamespaceDeclaration;
import org.apache.woden.types.QNameTokenUnion;
import org.apache.woden.wsdl20.Binding;
import org.apache.woden.wsdl20.BindingFault;
import org.apache.woden.wsdl20.BindingFaultReference;
import org.apache.woden.wsdl20.BindingMessageReference;
import org.apache.woden.wsdl20.BindingOperation;
import org.apache.woden.wsdl20.Description;
import org.apache.woden.wsdl20.ElementDeclaration;
import org.apache.woden.wsdl20.Endpoint;
import org.apache.woden.wsdl20.Interface;
import org.apache.woden.wsdl20.InterfaceFault;
import org.apache.woden.wsdl20.InterfaceFaultReference;
import org.apache.woden.wsdl20.InterfaceMessageReference;
import org.apache.woden.wsdl20.InterfaceOperation;
import org.apache.woden.wsdl20.Service;
import org.apache.woden.wsdl20.enumeration.MessageLabel;
import org.apache.woden.wsdl20.extensions.InterfaceOperationExtensions;
import org.apache.woden.wsdl20.extensions.http.HTTPBindingExtensions;
import org.apache.woden.wsdl20.extensions.http.HTTPBindingFaultExtensions;
import org.apache.woden.wsdl20.extensions.http.HTTPBindingMessageReferenceExtensions;
import org.apache.woden.wsdl20.extensions.http.HTTPBindingOperationExtensions;
import org.apache.woden.wsdl20.extensions.http.HTTPEndpointExtensions;
import org.apache.woden.wsdl20.extensions.http.HTTPHeader;
import org.apache.woden.wsdl20.extensions.http.HTTPHeaderElement;
import org.apache.woden.wsdl20.extensions.http.HTTPLocation;
import org.apache.woden.wsdl20.extensions.rpc.Argument;
import org.apache.woden.wsdl20.extensions.rpc.RPCInterfaceOperationExtensions;
import org.apache.woden.wsdl20.extensions.soap.SOAPBindingExtensions;
import org.apache.woden.wsdl20.extensions.soap.SOAPBindingFaultExtensions;
import org.apache.woden.wsdl20.extensions.soap.SOAPBindingFaultReferenceExtensions;
import org.apache.woden.wsdl20.extensions.soap.SOAPBindingMessageReferenceExtensions;
import org.apache.woden.wsdl20.extensions.soap.SOAPBindingOperationExtensions;
import org.apache.woden.wsdl20.extensions.soap.SOAPEndpointExtensions;
import org.apache.woden.wsdl20.extensions.soap.SOAPHeaderBlock;
import org.apache.woden.wsdl20.extensions.soap.SOAPModule;
import org.apache.woden.wsdl20.xml.BindingFaultElement;
import org.apache.woden.wsdl20.xml.BindingOperationElement;
import org.apache.woden.wsdl20.xml.DescriptionElement;
import org.apache.woden.wsdl20.xml.DocumentableElement;
import org.apache.woden.wsdl20.xml.DocumentationElement;
import org.apache.woden.wsdl20.xml.ImportElement;
import org.apache.woden.wsdl20.xml.InterfaceFaultElement;
import org.apache.woden.wsdl20.xml.InterfaceMessageReferenceElement;
import org.apache.woden.wsdl20.xml.TypesElement;
import org.apache.woden.xml.XMLAttr;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.utils.NamespaceMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class WSDL20ToAxisServiceBuilder extends WSDLToAxisServiceBuilder {

    protected static final Log log = LogFactory.getLog(WSDL20ToAxisServiceBuilder.class);
    protected Description description;

    private String wsdlURI;

    protected String interfaceName;

    private String savedTargetNamespace;

    private NamespaceDeclaration[] namespacemap;

    private List<QName> operationNames = new ArrayList<QName>();

    private NamespaceMap stringBasedNamespaceMap;

    private boolean setupComplete = false;

    private Service wsdlService;

    private boolean isAllPorts;

    private URIResolver customWSDLResolver;

//    As bindings are processed add it to this array so that we dont process the same binding twice
    private Map processedBindings;


    public WSDL20ToAxisServiceBuilder(InputStream in, QName serviceName,
                                      String interfaceName) {
        this.in = in;
        this.serviceName = serviceName;
        this.interfaceName = interfaceName;
        this.axisService = new AxisService();
        setPolicyRegistryFromService(axisService);
    }

    public WSDL20ToAxisServiceBuilder(String wsdlUri,
                                      String name, String interfaceName) throws WSDLException {
        String fullPath = wsdlUri;
        if (!wsdlUri.startsWith("http://")) {
            File file = new File(wsdlUri);
            fullPath = file.getAbsolutePath();
        }
        setBaseUri(fullPath);
        Description description;
        try {
            description = readInTheWSDLFile(fullPath);
        } catch (AxisFault axisFault) {
            throw new WSDLException("ERROR", "Exception occured while reading WSDL 2.0 doc", axisFault);
        }

        DescriptionElement descriptionElement = description.toElement();
        savedTargetNamespace = descriptionElement.getTargetNamespace()
                .toString();
        namespacemap = descriptionElement.getDeclaredNamespaces();
        this.description = description;
        this.serviceName = null;
        if (name != null) {
            serviceName = new QName(descriptionElement.getTargetNamespace().toString(), name);
        }
        this.interfaceName = interfaceName;
        this.axisService = new AxisService();
        setPolicyRegistryFromService(axisService);
    }

    public WSDL20ToAxisServiceBuilder(String wsdlUri,
                                      String name, String interfaceName, boolean isAllPorts) throws WSDLException {
        this(wsdlUri, name, interfaceName);
        this.isAllPorts = isAllPorts;
    }

    public WSDL20ToAxisServiceBuilder(String wsdlUri, QName serviceName) {
        super(null, serviceName);
        this.wsdlURI = wsdlUri;
    }

    public WSDL20ToAxisServiceBuilder(String wsdlUri, AxisService service) {
        super(null, service);
        this.wsdlURI = wsdlUri;
    }

    public WSDL20ToAxisServiceBuilder(Description descriptionComp, QName wsdlServiceName,
            String interfaceName) {
        DescriptionElement descriptionElement = descriptionComp.toElement();
        savedTargetNamespace = descriptionElement.getTargetNamespace().toString();
        namespacemap = descriptionElement.getDeclaredNamespaces();
        this.description = descriptionElement.toComponent();
        this.serviceName = null;
        if (wsdlServiceName != null) {
            serviceName = wsdlServiceName;
        }
        this.interfaceName = interfaceName;
        this.axisService = new AxisService();
        setPolicyRegistryFromService(axisService);
    }

	public boolean isAllPorts() {
        return isAllPorts;
    }

    public void setAllPorts(boolean allPorts) {
        isAllPorts = allPorts;
    }

    /**
     * sets a custom WSDL locator
     *
     * @param customResolver - A custom Resolver that can resolve imports and includes
     */
    public void setCustomWSDLResolver(URIResolver customResolver) {
        this.customWSDLResolver = customResolver;
    }

    public AxisService populateService() throws AxisFault {

        try {
            setup();
            // Setting wsdl4jdefintion to axisService , so if some one want
            // to play with it he can do that by getting the parameter
            Parameter wsdlDescriptionParamter = new Parameter();
            wsdlDescriptionParamter.setName(WSDLConstants.WSDL_20_DESCRIPTION);
            wsdlDescriptionParamter.setValue(description);
            axisService.addParameter(wsdlDescriptionParamter);

            if (isCodegen) {
                axisService.addParameter("isCodegen", Boolean.TRUE);
            }
            if (description == null) {
                return null;
            }
            // setting target name space
            axisService.setTargetNamespace(savedTargetNamespace);

            // if there are documentation elements in the root. Lets add them as the wsdlService description
            // but since there can be multiple documentation elements, lets only add the first one
            addDocumentation(axisService, description.toElement());
//            DocumentationElement[] documentationElements = description.toElement().getDocumentationElements();
//            if (documentationElements != null && documentationElements.length > 0) {
//                axisService.setServiceDescription(documentationElements[0].getContent().toString());
//            }

            // adding ns in the original WSDL
            // processPoliciesInDefintion(wsdl4jDefinition); TODO : Defering policy handling for now - Chinthaka
            // policy support

            // schema generation

            // Create the namespacemap

            axisService.setNamespaceMap(stringBasedNamespaceMap);
            // TypeDefinition[] typeDefinitions =
            // description.getTypeDefinitions();
            // for(int i=0; i<typeDefinitions.length; i++){
            // if("org.apache.ws.commons.schema".equals(typeDefinitions[i].getContentModel())){
            // axisService.addSchema((XmlSchema)typeDefinitions[i].getContent());
            // }else
            // if("org.w3c.dom".equals(typeDefinitions[i].getContentModel())){
            // axisService.addSchema(getXMLSchema((Element)typeDefinitions[i].getContent(),
            // null));
            // }
            //
            // }

            DescriptionElement descriptionElement = description.toElement();
            processTypes(descriptionElement);

            processService();
            return axisService;
        } catch (Exception e) {
            throw AxisFault.makeFault(e);
        }
    }
    
    /**
     * recursively drills down to find all type definitions 
     * (XSD schemas) in all imported WSDLs and XSDs
     * 
     * @param descriptionElement - a description element from where import elements 
     *                             and types can be found
     */
    private void processTypes(DescriptionElement descriptionElement) {
        TypesElement typesElement = descriptionElement
                .getTypesElement();
        if (typesElement != null) {
            Schema[] schemas = typesElement.getSchemas();
            for (int i = 0; i < schemas.length; i++) {
                XmlSchema schemaDefinition = schemas[i].getSchemaDefinition();
                
                // WSDL 2.0 spec requires that even the built-in schema should be returned
                // once asked for schema definitions. But for data binding purposes we can ignore that
                if (schemaDefinition != null && !Constants.URI_2001_SCHEMA_XSD
                        .equals(schemaDefinition.getTargetNamespace())) {
                    axisService.addSchema(schemaDefinition);
                }
            }
        }
        
        ImportElement[] importElements = descriptionElement.getImportElements();
        for (int i = 0; i < importElements.length; i++) {
            DescriptionElement descElem = importElements[i].getDescriptionElement();
            processTypes(descElem); // recursively drill down
        }
    }

    private void processEndpoints(Interface serviceInterface) throws AxisFault {
        Endpoint[] endpoints = wsdlService.getEndpoints();

        if (endpoints.length == 0) {
            throw new AxisFault("No endpoints found in the WSDL");
        }

        processedBindings = new HashMap();
        Endpoint endpoint = null;

        // If the interface name is not null thats means that this is a call from the codegen engine
        // and we need to populate a single endpoint. Hence find the endpoint and populate it.
        // If that was not the case then we need to check whether the call is from the codegen
        // engine with thw allports check false. If its so no need to populate all endpoints, we
        // select an enspoint accrding to the following criteria.
        // 1. Find the first SOAP 1.2 endpoint
        // 2. Find the first SOAP 1.1 endpoint
        // 3. Use the first endpoint
        if (this.interfaceName != null) {
            for (int i = 0; i < endpoints.length; ++i) {
                if (this.interfaceName.equals(endpoints[i].getName().toString())) {
                    endpoint = endpoints[i];
                    break;  // found it.  Stop looking
                }
            }
            if (endpoint == null) {
                throw new AxisFault("No endpoint found for the given name :"
                        + this.interfaceName);
            }

            axisService
                    .addEndpoint(endpoint.getName().toString(),
                                 processEndpoint(endpoint, serviceInterface));
        } else if (this.isCodegen && !this.isAllPorts) {
            Endpoint soap11Endpoint = null;
            for (int i = 0; i < endpoints.length; ++i) {
                Binding binding = endpoints[i].getBinding();
                if (WSDL2Constants.URI_WSDL2_SOAP.equals(binding.getType().toString())) {
                    SOAPBindingExtensions soapBindingExtensions;
                    try {
                        soapBindingExtensions = (SOAPBindingExtensions) binding
                                .getComponentExtensionContext(
                                        new URI(WSDL2Constants.URI_WSDL2_SOAP));
                    } catch (URISyntaxException e) {
                        throw new AxisFault("Soap Binding Extention not found");
                    }
                    if (!WSDL2Constants.SOAP_VERSION_1_1
                            .equals(soapBindingExtensions.getSoapVersion())) {
                        endpoint = endpoints[i];
                        break;  // found it.  Stop looking
                    } else if (soap11Endpoint == null){
                        soap11Endpoint = endpoints[i];
                    }
                }
            }
            if (endpoint == null) {
                endpoint = endpoints[0];
            }

            axisService
                    .addEndpoint(endpoint.getName().toString(),
                                 processEndpoint(endpoint, serviceInterface));
        } else {
            for (int i = 0; i < endpoints.length; i++) {
                axisService
                        .addEndpoint(endpoints[i].getName().toString(),
                                     processEndpoint(endpoints[i], serviceInterface));
            }
        }

        if (endpoint == null && endpoints.length > 0) {
            endpoint = endpoints[0];
        }

        if (endpoint != null) {
            axisService.setEndpointName(endpoint.getName().toString());
            axisService.setBindingName(endpoint.getBinding().getName().getLocalPart());
            axisService.setEndpointURL(endpoint.getAddress().toString());
        }
    }

    private void processService() throws AxisFault {
        Service[] services = description.getServices();
        if (services.length == 0) {
            throw new AxisFault("No wsdlService found in the WSDL");
        }

        if (serviceName != null) {
            for (int i = 0; i < services.length; i++) {
                if (serviceName.equals(services[i].getName())) {
                    wsdlService = services[i];
                    break;  // found it. Stop looking.
                }
            }
            if (wsdlService == null) {
                throw new AxisFault("Service with the specified name not found in the WSDL : "
                        + serviceName.getLocalPart());
            }
        } else {
           wsdlService = services[0];
        }

        axisService.setName(wsdlService.getName().getLocalPart());
        Interface serviceInterface = wsdlService.getInterface();
        axisService.addParameter(new Parameter(WSDL2Constants.INTERFACE_LOCAL_NAME, serviceInterface.getName().getLocalPart()));
        processInterface(serviceInterface);
        if (isCodegen) {
            axisService.setOperationsNameList(operationNames);
        }
        processEndpoints(serviceInterface);

    }

    private AxisEndpoint processEndpoint(Endpoint endpoint, Interface serviceInterface) throws AxisFault {
        AxisEndpoint axisEndpoint = new AxisEndpoint();
        axisEndpoint.setParent(axisService);
        axisEndpoint.setName(endpoint.getName().toString());
        setEndpointURL(axisEndpoint, endpoint.getAddress().toString());
        Binding binding = endpoint.getBinding();
        AxisBinding axisBinding;
        if (processedBindings.containsKey(binding.getName())) {
            axisBinding = (AxisBinding) processedBindings.get(binding.getName());
        } else {
            axisBinding = processBinding(binding, serviceInterface);
        }
        axisEndpoint.setBinding(axisBinding);
        
        String bindingType = binding.getType().toString();
        if (bindingType.equals(WSDL2Constants.URI_WSDL2_SOAP)) {
            processSOAPBindingEndpointExtensions(endpoint, axisEndpoint);
        } else if (bindingType.equals(WSDL2Constants.URI_WSDL2_HTTP)) {
            processHTTPBindingEndpointExtensions(endpoint, axisEndpoint);
        }
        addDocumentation(axisEndpoint, endpoint.toElement());
        return axisEndpoint;
    }

    private void processSOAPBindingEndpointExtensions(Endpoint endpoint, AxisEndpoint axisEndpoint) throws AxisFault {
        SOAPEndpointExtensions soapEndpointExtensions;
        try {
            soapEndpointExtensions = (SOAPEndpointExtensions) endpoint
                    .getComponentExtensionContext(new URI(WSDL2Constants.URI_WSDL2_SOAP));
        } catch (URISyntaxException e) {
            throw new AxisFault("SOAP Binding Endpoint Extension not found");
        }

        if (soapEndpointExtensions != null) {
            axisEndpoint.setProperty(WSDL2Constants.ATTR_WHTTP_AUTHENTICATION_TYPE,
                                     soapEndpointExtensions.getHttpAuthenticationScheme());
            axisEndpoint.setProperty(WSDL2Constants.ATTR_WHTTP_AUTHENTICATION_REALM,
                                     soapEndpointExtensions.getHttpAuthenticationRealm());
        }
    }

    private void processHTTPBindingEndpointExtensions(Endpoint endpoint, AxisEndpoint axisEndpoint) throws AxisFault {
        HTTPEndpointExtensions httpEndpointExtensions;
        try {
            httpEndpointExtensions = (HTTPEndpointExtensions) endpoint
                    .getComponentExtensionContext(new URI(WSDL2Constants.URI_WSDL2_HTTP));
        } catch (URISyntaxException e) {
            throw new AxisFault("HTTP Binding Endpoint Extension not found");
        }

        if (httpEndpointExtensions != null) {
            axisEndpoint.setProperty(WSDL2Constants.ATTR_WHTTP_AUTHENTICATION_TYPE,
                                     httpEndpointExtensions.getHttpAuthenticationScheme());
            axisEndpoint.setProperty(WSDL2Constants.ATTR_WHTTP_AUTHENTICATION_REALM,
                                     httpEndpointExtensions.getHttpAuthenticationRealm());
        }
    }

    /**
     * contains all code which gathers non-wsdlService specific information from the
     * wsdl.
     * <p/>
     * After all the setup completes successfully, the setupComplete field is
     * set so that any subsequent calls to setup() will result in a no-op. Note
     * that subclass WSDL20ToAllAxisServicesBuilder will call populateService
     * for each endpoint in the WSDL. Separating the non-wsdlService specific
     * information here allows WSDL20ToAllAxisServicesBuilder to only do this
     * work 1 time per WSDL, instead of for each endpoint on each wsdlService.
     *
     * @throws AxisFault - Thrown in case the necessary resources are not available in the WSDL
     * @throws WSDLException - Thrown in case Woden throws an exception
     */
    protected void setup() throws AxisFault, WSDLException {
        if (setupComplete) { // already setup, just do nothing and return
            return;
        }
        try {
            if (description == null) {

                Description description;
                DescriptionElement descriptionElement;
                if (wsdlURI != null && !"".equals(wsdlURI)) {
                    description = readInTheWSDLFile(wsdlURI);
                    descriptionElement = description.toElement();
                } else if (in != null) {
                    description = readInTheWSDLFile(in);
                    descriptionElement = description.toElement();
                } else {
                    throw new AxisFault("No resources found to read the wsdl");
                }

                savedTargetNamespace = descriptionElement.getTargetNamespace().toString();
                namespacemap = descriptionElement.getDeclaredNamespaces();
                this.description = description;

            }
            // Create the namespacemap

            stringBasedNamespaceMap = new NamespaceMap();
            for (int i = 0; i < namespacemap.length; i++) {
                NamespaceDeclaration namespaceDeclaration = namespacemap[i];
                stringBasedNamespaceMap.put(namespaceDeclaration.getPrefix(),
                                            namespaceDeclaration.getNamespaceURI().toString());
            }
            
            DescriptionElement descriptionElement = description.toElement();
            createNamespaceMap(descriptionElement);
            
            setupComplete = true;
        } catch (AxisFault e) {
            throw e; // just rethrow AxisFaults
        } catch (WSDLException e) {
            // Preserve the WSDLException
            throw e;
        } catch(Exception e) {
            throw AxisFault.makeFault(e);
        }
    }
    
    /**
     * recursively drills down to get namespace pairs in nested imported elements
     * 
     * @param descriptionElement - a description element from where import elements 
     *                             and types can be found
     */
    private void createNamespaceMap(DescriptionElement descriptionElement) {
        ImportElement[] importElements = descriptionElement.getImportElements();
        for (int i = 0; i < importElements.length; i++) {
            DescriptionElement descElem = importElements[i].getDescriptionElement();
            NamespaceDeclaration[] namespaceDeclarations = descElem.getDeclaredNamespaces();
            for (int j = 0; j < namespaceDeclarations.length; j++) {
                NamespaceDeclaration importedNamespaceDeclaration = namespaceDeclarations[j];
                if (!stringBasedNamespaceMap.containsKey(importedNamespaceDeclaration.getPrefix())) {
                    stringBasedNamespaceMap.put(importedNamespaceDeclaration.getPrefix(),
                                                importedNamespaceDeclaration.getNamespaceURI().toString());
                }
            }
            
            createNamespaceMap(descElem); // recursively drill down
        }
    }

    private AxisBinding processBinding(Binding binding, Interface serviceInterface)
            throws AxisFault {
        AxisBinding axisBinding = new AxisBinding();
        axisBinding.setName(binding.getName());
        String bindingType = binding.getType().toString();
        axisBinding.setType(bindingType);

        if (bindingType.equals(WSDL2Constants.URI_WSDL2_SOAP)) {
            processSOAPBindingExtention(binding, axisBinding, serviceInterface);
        } else if (bindingType.equals(WSDL2Constants.URI_WSDL2_HTTP)) {
            processHTTPBindingExtention(binding, axisBinding, serviceInterface);
        }

        // We should process the interface based on the service not on a binding

        processedBindings.put(binding.getName(), axisBinding);
        addDocumentation(axisBinding, binding.toElement());
        return axisBinding;
    }

    private void processSOAPBindingExtention(Binding binding, AxisBinding axisBinding, Interface serviceInterface)
            throws AxisFault {

        // Capture all the binding specific properties

        // Set a comparator so tha httpLocations are stored in decending order
        Map httpLocationTable = new TreeMap(new Comparator(){
            public int compare(Object o1, Object o2) {
                return (-1 * ((Comparable)o1).compareTo(o2));
            }
        });
        SOAPBindingExtensions soapBindingExtensions;
        try {
            soapBindingExtensions = (SOAPBindingExtensions) binding
                    .getComponentExtensionContext(new URI(WSDL2Constants.URI_WSDL2_SOAP));
        } catch (URISyntaxException e) {
            throw new AxisFault("Soap Binding Extention not found");
        }

        String soapVersion;
        if ((soapVersion = soapBindingExtensions.getSoapVersion()) != null) {
            if (soapVersion.equals(WSDL2Constants.SOAP_VERSION_1_1)) {
                axisBinding.setProperty(WSDL2Constants.ATTR_WSOAP_VERSION,
                                        SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI);
            } else {
                axisBinding.setProperty(WSDL2Constants.ATTR_WSOAP_VERSION,
                                        SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI);
            }
        } else {
            // Set the default to soap 1.2
            axisBinding.setProperty(WSDL2Constants.ATTR_WSOAP_VERSION,
                                    WSDL20DefaultValueHolder.getDefaultValue(
                                            WSDL2Constants.ATTR_WSOAP_VERSION));
        }

        URI soapUnderlyingProtocol = soapBindingExtensions.getSoapUnderlyingProtocol();
        if (soapUnderlyingProtocol != null) {
            axisBinding.setProperty(WSDL2Constants.ATTR_WSOAP_PROTOCOL,
                                    soapUnderlyingProtocol.toString());
        }
        URI soapMepDefault = soapBindingExtensions.getSoapMepDefault();
        if (soapMepDefault != null) {
            axisBinding.setProperty(WSDL2Constants.ATTR_WSOAP_MEP,
                                    soapMepDefault.toString());
        }
        axisBinding.setProperty(WSDL2Constants.ATTR_WHTTP_CONTENT_ENCODING,
                                soapBindingExtensions.getHttpContentEncodingDefault());
        axisBinding.setProperty(WSDL2Constants.ATTR_WSOAP_MODULE,
                                createSoapModules(soapBindingExtensions.getSoapModules()));
        axisBinding.setProperty(WSDL2Constants.ATTR_WHTTP_QUERY_PARAMETER_SEPARATOR,
                                soapBindingExtensions.getHttpQueryParameterSeparatorDefault());

        // Capture all the fault specific properties

        BindingFault[] bindingFaults = binding.getBindingFaults();
        for (int i = 0; i < bindingFaults.length; i++) {
            BindingFault bindingFault = bindingFaults[i];
            InterfaceFault interfaceFault = serviceInterface.getFromAllInterfaceFaults(((BindingFaultElement)bindingFault).getRef());
            AxisBindingMessage axisBindingFault = new AxisBindingMessage();
            axisBindingFault.setFault(true);
            axisBindingFault.setName(interfaceFault.getName().getLocalPart());
            axisBindingFault.setParent(axisBinding);

            addDocumentation(axisBindingFault, interfaceFault.toElement());
            SOAPBindingFaultExtensions soapBindingFaultExtensions;

            try {
                soapBindingFaultExtensions = (SOAPBindingFaultExtensions) bindingFault
                        .getComponentExtensionContext(new URI(WSDL2Constants.URI_WSDL2_SOAP));
            } catch (URISyntaxException e) {
                throw new AxisFault("Soap Binding Extention not found");
            }

            axisBindingFault.setProperty(WSDL2Constants.ATTR_WHTTP_HEADER,
                                         createHttpHeaders(
                                                 soapBindingFaultExtensions.getHttpHeaders()));
            axisBindingFault.setProperty(WSDL2Constants.ATTR_WHTTP_CONTENT_ENCODING,
                                         soapBindingFaultExtensions.getHttpContentEncoding());
            axisBindingFault.setProperty(WSDL2Constants.ATTR_WSOAP_CODE,
                                         soapBindingFaultExtensions.getSoapFaultCode());
            axisBindingFault.setProperty(WSDL2Constants.ATTR_WSOAP_SUBCODES,
                                         soapBindingFaultExtensions.getSoapFaultSubcodes());
            axisBindingFault.setProperty(WSDL2Constants.ATTR_WSOAP_HEADER,
                                         createSoapHeaders(
                                                 soapBindingFaultExtensions.getSoapHeaders()));
            axisBindingFault.setProperty(WSDL2Constants.ATTR_WSOAP_MODULE,
                                         createSoapModules(
                                                 soapBindingFaultExtensions.getSoapModules()));

            axisBinding.addFault(axisBindingFault);

        }

        // Capture all the binding operation specific properties

        BindingOperation[] bindingOperations = binding.getBindingOperations();
        for (int i = 0; i < bindingOperations.length; i++) {
            BindingOperation bindingOperation = bindingOperations[i];

            AxisBindingOperation axisBindingOperation = new AxisBindingOperation();
            InterfaceOperation interfaceOperation = serviceInterface.getFromAllInterfaceOperations(((BindingOperationElement)bindingOperation).getRef());
            AxisOperation axisOperation =
                    axisService.getOperation(interfaceOperation.getName());

            axisBindingOperation.setAxisOperation(axisOperation);
            axisBindingOperation.setParent(axisBinding);
            axisBindingOperation.setName(axisOperation.getName());
            addDocumentation(axisBindingOperation, bindingOperation.toElement());
            SOAPBindingOperationExtensions soapBindingOperationExtensions;
            try {
                soapBindingOperationExtensions = ((SOAPBindingOperationExtensions)
                        bindingOperation.getComponentExtensionContext(
                                new URI(WSDL2Constants.URI_WSDL2_SOAP)));
            } catch (URISyntaxException e) {
                throw new AxisFault("Soap Binding Extention not found");
            }

            URI soapAction = soapBindingOperationExtensions.getSoapAction();
            if (soapAction != null && !"\"\"".equals(soapAction.toString())) {
                axisBindingOperation.setProperty(WSDL2Constants.ATTR_WSOAP_ACTION,
                                                 soapAction.toString());
            }
            axisBindingOperation.setProperty(WSDL2Constants.ATTR_WSOAP_MODULE,
                                             createSoapModules(
                                                     soapBindingOperationExtensions.getSoapModules()));
            URI soapMep = soapBindingOperationExtensions.getSoapMep();
            if (soapMep != null) {
                axisBindingOperation.setProperty(WSDL2Constants.ATTR_WSOAP_MEP,
                                                 soapMep.toString());
            }
            HTTPLocation httpLocation = soapBindingOperationExtensions.getHttpLocation();
            // If httpLocation is not null we should extract a constant part from it and add its value and the
            // corresponding AxisOperation to a map in order to dispatch rest messages. If httpLocation is null we add
            // the operation name into this map.
            String httpLocationString = null;
            if (httpLocation != null) {
                String httpLocationTemplete = httpLocation.getOriginalLocation();
                axisBindingOperation
                        .setProperty(WSDL2Constants.ATTR_WHTTP_LOCATION, httpLocationTemplete);
                httpLocationString = WSDLUtil.getConstantFromHTTPLocation(httpLocationTemplete, HTTPConstants.HEADER_POST);

            }
            if (httpLocationString != null){
                // this map is used to dispatch operation based on request URI , in the HTTPLocationBasedDispatcher
                httpLocationTable.put(httpLocationString, axisOperation);
            }
            axisBindingOperation.setProperty(WSDL2Constants.ATTR_WHTTP_CONTENT_ENCODING,
                                             soapBindingOperationExtensions.getHttpContentEncodingDefault());
            axisBindingOperation.setProperty(WSDL2Constants.ATTR_WHTTP_QUERY_PARAMETER_SEPARATOR,
                                             soapBindingOperationExtensions.getHttpQueryParameterSeparator());


            BindingMessageReference[] bindingMessageReferences =
                    bindingOperation.getBindingMessageReferences();
            for (int j = 0; j < bindingMessageReferences.length; j++) {
                BindingMessageReference bindingMessageReference = bindingMessageReferences[j];

                AxisBindingMessage axisBindingMessage = new AxisBindingMessage();
                axisBindingMessage.setParent(axisBindingOperation);
                addDocumentation(axisBindingMessage, bindingMessageReference.toElement());
                AxisMessage axisMessage = axisOperation.getMessage(bindingMessageReference
                        .getInterfaceMessageReference().getMessageLabel().toString());

                axisBindingMessage.setAxisMessage(axisMessage);
                axisBindingMessage.setName(axisMessage.getName());
                axisBindingMessage.setDirection(axisMessage.getDirection());


                SOAPBindingMessageReferenceExtensions soapBindingMessageReferenceExtensions;
                try {
                    soapBindingMessageReferenceExtensions =
                            (SOAPBindingMessageReferenceExtensions) bindingMessageReference
                                    .getComponentExtensionContext(
                                            new URI(WSDL2Constants.URI_WSDL2_SOAP));
                } catch (URISyntaxException e) {
                    throw new AxisFault("Soap Binding Extention not found");
                }

                axisBindingMessage.setProperty(WSDL2Constants.ATTR_WHTTP_HEADER,
                                               createHttpHeaders(
                                                       soapBindingMessageReferenceExtensions.getHttpHeaders()));
                axisBindingMessage.setProperty(WSDL2Constants.ATTR_WHTTP_CONTENT_ENCODING,
                                               soapBindingMessageReferenceExtensions.getHttpContentEncoding());
                axisBindingMessage.setProperty(WSDL2Constants.ATTR_WSOAP_HEADER,
                                               createSoapHeaders(
                                                       soapBindingMessageReferenceExtensions.getSoapHeaders()));
                axisBindingMessage.setProperty(WSDL2Constants.ATTR_WSOAP_MODULE,
                                               createSoapModules(
                                                       soapBindingMessageReferenceExtensions.getSoapModules()));

                axisBindingOperation.addChild(axisMessage.getDirection(), axisBindingMessage);

            }

            BindingFaultReference [] bindingFaultReferences =
                    bindingOperation.getBindingFaultReferences();
            for (int j = 0; j < bindingFaultReferences.length; j++) {
                BindingFaultReference bindingFaultReference = bindingFaultReferences[j];

                AxisBindingMessage axisBindingMessageFault = new AxisBindingMessage();
                addDocumentation(axisBindingMessageFault, bindingFaultReference.toElement());
                axisBindingMessageFault.setParent(axisBindingOperation);
                axisBindingMessageFault.setFault(true);
                axisBindingMessageFault.setName(bindingFaultReference.getInterfaceFaultReference()
                        .getInterfaceFault().getName().getLocalPart());

                SOAPBindingFaultReferenceExtensions soapBindingFaultReferenceExtensions;
                try {
                    soapBindingFaultReferenceExtensions =
                            (SOAPBindingFaultReferenceExtensions) bindingFaultReference
                                    .getComponentExtensionContext(
                                            new URI(WSDL2Constants.URI_WSDL2_SOAP));
                } catch (URISyntaxException e) {
                    throw new AxisFault("Soap Binding Extention not found");
                }

                axisBindingMessageFault.setProperty(WSDL2Constants.ATTR_WSOAP_MODULE,
                                                    createSoapModules(
                                                            soapBindingFaultReferenceExtensions.getSoapModules()));

                axisBindingOperation.addFault(axisBindingMessageFault);

            }
            axisBinding.setProperty(WSDL2Constants.HTTP_LOCATION_TABLE, httpLocationTable);
            axisBinding.addChild(axisBindingOperation.getName(), axisBindingOperation);


        }
    }

    private void processHTTPBindingExtention(Binding binding, AxisBinding axisBinding, Interface serviceInterface)
            throws AxisFault {

        Map<String, AxisOperation> httpLocationTable = createHttpLocationTable();
        // Capture all the binding specific properties

        HTTPBindingExtensions httpBindingExtensions;
        try {
            httpBindingExtensions = (HTTPBindingExtensions) binding
                    .getComponentExtensionContext(new URI(WSDL2Constants.URI_WSDL2_HTTP));
        } catch (URISyntaxException e) {
            throw new AxisFault("HTTP Binding Extention not found");
        }

        String httpMethodDefault = httpBindingExtensions.getHttpMethodDefault();
        axisBinding.setProperty(WSDL2Constants.ATTR_WHTTP_METHOD,
                                httpMethodDefault);
        axisBinding.setProperty(WSDL2Constants.ATTR_WHTTP_QUERY_PARAMETER_SEPARATOR,
                                httpBindingExtensions.getHttpQueryParameterSeparatorDefault());
        axisBinding.setProperty(WSDL2Constants.ATTR_WHTTP_CONTENT_ENCODING,
                                httpBindingExtensions.getHttpContentEncodingDefault());

        // Capture all the fault specific properties

        BindingFault[] bindingFaults = binding.getBindingFaults();
        for (int i = 0; i < bindingFaults.length; i++) {
            BindingFault bindingFault = bindingFaults[i];
            InterfaceFault interfaceFault =
                    serviceInterface.getFromAllInterfaceFaults(((BindingFaultElement)bindingFault).getRef());
            AxisBindingMessage axisBindingFault = new AxisBindingMessage();
            axisBindingFault.setFault(true);
            axisBindingFault.setName(interfaceFault.getName().getLocalPart());
            axisBindingFault.setParent(axisBinding);

            addDocumentation(axisBindingFault, interfaceFault.toElement());
            HTTPBindingFaultExtensions httpBindingFaultExtensions;

            try {
                httpBindingFaultExtensions = (HTTPBindingFaultExtensions) bindingFault
                        .getComponentExtensionContext(new URI(WSDL2Constants.URI_WSDL2_HTTP));
            } catch (URISyntaxException e) {
                throw new AxisFault("HTTP Binding Extention not found");
            }

            axisBindingFault.setProperty(WSDL2Constants.ATTR_WHTTP_CODE,
                                         httpBindingFaultExtensions
                                                 .getHttpErrorStatusCode().getCode());
            axisBindingFault.setProperty(WSDL2Constants.ATTR_WHTTP_HEADER,
                                         createHttpHeaders(
                                                 httpBindingFaultExtensions.getHttpHeaders()));
            axisBindingFault.setProperty(WSDL2Constants.ATTR_WHTTP_CONTENT_ENCODING,
                                         httpBindingFaultExtensions.getHttpContentEncoding());
            axisBinding.addFault(axisBindingFault);

        }

        // Capture all the binding operation specific properties

        BindingOperation[] bindingOperations = binding.getBindingOperations();
        for (int i = 0; i < bindingOperations.length; i++) {
            BindingOperation bindingOperation = bindingOperations[i];

            AxisBindingOperation axisBindingOperation = new AxisBindingOperation();
            InterfaceOperation interfaceOperation = serviceInterface.getFromAllInterfaceOperations(((BindingOperationElement)bindingOperation).getRef());
            AxisOperation axisOperation =
                    axisService.getOperation(interfaceOperation.getName());

            axisBindingOperation.setAxisOperation(axisOperation);
            axisBindingOperation.setParent(axisBinding);
            axisBindingOperation.setName(axisOperation.getName());

            addDocumentation(axisBindingOperation, bindingOperation.toElement());
            HTTPBindingOperationExtensions httpBindingOperationExtensions;
            try {
                httpBindingOperationExtensions = ((HTTPBindingOperationExtensions)
                        bindingOperation.getComponentExtensionContext(
                                new URI(WSDL2Constants.URI_WSDL2_HTTP)));
            } catch (URISyntaxException e) {
                throw new AxisFault("HTTP Binding Extention not found");
            }

            axisBindingOperation.setProperty(WSDL2Constants.ATTR_WHTTP_FAULT_SERIALIZATION,
                                             httpBindingOperationExtensions.getHttpFaultSerialization());
            axisBindingOperation.setProperty(WSDL2Constants.ATTR_WHTTP_INPUT_SERIALIZATION,
                                             httpBindingOperationExtensions.getHttpInputSerialization());
            String httpMethod = httpBindingOperationExtensions.
                    getHttpMethod();
            if (httpMethod == null) {
                if (httpMethodDefault != null) {
                    httpMethod = httpMethodDefault;
                } else {
                    Boolean safeParameter =
                            (Boolean) axisOperation.getParameterValue(WSDL2Constants.ATTR_WSDLX_SAFE);
                    if (safeParameter != null && safeParameter.booleanValue()){
                        httpMethod = HTTPConstants.HEADER_GET;
                    } else {
                        httpMethod = HTTPConstants.HEADER_POST;
                    }
                }
            }
            axisBindingOperation
                    .setProperty(WSDL2Constants.ATTR_WHTTP_METHOD, httpMethod);
            HTTPLocation httpLocation = httpBindingOperationExtensions.getHttpLocation();

            // If httpLocation is not null we should extract a constant part from it and add its value and the
            // corresponding AxisOperation to a map in order to dispatch rest messages. If httpLocation is null we add
            // the operation name into this map.
            String httpLocationString = "";
            if (httpLocation != null) {
                String httpLocationTemplete = httpLocation.getOriginalLocation();
                axisBindingOperation
                        .setProperty(WSDL2Constants.ATTR_WHTTP_LOCATION, httpLocationTemplete);
                httpLocationString = WSDLUtil.getConstantFromHTTPLocation(httpLocationTemplete, httpMethod);

            }

            httpLocationTable.put(httpLocationString, axisOperation);

            axisBindingOperation.setProperty(WSDL2Constants.ATTR_WHTTP_IGNORE_UNCITED,
                                             httpBindingOperationExtensions.
                                                     isHttpLocationIgnoreUncited());
            axisBindingOperation.setProperty(WSDL2Constants.ATTR_WHTTP_OUTPUT_SERIALIZATION,
                                             httpBindingOperationExtensions.getHttpOutputSerialization());
            axisBindingOperation.setProperty(WSDL2Constants.ATTR_WHTTP_QUERY_PARAMETER_SEPARATOR,
                                             httpBindingOperationExtensions.getHttpQueryParameterSeparator());
            axisBindingOperation.setProperty(WSDL2Constants.ATTR_WHTTP_CONTENT_ENCODING,
                                             httpBindingOperationExtensions.getHttpContentEncodingDefault());

            BindingMessageReference[] bindingMessageReferences =
                    bindingOperation.getBindingMessageReferences();
            for (int j = 0; j < bindingMessageReferences.length; j++) {
                BindingMessageReference bindingMessageReference = bindingMessageReferences[j];

                AxisBindingMessage axisBindingMessage = new AxisBindingMessage();
                axisBindingMessage.setParent(axisBindingOperation);

                AxisMessage axisMessage = axisOperation.getMessage(bindingMessageReference
                        .getInterfaceMessageReference().getMessageLabel().toString());

                axisBindingMessage.setAxisMessage(axisMessage);
                axisBindingMessage.setName(axisMessage.getName());
                axisBindingMessage.setDirection(axisMessage.getDirection());

                addDocumentation(axisBindingMessage, bindingMessageReference.toElement());
                HTTPBindingMessageReferenceExtensions httpBindingMessageReferenceExtensions;
                try {
                    httpBindingMessageReferenceExtensions =
                            (HTTPBindingMessageReferenceExtensions) bindingMessageReference
                                    .getComponentExtensionContext(
                                            new URI(WSDL2Constants.URI_WSDL2_HTTP));
                } catch (URISyntaxException e) {
                    throw new AxisFault("HTTP Binding Extention not found");
                }

                axisBindingMessage.setProperty(WSDL2Constants.ATTR_WHTTP_HEADER,
                                               createHttpHeaders(
                                                       httpBindingMessageReferenceExtensions.getHttpHeaders()));
                axisBindingMessage.setProperty(WSDL2Constants.ATTR_WHTTP_CONTENT_ENCODING,
                                               httpBindingMessageReferenceExtensions.getHttpContentEncoding());
                axisBindingOperation.addChild(WSDLConstants.MESSAGE_LABEL_IN_VALUE, axisBindingMessage);

            }

            BindingFaultReference[] bindingFaultReferences =
                    bindingOperation.getBindingFaultReferences();
            for (int j = 0; j < bindingFaultReferences.length; j++) {
                BindingFaultReference bindingFaultReference = bindingFaultReferences[j];

                AxisBindingMessage axisBindingMessageFault = new AxisBindingMessage();
                axisBindingMessageFault.setFault(true);
                axisBindingMessageFault.setName(bindingFaultReference.getInterfaceFaultReference()
                        .getInterfaceFault().getName().getLocalPart());
                axisBindingMessageFault.setParent(axisBindingOperation);
                axisBindingOperation.addFault(axisBindingMessageFault);
                addDocumentation(axisBindingMessageFault, bindingFaultReference.toElement());

            }

            axisBinding.setProperty(WSDL2Constants.HTTP_LOCATION_TABLE, httpLocationTable);
            axisBinding.addChild(axisBindingOperation.getName(), axisBindingOperation);

        }
    }

    private void processInterface(Interface serviceInterface)
            throws AxisFault {

        // TODO copy the policy elements
        // copyExtensionAttributes(wsdl4jPortType.getExtensionAttributes(),
        // axisService, PORT_TYPE);

        InterfaceOperation[] interfaceOperations = serviceInterface
                .getInterfaceOperations();
        for (int i = 0; i < interfaceOperations.length; i++) {
            axisService.addOperation(populateOperations(interfaceOperations[i]));
            operationNames.add(interfaceOperations[i].getName());
        }

        Interface[] extendedInterfaces = serviceInterface.getExtendedInterfaces();
        for (int i = 0; i < extendedInterfaces.length; i++) {
            Interface extendedInterface = extendedInterfaces[i];
            processInterface(extendedInterface);
        }

    }

    private AxisOperation populateOperations(InterfaceOperation operation) throws AxisFault {
        QName opName = operation.getName();
        // Copy Name Attribute
        AxisOperation axisOperation = axisService.getOperation(opName);

        if (axisOperation == null) {
            URI pattern = operation.getMessageExchangePattern();
            String MEP;
            if (pattern == null) {
                MEP = WSDL20DefaultValueHolder.getDefaultValue(WSDL2Constants.ATTR_WSOAP_MEP);
            } else {
                MEP = pattern.toString();
            }
            if(!isServerSide){
            	// If in the client, need to toggle in-out to out-in etc.
            	if(WSDL2Constants.MEP_URI_IN_OUT.equals(MEP)){
            		MEP = WSDL2Constants.MEP_URI_OUT_IN;
            	}
            	if(WSDL2Constants.MEP_URI_IN_ONLY.equals(MEP)){
            		MEP = WSDL2Constants.MEP_URI_OUT_ONLY;
            	}
            	if(WSDL2Constants.MEP_URI_IN_OPTIONAL_OUT.equals(MEP)){
            		MEP = WSDL2Constants.MEP_URI_OUT_OPTIONAL_IN;
            	}
            }
            axisOperation = AxisOperationFactory.getOperationDescription(MEP);
            axisOperation.setName(opName);

        }
        URI[] operationStyle = operation.getStyle();
        if (operationStyle != null && operationStyle.length > 0) {
            Parameter opStyleParameter = new Parameter();
            opStyleParameter.setName(WSDL2Constants.OPERATION_STYLE);
            opStyleParameter.setValue(operationStyle);
            axisOperation.addParameter(opStyleParameter);
        }
        addDocumentation(axisOperation, operation.toElement());

        // assuming the style of the operations of WSDL 2.0 is always document, for the time being :)
        // The following can be used to capture the wsdlx:safe attribute

        InterfaceOperationExtensions interfaceOperationExtensions;
        try {
            interfaceOperationExtensions = (InterfaceOperationExtensions) operation
                    .getComponentExtensionContext(
                            new URI(WSDL2Constants.URI_WSDL2_EXTENSIONS));
        } catch (URISyntaxException e) {
            throw new AxisFault("WSDL2 extensions not defined for this operation");
        }

        if (interfaceOperationExtensions != null) {
            Parameter parameter = new Parameter(WSDL2Constants.ATTR_WSDLX_SAFE, Boolean.valueOf(
                    interfaceOperationExtensions.isSafe()));
            axisOperation.addParameter(parameter);
        }

        RPCInterfaceOperationExtensions rpcInterfaceOperationExtensions;
        try {
            rpcInterfaceOperationExtensions = (RPCInterfaceOperationExtensions) operation
                    .getComponentExtensionContext(
                            new URI(WSDL2Constants.URI_WSDL2_RPC));
        } catch (URISyntaxException e) {
            throw new AxisFault("WSDL2 extensions not defined for this operation");
        }

        if (rpcInterfaceOperationExtensions != null) {
            String rpcsig = "";
            Argument[] signatures = rpcInterfaceOperationExtensions.getRPCSignature();
            for (int i = 0; i < signatures.length; i++) {
                Argument sigArgument = signatures[i];
                rpcsig = rpcsig + sigArgument.getName().getLocalPart() + " " + sigArgument.getDirection() + " ";
            }
            Parameter parameter = new Parameter(WSDL2Constants.ATTR_WRPC_SIGNATURE, rpcsig);
            axisOperation.addParameter(parameter);
        }

        InterfaceMessageReference[] interfaceMessageReferences = operation
                .getInterfaceMessageReferences();
        for (int i = 0; i < interfaceMessageReferences.length; i++) {
            InterfaceMessageReference messageReference = interfaceMessageReferences[i];
            if (messageReference.getMessageLabel().equals(
                    MessageLabel.IN)) {
                // Its an input message

                if (isServerSide) {
                    createAxisMessage(axisOperation, messageReference,
                                      WSDLConstants.MESSAGE_LABEL_IN_VALUE);
                } else {
                    createAxisMessage(axisOperation, messageReference,
                                      WSDLConstants.MESSAGE_LABEL_OUT_VALUE);
                }
            } else if (messageReference.getMessageLabel().equals(
                    MessageLabel.OUT)) {
                if (isServerSide) {
                    createAxisMessage(axisOperation, messageReference,
                                      WSDLConstants.MESSAGE_LABEL_OUT_VALUE);
                } else {
                    createAxisMessage(axisOperation, messageReference,
                                      WSDLConstants.MESSAGE_LABEL_IN_VALUE);
                }
            }

        }

        // add operation level faults

        InterfaceFaultReference[] faults = operation.getInterfaceFaultReferences();
        for (int i = 0; i < faults.length; i++) {
            AxisMessage faultMessage = new AxisMessage();

            InterfaceFaultReference interfaceFaultReference = faults[i];
            faultMessage.setDirection(interfaceFaultReference.getDirection().toString());

            InterfaceFault interfaceFault = interfaceFaultReference.getInterfaceFault();

            if (interfaceFault == null) {
                throw new AxisFault("Interface Fault reference defined in operation " + opName + " cannot be found in interface");
            }
            
            // retrieve interface fault element
            InterfaceFaultElement interfaceFaultElement = interfaceFault.toElement();
            // drill down to get actual interface fault element definition
            QNameTokenUnion interfaceFaultElementDef = interfaceFaultElement.getElement();
            QName qName = interfaceFaultElementDef.getQName();
            String name = qName.getLocalPart();
            
            faultMessage.setElementQName(qName);
            faultMessage.setName(name);

            axisOperation.setFaultMessages(faultMessage);
        }


        return axisOperation;
    }

    private void createAxisMessage(AxisOperation axisOperation,
                                   InterfaceMessageReference messageReference, String messageLabel)
            throws AxisFault {
        AxisMessage message = axisOperation
                .getMessage(messageLabel);

        String messageContentModelName = messageReference.getMessageContentModel();
        QName elementQName = null;

        if (WSDL2Constants.NMTOKEN_ELEMENT.equals(messageContentModelName)) {
            ElementDeclaration elementDeclaration = messageReference.getElementDeclaration();
            if (elementDeclaration == null) {
                InterfaceMessageReferenceElement messageReferenceElement =
                        messageReference.toElement();
                QName qName = messageReferenceElement.getElement().getQName();
                throw new AxisFault("Unable to find element " + qName.toString() + " reffered to by operation " + axisOperation.getName().getLocalPart());
            }
            elementQName = elementDeclaration.getName();
        } else if (WSDL2Constants.NMTOKEN_ANY.equals(messageContentModelName)) {
            elementQName = Constants.XSD_ANY;
        } else
        if (WSDL2Constants.NMTOKEN_NONE.equals(messageContentModelName)) {
            // nothing to do here keep the message element as null
        } else {
            throw new AxisFault("Sorry we do not support " + messageContentModelName +
                    ". We do only support #any, #none and #element as message content models.");
        }

        message.setElementQName(elementQName);
        message.setName(elementQName != null ? elementQName.getLocalPart() : axisOperation.getName().getLocalPart());
        axisOperation.addMessage(message, messageLabel);

        
        if(WSDLConstants.MESSAGE_LABEL_IN_VALUE.equals(messageLabel)){
        	XMLAttr xa = messageReference.toElement().getExtensionAttribute(new QName("http://www.w3.org/2006/05/addressing/wsdl","Action"));
        	if(xa!=null){
        		String value = (String)xa.getContent();
        		if(value != null){
        			ArrayList<String> al = axisOperation.getWSAMappingList();
        			if(al == null){
        				al = new ArrayList<String>();
        				axisOperation.setWsamappingList(al);
        			}
        			al.add(value);
        		}
        	}
        }else{
        	XMLAttr xa = messageReference.toElement().getExtensionAttribute(new QName("http://www.w3.org/2006/05/addressing/wsdl","Action"));
        	if(xa!=null){
        		String value = (String)xa.getContent();
        		if(value != null){
        			axisOperation.setOutputAction(value);
        		}
        	}
        }
        
        // populate this map so that this can be used in SOAPBody based dispatching
        if (elementQName != null) {
            axisService
                    .addMessageElementQNameToOperationMapping(elementQName, axisOperation);
        }
    }

    private Description readInTheWSDLFile(String wsdlURI) throws WSDLException, AxisFault {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
                .newInstance();
        documentBuilderFactory.setNamespaceAware(true);
        DocumentBuilder documentBuilder;
        Document document;
        try {
            documentBuilder = documentBuilderFactory.newDocumentBuilder();
            document = documentBuilder.parse(wsdlURI);
        } catch (ParserConfigurationException e) {
            throw AxisFault.makeFault(e);
        } catch (IOException e) {
            throw AxisFault.makeFault(e);
        } catch (SAXException e) {
            throw AxisFault.makeFault(e);
        }
        return readInTheWSDLFile(document);
    }

    private Description readInTheWSDLFile(InputStream inputStream) throws WSDLException, AxisFault {

       DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
                .newInstance();
        documentBuilderFactory.setNamespaceAware(true);
        DocumentBuilder documentBuilder;
        Document document;
        try {
            documentBuilder = documentBuilderFactory.newDocumentBuilder();
            document = documentBuilder.parse(inputStream);
        } catch (ParserConfigurationException e) {
            throw AxisFault.makeFault(e);
        } catch (IOException e) {
            throw AxisFault.makeFault(e);
        } catch (SAXException e) {
            throw AxisFault.makeFault(e);
        }
        return readInTheWSDLFile(document);
    }

    private Description readInTheWSDLFile(Document document) throws WSDLException {
        WSDLFactory factory = WSDLFactory.newInstance();
        WSDLReader reader = factory.newWSDLReader();
        if (customWSDLResolver != null) {
            reader.setURIResolver(customWSDLResolver);
        }
        // This turns on WSDL validation which is set off by default.
        reader.setFeature(WSDLReader.FEATURE_VALIDATION, true);
        WSDLSource wsdlSource = reader.createWSDLSource();
        wsdlSource.setSource(document.getDocumentElement());
        String uri = getBaseUri();
        if (uri != null && !"".equals(uri)) {
            try {
                wsdlSource.setBaseURI(new URI(uri));
            } catch (URISyntaxException e) {
                File f = new File(uri);
                if(f.exists()) {
                    wsdlSource.setBaseURI(f.toURI());
                } else {
                    log.error(e.toString(), e);
                }
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("Reading 2.0 WSDL with wsdl uri = " + wsdlURI);
            log.trace("  the stack at this point is: " + stackToString());
        }
        return reader.readWSDL(wsdlSource);
    }

    /**
     * Convert woden dependent SOAPHeaderBlock objects to SOAPHeaderMessage objects
     *
     * @param soapHeaderBlocks - An array of SOAPHeaderBlock objects
     * @return List<SOAPHeaderMessage - An List of SOAPHeaderMessage objects
     */
    private List<SOAPHeaderMessage> createSoapHeaders(SOAPHeaderBlock soapHeaderBlocks[]) {

        if (soapHeaderBlocks.length == 0) {
            return null;
        }
        List<SOAPHeaderMessage> soapHeaderMessages = new ArrayList<SOAPHeaderMessage>();

        for (int i = 0; i < soapHeaderBlocks.length; i++) {
            SOAPHeaderBlock soapHeaderBlock = soapHeaderBlocks[i];
            ElementDeclaration elementDeclaration = soapHeaderBlock.getElementDeclaration();

            if (elementDeclaration != null) {
                QName name = elementDeclaration.getName();
                SOAPHeaderMessage soapHeaderMessage = new SOAPHeaderMessage();
                soapHeaderMessage.setElement(name);
                soapHeaderMessage.setRequired(soapHeaderBlock.isRequired().booleanValue());
                soapHeaderMessage
                        .setMustUnderstand(soapHeaderBlock.mustUnderstand().booleanValue());
                soapHeaderMessages.add(soapHeaderMessage);
            }
        }
        return soapHeaderMessages;
    }

    /**
     * Convert woden dependent SOAPHeaderBlock objects to SOAPHeaderMessage objects
     *
     * @param soapModules - An array of SOAPModule objects
     * @return List<SOAPModuleMessage> - An List of SOAPHeaderMessage objects
     */
    private List<SOAPModuleMessage> createSoapModules(SOAPModule soapModules[]) {

        if (soapModules.length == 0) {
            return null;
        }
        List<SOAPModuleMessage> soapModuleMessages = new ArrayList<SOAPModuleMessage>();

        for (int i = 0; i < soapModules.length; i++) {
            SOAPModule soapModule = soapModules[i];
            SOAPModuleMessage soapModuleMessage = new SOAPModuleMessage();
            soapModuleMessage.setUri(soapModule.getRef().toString());
            soapModuleMessages.add(soapModuleMessage);
        }
        return soapModuleMessages;
    }

    /**
     * Convert woden dependent HTTPHeader objects to Header objects
     *
     * @param httpHeaders - An array of HTTPHeader objects
     * @return List<HTTPHeaderMessage> - An List of Header objects
     */
    private List<HTTPHeaderMessage>  createHttpHeaders(HTTPHeader httpHeaders[]) {

        if (httpHeaders.length == 0) {
            return null;
        }
        List<HTTPHeaderMessage> httpHeaderMessages = new ArrayList<HTTPHeaderMessage>();

        for (int i = 0; i < httpHeaders.length; i++) {
            HTTPHeader httpHeader = (HTTPHeader) httpHeaders[i];
            HTTPHeaderMessage httpHeaderMessage = new HTTPHeaderMessage();
            httpHeaderMessage.setqName(((HTTPHeaderElement)httpHeader).getTypeName());
            httpHeaderMessage.setName(httpHeader.getName());
            httpHeaderMessage.setRequired(httpHeader.isRequired().booleanValue());
            httpHeaderMessages.add(httpHeaderMessage);
        }
        return httpHeaderMessages;
    }

    /**
     * Adds documentation details to a given AxisDescription.
     * The documentation details is extracted from the WSDL element given.
     * @param axisDescription - The documentation will be added to this
     * @param element - The element that the documentation is extracted from.
     */
    private void addDocumentation(AxisDescription axisDescription, DocumentableElement element) {
        DocumentationElement[] documentationElements = element.getDocumentationElements();
        String documentation = "";
        for (int i = 0; i < documentationElements.length; i++) {
            DocumentationElement documentationElement = documentationElements[i];
            XMLElement contentElement = documentationElement.getContent();
            Element content = (Element)contentElement.getSource();
            if (content != null) {
                documentation = documentation + DOM2Writer.nodeToString(content.getFirstChild());
            }
        }
        if (!"".equals(documentation)) {
            axisDescription.setDocumentation(documentation);
        }
    }
    
    private void setEndpointURL(AxisEndpoint axisEndpoint, String endpointURL) {
    	axisEndpoint.setEndpointURL(endpointURL);
    	try {
    		URL url = new URL(endpointURL);
    		axisEndpoint.setTransportInDescription(url.getProtocol());
    	} catch (Exception e) {
    		log.debug(e.getMessage(), e);
		}    	
    }
}
