WSDL Class

package javaxt.webservices;
import org.w3c.dom.*;
import javaxt.xml.DOM;

/******************************************************************************
/**  WSDL Parser
/*****************************************************************************/
/**  Used to parse a WSDL return information about the web services documented
 *   in the WSDL including service name and description, web methods, and input
 *   parameters.
 *
 <pre>
javaxt.webservices.WSDL wsdl = new javaxt.webservices.WSDL(url);
for (javaxt.webservices.Service service : wsdl.getServices()){
    System.out.println(service.getName());
    for (javaxt.webservices.Method method : service.getMethods()){
        System.out.println(" - " + method.getName());
        javaxt.webservices.Parameters parameters = method.getParameters();
        if (parameters!=null){
            for (javaxt.webservices.Parameter parameter : parameters.getArray()){
                System.out.println("   * " + parameter.getName());
            }
        }
    }
}
 </pre>
 *
 *****************************************************************************/

public class WSDL {

    private Document wsdl;
    private Document ssd; //Simple Service Definition
    private String vbCrLf = "\r\n";
    private String ElementNameSpace = "";
    private java.util.HashMap<String, String> NameSpaces;
    private NodeList Schema;
    private String HttpProxyServer;

    private class Port{
        public String Name;
        public String Binding;
        public String Address;
    }

    private class Binding{
        public String Operation;
        public String SoapAction;
        public String Style;

        public String Name;
        public String Type;
    }

    private class Message{ //PortType
        public String Input;
        public String Output;
        public String Documentation = null;
    }

    private class Element{
        private int id;
        public String Name;
        public String Type;
        public boolean IsNillable = false;
        public boolean IsComplex = false;
        public String minOccurs = "0";
        public String maxOccurs = "1";
        public boolean IsAttribute = false;

        private java.util.ArrayList<Object> children = new java.util.ArrayList<Object>();

        public int hashCode(){
            return id;
        }

        public boolean equals(Object obj){
            if (obj instanceof Element){
                return id==((Element) obj).id;
            }
            else if (obj instanceof Node){
                return id==new Element((Node) obj).id;
            }
            return false;
        }

        public Element(String Name, String Type){
            id = (Name + "\t" + Type).hashCode();
            this.Name = Name;
            this.Type = stripNameSpace(Type);
            IsComplex = isElementComplex(Type);
        }

        public Element(Node node){

            String nodeName = stripNameSpace(node.getNodeName());
            NamedNodeMap attr = node.getAttributes();


            Name = DOM.getAttributeValue(attr, "name");
            Type = DOM.getAttributeValue(attr, "type");
            id = (Name + "\t" + Type).hashCode();

            IsNillable = isElementNillable(DOM.getAttributeValue(attr, "nillable"));
            IsComplex = isElementComplex(Type);
            minOccurs = DOM.getAttributeValue(attr, "minOccurs");
            maxOccurs = DOM.getAttributeValue(attr, "maxOccurs");

            if (minOccurs.length()==0) minOccurs = "0";
            if (maxOccurs.length()==0) maxOccurs = "1";

            Type = stripNameSpace(Type);

            String elementRef = DOM.getAttributeValue(attr, "ref");
            if (elementRef.length()>0){
                Name = elementRef;
            }

            if (nodeName.equalsIgnoreCase("attribute")){
                IsAttribute = true;
                IsNillable = DOM.getAttributeValue(attr, "use").equalsIgnoreCase("optional");
            }
        }

        public void addElement(Element element){

          //Child elements should be unique.
            boolean addElement = true;
            java.util.Iterator it = children.iterator();
            while (it.hasNext()){
                Object child = it.next();
                if (child instanceof Element){
                    Element e = (Element) child;
                    if (e.Name.equals(element.Name) && e.Type.equals(element.Type)){
                        addElement = false;
                    }
                }
            }
            if (addElement) children.add(element);
        }

        public void addChoices(Choices choices){
            children.add(choices);
        }

        public void addOptions(Options options){
            children.add(options);
        }


        public String toString(){
            StringBuffer xml = new StringBuffer();
            xml.append("   <parameter " +
                               "name=\"" + Name + "\" " +
                               "type=\"" + Type + "\" " +
                               "minOccurs=\"" + minOccurs + "\" " +
                               "maxOccurs=\"" + maxOccurs + "\" " +
                               "isattribute=\"" + IsAttribute + "\" " +
                               "iscomplex=\"" + IsComplex + "\" " +
                               "isnillable=\"" + IsNillable + "\">" + vbCrLf);

            java.util.Iterator it = children.iterator();
            while (it.hasNext()){
                xml.append(it.next().toString());
            }

            xml.append("   </parameter>" + vbCrLf);
            return xml.toString();
        }
    }

    private class Options{
        private java.util.ArrayList<String> options = new java.util.ArrayList<String>();
        public Options(){}
        public void addOption(String option){
            options.add(option);
        }
        public String toString(){
            StringBuffer xml = new StringBuffer();
            if (!options.isEmpty()){
                xml.append("<options>");
                java.util.Iterator<String> it = options.iterator();
                while (it.hasNext()){
                    String option = it.next();
                    xml.append("    <option value=\"" + option + "\">" + option + "</option>" + vbCrLf);
                }
                xml.append("</options>");
            }
            return xml.toString();
        }
    }

    private class Choices{
        private java.util.ArrayList<Element> choices = new java.util.ArrayList<Element>();
        public Choices(){}
        public void addChoice(Element choice){
            choices.add(choice);
        }
        public String toString(){
            StringBuffer xml = new StringBuffer();
            if (!choices.isEmpty()){
                xml.append("<options>");
                java.util.Iterator<Element> it = choices.iterator();
                while (it.hasNext()){
                    xml.append("<option>");
                    xml.append(it.next().toString());
                    xml.append("</option>");
                }
                xml.append("</options>");
            }
            return xml.toString();
        }
    }



  //**************************************************************************
  //** Constructor
  //**************************************************************************
  /** Instantiate wsdl parser using a url to a wsdl (java.net.url)
   */
    public WSDL(java.net.URL url){
        this(downloadXML(url, null), null, true, null);
    }

    public WSDL(String url){
        this(downloadXML(url, null), null, true, null);
    }

    public WSDL(java.net.URL url, String HttpProxyServer){
        this(downloadXML(url, HttpProxyServer), null, true, HttpProxyServer);
    }

    public WSDL(Document wsdl) {
        this(wsdl, null, true, null);
    }

    public WSDL(Document wsdl, String HttpProxyServer) {
        this(wsdl, null, true, HttpProxyServer);
    }

    public WSDL(Document wsdl, Document[] xsd) {
        this(wsdl, xsd, false, null);
    }

    private WSDL(Document wsdl, Document[] xsd, boolean followImports, String HttpProxyServer) {
        this.wsdl = wsdl;
        NameSpaces = DOM.getNameSpaces(wsdl);
        ElementNameSpace = getElementNameSpace();

        if (xsd==null){
            xsd = new Document[1];
            xsd[0] = wsdl;
        }
        else{
            Document[] arr = new Document[xsd.length+1];
            arr[0] = wsdl;
            for (int i=1; i<arr.length; i++){
                arr[i] = xsd[i-1];
            }
            xsd = arr;
        }

        this.HttpProxyServer = HttpProxyServer;
        addSchema(xsd, followImports, false);
        parseWSDL();
    }



  //**************************************************************************
  //** getSSD
  //**************************************************************************
  /** Returns a Simple Service Description (SSD) - an xml document which
   *  outlines all the web methods and input parameters defined in
   *  the WSDL. This document is significantly easier to parse and understand
   *  than the original WSDL.
   */
    public Document getSSD(){
        return ssd;
    }


  //**************************************************************************
  //** toString
  //**************************************************************************
  /** Returns a string representing an XML document containing a Simple
   *  Service Description (SSD). Please see the getSSD() for more information.
   */
    public String toString(){
        return DOM.getText(ssd);
    }


// <editor-fold defaultstate="collapsed" desc="Core WSDL Parser. Click on the + sign on the left to edit the code.">



    private NamedNodeMap getDefinitionAttributes(){
        NodeList Definitions = wsdl.getChildNodes();
        for (int i=0; i<Definitions.getLength(); i++ ) {
             if (Definitions.item(i).getNodeType() == 1){
                 if (contains(Definitions.item(i).getNodeName(), "definitions")) {
                     return Definitions.item(i).getAttributes();
                 }
             }
        }
        return null;
    }


  //**************************************************************************
  //** getTargetNameSpace
  //**************************************************************************

    private String getTargetNameSpace(){
        NamedNodeMap attr = getDefinitionAttributes();
        return DOM.getAttributeValue(attr, "targetNamespace");
    }


  //**************************************************************************
  //** getElementNameSpace
  //**************************************************************************

    private String getElementNameSpace(){
        String elementNameSpace = "http://www.w3.org/2001/XMLSchema";
        NamedNodeMap attr = getDefinitionAttributes();
        if (attr!=null){

            Node node;
            String nodeName;
            String nodeValue;

            for (int i=0; i < attr.getLength(); i++ ) {
                node = attr.item(i);
                nodeName = node.getNodeName().toLowerCase();
                nodeValue = node.getNodeValue();

                if (nodeValue.toLowerCase().equals(elementNameSpace.toLowerCase())) {
                    return stripNameSpace(nodeName);
                }

            }
        }
        return "";
    }


  //**************************************************************************
  //** isElementComplex
  //**************************************************************************

    private boolean isElementComplex(String elementType){
        String elementNameSpace = "";
        if (elementType.contains(":")){
            elementNameSpace = elementType.substring(0, elementType.indexOf(":"));
        }
        return !elementNameSpace.equalsIgnoreCase(ElementNameSpace);
    }


  //**************************************************************************
  //** isElementNillable
  //**************************************************************************

    private boolean isElementNillable(String isnillable){
        if (isnillable.toLowerCase().equals("true")){
            return true;
        }
        else{
            return false;
        }
    }


  //**************************************************************************
  //** parseWSDL
  //**************************************************************************
  /** Used to parse the WSDL and generate the SSD.
   *  Note that this parser works for most xml web services in production. That
   *  said, there are a couple limitations. Here's a short list of outstanding
   *  tasks:
   *  <ul>
   *   <ul>
   *   <li>Implement support for Abstract Types</li>
   *   <li>Finish the Parameters.setValue() method to support arrays</li>
   *   <li>Verify that the namespace is identified and used properly (currently
   *   assume one targetNamespace per wsdl)</li>
   *   <li>Test whether parameters are identified properly</li>
   *   <li>Need to test against wsdls w/multiple services</li>
   *   </ul>
   *  </ul>
   */
    private void parseWSDL(){

        String SSD = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + vbCrLf +
              "<ssd>" + vbCrLf;


        String ServiceName = "";
        String ServiceDescription = "";

        NodeList Definitions, ChildNodes;
        NamedNodeMap attr;



        Port Port = null;
        Binding Binding = null;
        java.util.ArrayList<Binding> arrBindings = null;
        Element Element = null;
        java.util.ArrayList<Element> arrElements = null;


      //Loop Through Definitions and Get Services
      //definitions->service (name attribute)
        Definitions = getDefinitions();
        for (int i=0; i<Definitions.getLength(); i++ ) {
            if (contains(Definitions.item(i).getNodeName(), "service")) {

              //Get Service Name
                attr = Definitions.item(i).getAttributes();
                ServiceName = DOM.getAttributeValue(attr, "name");


              //Get Service Description
                ChildNodes = Definitions.item(i).getChildNodes();
                for (int j=0; j<ChildNodes.getLength(); j++ ) {
                    if (contains(ChildNodes.item(j).getNodeName(), "documentation")) {
                        ServiceDescription = ChildNodes.item(j).getTextContent();
                    }
                }


              //Get Service Port
                Port = getPort(ChildNodes);


                if (Port!=null){


                    SSD += " <service name=\"" + ServiceName + "\" url=\"" + Port.Address + "\" namespace=\"" + getTargetNameSpace() + "\">" + vbCrLf;
                    if (ServiceDescription!=null){
                    SSD += "  <description>" + ServiceDescription + "</description>" + vbCrLf;}
                    SSD += "  <methods>" + vbCrLf;

                  //Get Bindings
                    arrBindings = getBindings(Port.Binding);
                    for (int j=0; j<arrBindings.size(); j++ ) {
                         Binding = arrBindings.get(j);


                       //Get Soap Action
                         String SoapAction = Binding.SoapAction;
                         if (SoapAction!=null) SoapAction = " soapAction=\"" + SoapAction + "\"";



                       //Get Messages (need to valid logic here!)
                         //Message Message = getMessages(Port.Name,Binding.Operation);
                         //if (Message==null) Message = getMessages(Port.Binding,Binding.Operation);
                         Message Message = getMessages(Binding.Type,Binding.Operation);


                       //Get Response Element
                         String ResultsNode = "";
                         try{
                             arrElements = getElements(Message.Output);
                             for (int k=0; k<arrBindings.size(); k++ ) {
                                  Element = arrElements.get(k);
                                  ResultsNode = Element.Name;
                             }
                         }
                         catch(Exception e){
                             //System.out.println(e.toString());
                         }


                         SSD +="  <method name=\"" + Binding.Operation + "\"" + SoapAction + " resultsNode=\"" + ResultsNode + "\">" + vbCrLf;
                         if (Message.Documentation!=null){
                            SSD += "  <description>" + Message.Documentation + "</description>" + vbCrLf;
                         }

                         SSD += "  <parameters>" + vbCrLf;
                         arrElements = getElements(Message.Input);
                         try{
                             for (int k=0; k<arrElements.size(); k++ ) {
                                 Element = arrElements.get(k);
                                 SSD += Element.toString();
                             }
                         }
                         catch(Exception e){
                             //System.out.println(e.toString());
                         }
                         SSD += "  </parameters>" + vbCrLf;


                         SSD +="  </method>" + vbCrLf;
                    }

                  //Update SSD
                    SSD += "  </methods>" + vbCrLf;
                    SSD += " </service>" + vbCrLf;
                }
            }
        }

        SSD += vbCrLf + "</ssd>";
        ssd = DOM.createDocument(SSD);

        knownTypes.clear();
    }


  //**************************************************************************
  //** getPort
  //**************************************************************************

    private Port getPort(NodeList Ports){

        Port Port = null;
        String PortName, PortBinding, PortAddress;
        boolean foundSoapPort = false;

        for (int j=0; j<Ports.getLength(); j++ ) {
            if (contains(Ports.item(j).getNodeName(), "port")) {

                //Get Service Binding
                NamedNodeMap attr = Ports.item(j).getAttributes();
                PortName = DOM.getAttributeValue(attr, "name");
                PortBinding = stripNameSpace(DOM.getAttributeValue(attr, "binding"));


                //Get Service Endpoint (url)
                PortAddress = "";
                NodeList Addresses = Ports.item(j).getChildNodes();
                for (int k=0; k<Addresses.getLength(); k++ ) {
                    String Address = Addresses.item(k).getNodeName();
                    if (contains(Address, "address") && !contains(Address,"http:") ) { //soap:address
                        attr = Addresses.item(k).getAttributes();
                        PortAddress = DOM.getAttributeValue(attr, "location");
                        foundSoapPort = true;
                    }
                }


                if (foundSoapPort){
                    Port = new Port();
                    Port.Name = PortName;
                    Port.Binding = PortBinding;
                    Port.Address = PortAddress;
                    return Port;
                }

            }
        }

        return Port;
    }

  //**************************************************************************
  //** getBindings
  //**************************************************************************

    private java.util.ArrayList<Binding> getBindings(String PortBinding){

        NodeList Definitions, ChildNodes;
        NamedNodeMap attr;

        String BindingName, BindingType, BindingStyle, BindingTransport;

        Binding Binding = null;
        java.util.ArrayList<Binding> arrBindings = null;
        int BindingCount = -1;

        //Loop through definitions
        Definitions = getDefinitions();
        //Definitions = Definitions.item(0).getChildNodes();
        for (int i=0; i<Definitions.getLength(); i++ ) {
            if (contains(Definitions.item(i).getNodeName(), "binding")) {

                //Get Binding Name
                attr = Definitions.item(i).getAttributes();
                BindingName = DOM.getAttributeValue(attr, "name");
                BindingType = DOM.getAttributeValue(attr, "type");
                if (BindingName.equals(PortBinding)){


                    arrBindings = new java.util.ArrayList<Binding>();

                    //Get Binding Transport/Style
                    BindingStyle = BindingTransport = "";
                    ChildNodes = Definitions.item(i).getChildNodes();
                    for (int j=0; j<ChildNodes.getLength(); j++ ) {
                        if (contains(ChildNodes.item(j).getNodeName(), "binding")) {
                            attr = ChildNodes.item(j).getAttributes();
                            BindingStyle = DOM.getAttributeValue(attr, "style");
                            BindingTransport = DOM.getAttributeValue(attr, "transport");
                        }
                    }


                    //Get Operation Names/Soap Action
                    for (int j=0; j<ChildNodes.getLength(); j++ ) {
                        if (contains(ChildNodes.item(j).getNodeName(), "operation")) {

                            Binding = new Binding();
                            BindingCount +=1;

                            attr = ChildNodes.item(j).getAttributes();
                            Binding.Operation = DOM.getAttributeValue(attr, "name");

                            NodeList Operations = ChildNodes.item(j).getChildNodes();
                            for (int k=0; k<Operations.getLength(); k++ ) {
                                 if (contains(Operations.item(k).getNodeName(), "operation")) {
                                     attr = Operations.item(k).getAttributes();
                                     Binding.SoapAction = DOM.getAttributeValue(attr, "soapaction");
                                     Binding.Style = BindingStyle; //DOM.getAttributeValue(attr, "style");

                                     Binding.Name = BindingName;
                                     Binding.Type = stripNameSpace(BindingType);
                                 }
                            }


                            arrBindings.add(Binding);
                        }
                    }


                    return arrBindings;

                }



            }
        }

        return null;
    }


  //**************************************************************************
  //** getMessages
  //**************************************************************************

    private Message getMessages(String PortTypeName, String OperationName){

        //System.out.println();
        //System.out.println(PortTypeName);

        NodeList Definitions, PortTypes, Messages;
        NamedNodeMap attr;
        String portTypeName, operationName, messageName;
        Message Message = null;


        //Loop through definitions
        Definitions = getDefinitions();
        //Definitions = Definitions.item(0).getChildNodes();
        for (int i=0; i<Definitions.getLength(); i++ ) {
            if (contains(Definitions.item(i).getNodeName(), "porttype")) {



                attr = Definitions.item(i).getAttributes();
                portTypeName = DOM.getAttributeValue(attr, "name");

                //System.out.println(" vs " + DOM.getAttributeValue(attr, "name"));

                if (portTypeName.equals(PortTypeName)){


                    String Documentation = "";

                    //Loop through PortTypes
                    PortTypes = Definitions.item(i).getChildNodes();
                    for (int j=0; j<PortTypes.getLength(); j++ ) {

                        String NodeName = PortTypes.item(j).getNodeName();


                        if (NodeName.endsWith("documentation")) {
                            Documentation = DOM.getNodeValue(PortTypes.item(j));
                        }

                        if (NodeName.endsWith("operation")) {

                            attr = PortTypes.item(j).getAttributes();
                            operationName = DOM.getAttributeValue(attr, "name");
                            if (operationName.equals(OperationName)){

                              //Instantiate Message Object
                                Message = new Message();
                                Message.Documentation = Documentation;

                              //Loop through the Messages
                                Messages = PortTypes.item(j).getChildNodes();
                                for (int k=0; k<Messages.getLength(); k++ ) {
                                    if (Messages.item(k).getNodeType()==1){

                                        attr = Messages.item(k).getAttributes();
                                        messageName = stripNameSpace(DOM.getAttributeValue(attr, "message"));

                                        if (contains(Messages.item(k).getNodeName(), "input")) {
                                            Message.Input = messageName;
                                        }
                                        if (contains(Messages.item(k).getNodeName(), "output")) {
                                            Message.Output = messageName;
                                        }
                                        if (contains(Messages.item(k).getNodeName(), "documentation")) {
                                            Documentation = DOM.getNodeValue(Messages.item(k));
                                            if (Documentation.length()>0){
                                                Message.Documentation = Documentation;
                                            }
                                        }
                                    }
                                }

                                return Message;

                            }
                        }
                    }
                }


            }
        }

        return Message;
    }


  //**************************************************************************
  //** getElements
  //**************************************************************************
  /** Returns a list of elements (parameters) associated with a given message.
   *  definitions->message->part (element name attribute)
   */
    private java.util.ArrayList<Element> getElements(String MessageName){

        NodeList Definitions, Messages;
        NamedNodeMap attr;

        String messageName, name, type, min, max;


        java.util.ArrayList<Element> elements = new java.util.ArrayList<Element>();

      //Loop through Definitions and Find Messages
        Definitions = getDefinitions();
        for (int i=0; i<Definitions.getLength(); i++ ) {
            if (contains(Definitions.item(i).getNodeName(), "message")) {

                attr = Definitions.item(i).getAttributes();
                messageName = DOM.getAttributeValue(attr, "name");
                if (messageName.equals(MessageName)){


                  //Loop through Messages and Find Message Parts
                    Messages = Definitions.item(i).getChildNodes();
                    for (int j=0; j<Messages.getLength(); j++ ) {
                        if (contains(Messages.item(j).getNodeName(), "part")) {

                            attr = Messages.item(j).getAttributes();
                            type = DOM.getAttributeValue(attr, "type");

                            String element = stripNameSpace(DOM.getAttributeValue(attr, "element"));
                            elements = getElement(element);
                        }
                    }
                    return elements;
                }
            }
        }

        return null;
    }


  //**************************************************************************
  //** getDefinitions
  //**************************************************************************

    private NodeList getDefinitions(){
        NodeList Definitions = wsdl.getChildNodes();
        if (Definitions!=null){
            for (int i=0; i<Definitions.getLength(); i++){
                 Node node = Definitions.item(i);
                 if (node.getNodeType() == 1){
                     if (node.getNodeName().endsWith("definitions")) {
                         return Definitions.item(i).getChildNodes();
                     }
                 }
            }
        }
        return null;
    }


  //**************************************************************************
  //** getTypes
  //**************************************************************************

    private NodeList getTypes(){
        NodeList Definitions = getDefinitions();
        if (Definitions!=null){
            for (int i=0; i<Definitions.getLength(); i++ ) {
                 Node node = Definitions.item(i);
                 if (node.getNodeType()==1){
                     if (node.getNodeName().endsWith("types")){
                         return node.getChildNodes();
                     }
                 }
            }
        }
        return null;
    }



  //**************************************************************************
  //** getShema
  //**************************************************************************

    private NodeList getSchema(){
        return Schema;
    }


  //**************************************************************************
  //** addSchema
  //**************************************************************************
  /** Used to add an external XSD.
   *  @param followImports Flag used to indicate whether to download/parse XSD
   *  files referenced by the schema/import nodes.
   */
    public void addSchema(Document xsd, boolean followImports){
        addSchema(new Document[]{xsd}, followImports);
    }


  //**************************************************************************
  //** addSchemas
  //**************************************************************************
  /** Used to add multiple external XSDs.
   *  @param followImports Flag used to indicate whether to download/parse XSD
   *  files referenced by the schema/import nodes.
   */
    public void addSchema(Document[] xsd, boolean followImports){
        this.addSchema(xsd, followImports, true);
    }


    private void addSchema(Document[] XSDs, boolean followImports, boolean parseWSDL){

        if (XSDs==null) return;
        if (XSDs.length==0) return;


      //Create xml document to store all the schema
        StringBuffer xml = new StringBuffer();
        xml.append("<?xml version=\"1.0\"?>");
        xml.append("<schemas");
        java.util.Iterator<String> it = NameSpaces.keySet().iterator();
        while (it.hasNext()){
            String ns = it.next();
            String url = (String) NameSpaces.get(ns);
            xml.append(" xmlns:");
            xml.append(ns);
            xml.append("=\"");
            xml.append(url);
            xml.append("\"");
        }
        xml.append(">");


      //Add existing schema
        if (this.Schema!=null) xml.append(DOM.getText(this.Schema));


      //Loop through the new schemas
        for (Document xsd : XSDs){

            if (xsd!=null){

                Node outerNode = DOM.getOuterNode(xsd);
                String outerNodeName = stripNameSpace(outerNode.getNodeName());

                java.util.ArrayList<Node> schemaNodes = new java.util.ArrayList<Node>();
                if (outerNodeName.equalsIgnoreCase("definitions")){
                    NodeList childNodes = outerNode.getChildNodes();
                    for (int i=0; i<childNodes.getLength(); i++){
                        Node node = childNodes.item(i);
                        if (stripNameSpace(node.getNodeName()).equalsIgnoreCase("types")){
                            NodeList types = node.getChildNodes();
                            for (int j=0; j<types.getLength(); j++){
                                Node typeNode = types.item(j);
                                if (stripNameSpace(typeNode.getNodeName()).equalsIgnoreCase("schema")){
                                    NodeList nodes = typeNode.getChildNodes();
                                    for (int k=0; k<nodes.getLength(); k++ ) {
                                        schemaNodes.add(nodes.item(k));
                                    }
                                }
                            }
                        }
                    }
                }
                else if (outerNodeName.equalsIgnoreCase("schema")){
                    //schemaNodes = outerNode.getChildNodes();
                    NodeList nodes = outerNode.getChildNodes();
                    for (int k=0; k<nodes.getLength(); k++ ) {
                        schemaNodes.add(nodes.item(k));
                    }
                }
                else{
                    return;
                }


                NameSpaces.putAll(DOM.getNameSpaces(xsd));
                StringBuffer auxSchemas = new StringBuffer();


              //Loop through Schemas and Get Imports
                for (int i=0; i<schemaNodes.size(); i++ ) {
                    Node schemaNode = schemaNodes.get(i);
                    Node importNode = getNode(schemaNode, "import");
                    if (importNode!=null){

                        if (followImports){
                            NamedNodeMap attr = importNode.getAttributes();
                            String schemaLocation = DOM.getAttributeValue(attr, "schemaLocation");
                            if (schemaLocation.length()>0){
                                importSchemas(schemaLocation, auxSchemas, HttpProxyServer);
                            }
                        }

                        Node iSchema = importNode.getParentNode();
                        iSchema.removeChild(importNode);
                    }
                }


              //Insert new schemas
                for (int i=0; i<schemaNodes.size(); i++ ) {
                    Node schemaNode = schemaNodes.get(i);
                    xml.append(DOM.getText(schemaNode));
                }
                //xml.append(DOM.getText(schemaNodes));
                xml.append(auxSchemas);


            }
        }


      //Finish writing the xml document, serialize it to a DOM object, and reparse the WSDL
        xml.append("</schemas>");
        //boolean parseWSDL = this.Schema!=null;
        this.Schema = DOM.getOuterNode(DOM.createDocument(xml.toString())).getChildNodes();
        if (parseWSDL) parseWSDL();
    }


  //**************************************************************************
  //** importSchemas
  //**************************************************************************
  /** Used to download and parse an XSD document. Used in conjunction with the
   *  addSchema method. Note that this is a recursive function.
   *
   *  @param schemaLocation URL to the XSD file.
   *  @param auxSchemas StringBuffer used to append new schema nodes.
   */
    private void importSchemas(String schemaLocation, StringBuffer auxSchemas, String HttpProxyServer){

        if (schemaLocation==null || schemaLocation.length()==0) return;

        Document doc = downloadXML(schemaLocation, HttpProxyServer);
        if (doc!=null){


            NameSpaces.putAll(DOM.getNameSpaces(doc));


            NodeList Schema = doc.getChildNodes();
            Schema = getChildNodes(Schema,"schema");
            for (int i=0; i<Schema.getLength(); i++ ) {
                Node node = getNode(Schema.item(i), "import");
                if (node!=null){
                    NamedNodeMap attr = node.getAttributes();
                    schemaLocation = DOM.getAttributeValue(attr, "schemaLocation");
                    if (schemaLocation.length()>0){
                        importSchemas(schemaLocation, auxSchemas, HttpProxyServer);
                    }
                    Node iSchema = node.getParentNode();
                    iSchema.removeChild(node);
                }
            }
            auxSchemas.append(DOM.getText(Schema));
        }
    }




  //**************************************************************************
  //** getElement
  //**************************************************************************
  /** Used to find an element with a given name.
   *  definitions->types->schema->element (name attribute)
   */
    private java.util.ArrayList<Element> getElement(String ElementName){

      //Loop Through Schemas and Find Elements
        NodeList schemas = getSchema();
        for (int i=0; i<schemas.getLength(); i++ ) {

            Node elementNode = schemas.item(i);
            String elementName = DOM.getAttributeValue(elementNode, "name");
            String elementType = stripNameSpace(elementNode.getNodeName());

            if (elementName.equals(ElementName)) {

                if (elementType.equalsIgnoreCase("element")) {

                    java.util.ArrayList<Element> elements = new java.util.ArrayList<Element>();
                    Element element = new Element(elementNode);
                    decomposeComplexType(stripNameSpace(element.Name), element);

                    if (!DOM.hasChildren(elementNode)){ //Complex Type!

                        elements.add(element);

                    }
                    else{ //Simple Type

                        java.util.Iterator<Object> it = element.children.iterator();
                        while (it.hasNext()){
                            Object obj = it.next();
                            if (obj instanceof Element){
                                elements.add((Element) obj);
                            }
                            else{// ???
                            }
                        }
                    }

                    return elements;
                }
            }
        }

        return null;
    }

  //**************************************************************************
  //** knownTypes
  //**************************************************************************
  /** Hashmap of known Element Types. It is extremely important that you update
   *  this hashmap whenever you find a new element to prevent StackOverflows
   *  in the recursive decomposeComplexType method.
   */
    private java.util.HashMap<String, Element> knownTypes = new java.util.HashMap<String, Element>();


  //**************************************************************************
  //** decomposeComplexType
  //**************************************************************************
  /** Used to find a complex type and break it down into a collection of simple
   *  types. Note that this is a recursive function.
   */
    private void decomposeComplexType(String ElementName, Element parentElement){

      //Find element in the list of known element types. Return match to bypass
      //the recursive reach and prevent possible stack overflow.
        if (knownTypes.containsKey(ElementName)){
            parentElement.addElement(knownTypes.get(ElementName));
            return;
        }

        NodeList Schemas = getSchema();
        for (int i=0; i<Schemas.getLength(); i++ ) {

            Node node = Schemas.item(i);

            String typeName = DOM.getAttributeValue(node, "name");
            if (typeName.equals(ElementName)){

              //Make sure that the node is different from the parent
                //if (!parentElement.equals(node)){
                    parseComplexNode(node, ElementName, parentElement);
                //}
            }
        }
    }



    private void parseComplexNode(Node node, String ElementName, Element parentElement){

        String nodeName = stripNameSpace(node.getNodeName().toLowerCase());

        if (nodeName.equals("element")){

            if (!DOM.hasChildren(node)){

                Element Element = new Element(node);

              //Bug fix:
                if (Element.equals(parentElement)) return;

                if (Element.IsComplex){
                    decomposeComplexType(stripNameSpace(Element.Type), parentElement);
                }

            }
            else{ //Simple type

                NodeList complexTypes = node.getChildNodes();
                for (Node complexNode : DOM.getNodes(complexTypes)){
                    parseComplexNode(complexNode, ElementName, parentElement);
                }
            }
        }
        else if (nodeName.equals("complextype")){
            NodeList complexTypes = node.getChildNodes();
            for (Node complexNode : DOM.getNodes(complexTypes)){
                String complexType = stripNameSpace(complexNode.getNodeName());
                if (complexType.equalsIgnoreCase("sequence")) {
                    NodeList sequenceNodes = complexNode.getChildNodes();
                    for (Node sequenceNode : DOM.getNodes(sequenceNodes)){
                        if (stripNameSpace(sequenceNode.getNodeName()).equals("element")) {

                            Element Element = new Element(sequenceNode);
                            parentElement.addElement(Element);

                            if (Element.IsComplex){
                                decomposeComplexType(stripNameSpace(Element.Type), Element);
                            }
                            else{
                                knownTypes.put(ElementName, Element);
                            }
                        }
                    }
                }
                else if (complexType.equalsIgnoreCase("attribute")) {

                    Element Element = new Element(complexNode);
                    parentElement.addElement(Element);

                    if (Element.IsComplex){
                        decomposeComplexType(stripNameSpace(Element.Type), Element);
                    }
                    else{
                        knownTypes.put(ElementName, Element);
                    }

                }
                else if (complexType.equalsIgnoreCase("attributeGroup")) {

                    java.util.Iterator<Element> it = getAttributes(complexNode).iterator();
                    while (it.hasNext()){
                        Element Element = it.next();

                        parentElement.addElement(Element);
                        //knownTypes.put(ElementName, Element);
                    }

                }
                else if (complexType.equalsIgnoreCase("simpleContent") || complexType.equalsIgnoreCase("complexContent")) {

                    NodeList childNodes = complexNode.getChildNodes();
                    for (Node childNode : DOM.getNodes(childNodes)){
                        if (stripNameSpace(childNode.getNodeName()).equalsIgnoreCase("extension")){

                            Element Element = new Element(ElementName, DOM.getAttributeValue(childNode, "base"));
                            if (Element.IsComplex){
                                //???
                            }

                            knownTypes.put(ElementName, Element);

                            java.util.Iterator<Element> it = getAttributes(childNode).iterator();
                            while (it.hasNext()){
                                Element.addElement(it.next());
                            }

                            parentElement.addElement(Element);

                        }
                    }
                }
                else if (complexType.equalsIgnoreCase("choice")) {

                    Choices choices = new Choices();
                    NodeList choiceNodes = complexNode.getChildNodes();
                    for (int k=0; k<choiceNodes.getLength(); k++){
                        Node choiceNode = choiceNodes.item(k);
                        if (stripNameSpace(choiceNode.getNodeName()).equalsIgnoreCase("element")){

                            Element Element = new Element(choiceNode);

                            choices.addChoice(Element);

                            if (Element.IsComplex){
                                decomposeComplexType(stripNameSpace(Element.Type), Element);
                            }
                            else{
                                knownTypes.put(ElementName, Element);
                            }
                        }
                    }
                    parentElement.addChoices(choices);
                }
                else if (complexType.equalsIgnoreCase("annotation")) {
                    //Do nothing
                }
                else{
                    //System.out.println("Unsupported complexType: " + complexType);
                }
            }



          //TODO: Need to come up with logic to deal with abstract types!
            /*
            boolean isAbstract = bool(DOM.getAttributeValue(node, "abstract"));
            if (!isAbstract) isAbstract = ElementName.equals(parentElement.Type);
            if (isAbstract && parentElement.children.size()==0){
                System.out.println("Find implementations of " + ElementName + " " + parentElement.children.size());

                java.util.ArrayList<Element> elements = findImplementations(ElementName);
                if (!elements.isEmpty()){
                    Choices choices = new Choices();
                    java.util.Iterator<Element> it = elements.iterator();
                    while (it.hasNext()){
                        choices.addChoice(it.next());
                    }
                    parentElement.addChoices(choices);
                }

            }
            */

        }
        else if (nodeName.equals("simpletype")){

            for (Node restriction : DOM.getElementsByTagName("restriction", node)){
                String base = DOM.getAttributeValue(restriction, "base");

                if (!isElementComplex(base)){
                    parentElement.Type = stripNameSpace(base);
                    parentElement.IsComplex = false;
                }
                else{
                    //???
                }

                Node[] enumeration = DOM.getElementsByTagName("enumeration", restriction);
                if (enumeration.length>0){
                    Options options = new Options();
                    for (Node valueNode : enumeration){
                        String value = DOM.getAttributeValue(valueNode, "value");
                        if (value.length()>0) options.addOption(value);
                    }
                    parentElement.addOptions(options);
                }
            }
        }
        else if (nodeName.equals("attributegroup")){

            java.util.Iterator<Element> it = getAttributes(node).iterator();
            while (it.hasNext()){
                Element Element = it.next();
                parentElement.addElement(Element);
                //knownTypes.put(ElementName, Element);
            }

        }
        else{
            System.out.println("Unsupported element type: " + nodeName);
        }
    }


  //**************************************************************************
  //** getAttributes
  //**************************************************************************
  /** Used to return a list of attributes within an attributeGroup. Note that
   *  this is a recursive function.
   */
    private java.util.ArrayList<Element> getAttributes(Node attributeGroup){
        java.util.ArrayList<Element> elements = new java.util.ArrayList<Element>();
        String name = DOM.getAttributeValue(attributeGroup, "name");
        String ref = DOM.getAttributeValue(attributeGroup, "ref");
        if (name.length()>0){

            NodeList attributes = attributeGroup.getChildNodes();
            for (Node attribute : DOM.getNodes(attributes)){

                String attributeNodeName = stripNameSpace(attribute.getNodeName());
                if (attributeNodeName.equalsIgnoreCase("attribute")){
                    Element element = new Element(attribute);
                    elements.add(element);
                    if (element.IsComplex){
                        decomposeComplexType(stripNameSpace(element.Type), element);
                    }
                }
                else if (attributeNodeName.equalsIgnoreCase("attributeGroup")){
                    java.util.Iterator<Element> it = getAttributes(attribute).iterator();
                    while (it.hasNext()){
                        elements.add(it.next());
                    }
                }
            }
        }
        else if (ref.length()>0){

            Element temp = new Element("", name);
            decomposeComplexType(stripNameSpace(ref), temp);
            java.util.Iterator<Object> it = temp.children.iterator();
            while (it.hasNext()){
                Object obj = it.next();
                if (obj instanceof Element){
                    elements.add((Element)obj);
                }
            }
        }

        return elements;
    }


    /** Used to find implementations of a given abstract type. */
    private java.util.ArrayList<Element> findImplementations(String base){
        java.util.ArrayList<Element> elements = new java.util.ArrayList<Element>();
        NodeList Schemas = getSchema();
        for (int i=0; i<Schemas.getLength(); i++ ) {

            Node node = Schemas.item(i);

            String typeName = DOM.getAttributeValue(node, "name");
            String nodeName = stripNameSpace(node.getNodeName().toLowerCase());

            if (nodeName.equals("complextype")){


                boolean isAbstract = DOM.getAttributeValue(node, "abstract").equalsIgnoreCase("true");
                if (!isAbstract){
                    NodeList complexTypes = node.getChildNodes();
                    for (Node complexNode : DOM.getNodes(complexTypes)){
                        String complexType = stripNameSpace(complexNode.getNodeName());



                        if (complexType.equalsIgnoreCase("simpleContent") || complexType.equalsIgnoreCase("complexContent")) {


                            NodeList childNodes = complexNode.getChildNodes();
                            for (Node childNode : DOM.getNodes(childNodes)){
                                if (stripNameSpace(childNode.getNodeName()).equalsIgnoreCase("extension")){

                                    if (DOM.getAttributeValue(childNode, "base").equals(base)){

                                        System.out.println("\t" + typeName);
                                        elements.add(new Element(node));

                                    }

                                }
                            }

                        }


                    }
                }

            }
        }

        return elements;
    }




  // </editor-fold>

// <editor-fold defaultstate="collapsed" desc="Public Members. Click on the + sign on the left to edit the code.">



  //**************************************************************************
  //** getServices
  //**************************************************************************
  /** Returns a list of web services found in this WSDL. */

    public Service[] getServices(){
        java.util.ArrayList<Service> services = new java.util.ArrayList<Service>();
        for (Node serviceNode : DOM.getNodes(ssd.getElementsByTagName("service"))){
            services.add(new Service(serviceNode));
        }
        if (services.isEmpty()) return null;
        else return services.toArray(new Service[services.size()]);
    }


  //**************************************************************************
  //** getListOfServices
  //**************************************************************************
  /** Returns a list of service names found in this WSDL. */

    public String[] getListOfServices(){
        Service[] services = getServices();
        if (services==null) return null;
        String[] arr = new String[services.length];
        for (int i=0; i<services.length; i++) {
             arr[i] = services[i].getName();
        }
        return arr;
    }


  //**************************************************************************
  //** getService
  //**************************************************************************

    public Service getService(String ServiceName){

        if (ServiceName==null) ServiceName = "";
        ServiceName = ServiceName.trim();

        if (ServiceName.equals("")){
            return getService(0);
        }

        Service[] arrServices = getServices();
        if (arrServices!=null){
            for (int i=0; i<arrServices.length; i++){
                 if (arrServices[i].equals(ServiceName)){
                     return arrServices[i];
                 }
            }
        }
        return null;
    }


  //**************************************************************************
  //** getService
  //**************************************************************************

    public Service getService(int i){
        Service[] arrServices = getServices();
        if (arrServices!=null){
            if (i<arrServices.length) return arrServices[i];
        }
        return null;
    }


  //**************************************************************************
  //** getMethod
  //**************************************************************************

    public Method getMethod(String ServiceName, String MethodName){
        Service service = getService(ServiceName);
        if (service!=null) return service.getMethod(MethodName);
        else return null;
    }


  //**************************************************************************
  //** getMethod
  //**************************************************************************

    public Method getMethod(String MethodName){
        Service Service = getService(0);
        if (Service!=null){
            return Service.getMethod(MethodName);
        }
        return null;
    }


  //**************************************************************************
  //** getListOfMethods
  //**************************************************************************

    public String[] getListOfMethods(String ServiceName){
        Service service = getService(ServiceName);
        if (service!=null){
            Method[] methods = service.getMethods();
            if (methods!=null){
                String[] arr = new String[methods.length];
                for (int i=0; i<methods.length; i++) {
                     arr[i] = methods[i].getName();
                }
                return arr;
            }
        }

        return null;
    }


// </editor-fold>


    private NodeList getChildNodes(NodeList ParentNodes, String NodeName){
        for (int i=0; i<ParentNodes.getLength(); i++ ) {
            Node node = ParentNodes.item(i);
            if (node.getNodeType() == 1){
                 //System.out.println(node.getNodeName());
                if (node.getNodeName().endsWith(NodeName)) {
                    return node.getChildNodes();
                }
            }
        }
        return null;
    }

    private Node getNode(Node node, String NodeName){
       if (node.getNodeType()==1) {
           if (node.getNodeName().endsWith(NodeName)){
               return node;
           }
       }
       return null;
    }


  /** Used to download an XML file and convert it to a DOM Document. */
    private static org.w3c.dom.Document downloadXML(java.net.URL url, String HttpProxyServer){
        javaxt.http.Request request = new javaxt.http.Request(url);
        if (HttpProxyServer!=null) request.setProxy(HttpProxyServer);
        return request.getResponse().getXML();
    }

    private static org.w3c.dom.Document downloadXML(String url, String HttpProxyServer){
        javaxt.http.Request request = new javaxt.http.Request(url);
        if (HttpProxyServer!=null) request.setProxy(HttpProxyServer);
        return request.getResponse().getXML();
    }


  //**************************************************************************
  //** stripNameSpace
  //**************************************************************************

    private String stripNameSpace(String str){
        if (str.contains(":")) str = str.substring(str.lastIndexOf(":")+1);
        return str;
    }

    private boolean contains(String a, String b){
        return a.toLowerCase().contains(b.toLowerCase());
    }

}