org.apache.axis.wsdl.symbolTable.SymbolTable Maven / Gradle / Ivy
/*
 * Copyright 2001-2004 The Apache Software Foundation.
 * 
 * Licensed 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.axis.wsdl.symbolTable;
import org.apache.axis.Constants;
import org.apache.axis.constants.Style;
import org.apache.axis.constants.Use;
import org.apache.axis.utils.Messages;
import org.apache.axis.utils.URLHashSet;
import org.apache.axis.utils.XMLUtils;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.wsdl.Binding;
import javax.wsdl.BindingFault;
import javax.wsdl.BindingInput;
import javax.wsdl.BindingOperation;
import javax.wsdl.BindingOutput;
import javax.wsdl.Definition;
import javax.wsdl.Fault;
import javax.wsdl.Import;
import javax.wsdl.Input;
import javax.wsdl.Message;
import javax.wsdl.Operation;
import javax.wsdl.Output;
import javax.wsdl.Part;
import javax.wsdl.Port;
import javax.wsdl.PortType;
import javax.wsdl.Service;
import javax.wsdl.WSDLException;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.extensions.UnknownExtensibilityElement;
import javax.wsdl.extensions.http.HTTPBinding;
import javax.wsdl.extensions.mime.MIMEContent;
import javax.wsdl.extensions.mime.MIMEMultipartRelated;
import javax.wsdl.extensions.mime.MIMEPart;
import javax.wsdl.extensions.soap.SOAPBinding;
import javax.wsdl.extensions.soap.SOAPBody;
import javax.wsdl.extensions.soap.SOAPFault;
import javax.wsdl.extensions.soap.SOAPHeader;
import javax.wsdl.extensions.soap.SOAPHeaderFault;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.rpc.holders.BooleanHolder;
import javax.xml.rpc.holders.IntHolder;
import javax.xml.rpc.holders.QNameHolder;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
/**
 * This class represents a table of all of the top-level symbols from a set of WSDL Definitions and
 * DOM Documents:  XML types; WSDL messages, portTypes, bindings, and services.
 * 
 * This symbolTable contains entries of the form  where key is of type QName and value is
 * of type Vector.  The Vector's elements are all of the objects that have the given QName.  This is
 * necessary since names aren't unique among the WSDL types.  message, portType, binding, service,
 * could all have the same QName and are differentiated merely by type.  SymbolTable contains
 * type-specific getters to bypass the Vector layer:
 * public PortTypeEntry getPortTypeEntry(QName name), etc.
 */
public class SymbolTable {
    // used to cache dervied types
    protected HashMap derivedTypes = new HashMap();
    // Should the contents of imported files be added to the symbol table?
    /** Field addImports */
    private boolean addImports;
    // The actual symbol table.  This symbolTable contains entries of the form
    //  where key is of type QName and value is of type Vector.  The
    // Vector's elements are all of the objects that have the given QName.  This
    // is necessary since names aren't unique among the WSDL types.  message,
    // portType, binding, service, could all have the same QName and are
    // differentiated merely by type.  SymbolTable contains type-specific
    // getters to bypass the Vector layer:
    // public PortTypeEntry getPortTypeEntry(QName name), etc.
    /** Field symbolTable */
    private HashMap symbolTable = new HashMap();
    // a map of qnames -> Elements in the symbol table
    /** Field elementTypeEntries */
    private final Map elementTypeEntries = new HashMap();
    // an unmodifiable wrapper so that we can share the index with others, safely
    /** Field elementIndex */
    private final Map elementIndex =
            Collections.unmodifiableMap(elementTypeEntries);
    // a map of qnames -> Types in the symbol table
    /** Field typeTypeEntries */
    private final Map typeTypeEntries = new HashMap();
    // an unmodifiable wrapper so that we can share the index with others, safely
    /** Field typeIndex */
    private final Map typeIndex = Collections.unmodifiableMap(typeTypeEntries);
    /**
     * cache of nodes -> base types for complexTypes.  The cache is
     * built on nodes because multiple TypeEntry objects may use the
     * same node.
     */
    protected final Map node2ExtensionBase =
            new HashMap();    // allow friendly access
    /** Field verbose */
    private boolean verbose;
    /** Field quiet */
    protected boolean quiet;
    /** Field btm */
    private BaseTypeMapping btm = null;
    // should we attempt to treat document/literal WSDL as "rpc-style"
    /** Field nowrap */
    private boolean nowrap;
    // Did we encounter wraped mode WSDL
    /** Field wrapped */
    private boolean wrapped = false;
    /** Field ANON_TOKEN */
    public static final String ANON_TOKEN = ">";
    /** Field def */
    private Definition def = null;
    /** Field wsdlURI */
    private String wsdlURI = null;
    /** If this is false, we will "unwrap" literal arrays, generating a plan "String[]" instead
     * of "ArrayOfString" when encountering an element containing a single maxOccurs="unbounded"
     * inner element.
     */
    private boolean wrapArrays;
    Set arrayTypeQNames = new HashSet();
    /** Field elementFormDefaults */
    private final Map elementFormDefaults = new HashMap();
    /**
     * Construct a symbol table with the given Namespaces.
     * 
     * @param btm        
     * @param addImports 
     * @param verbose    
     * @param nowrap     
     */
    public SymbolTable(BaseTypeMapping btm, boolean addImports,
                       boolean verbose, boolean nowrap) {
        this.btm = btm;
        this.addImports = addImports;
        this.verbose = verbose;
        this.nowrap = nowrap;
    }    // ctor
    /**
     * Method isQuiet
     * 
     * @return 
     */
    public boolean isQuiet() {
        return quiet;
    }    
    /**
     * Method setQuiet
     * 
     * @param quiet
     */
    public void setQuiet(boolean quiet) {
        this.quiet = quiet;
    }   
    /**
     * Get the raw symbol table HashMap.
     * 
     * @return 
     */
    public HashMap getHashMap() {
        return symbolTable;
    }    // getHashMap
    /**
     * Get the list of entries with the given QName.  Since symbols can share QNames, this list is
     * necessary.  This list will not contain any more than one element of any given SymTabEntry.
     * 
     * @param qname 
     * @return 
     */
    public Vector getSymbols(QName qname) {
        return (Vector) symbolTable.get(qname);
    }    // get
    /**
     * Get the entry with the given QName of the given class.  If it does not exist, return null.
     * 
     * @param qname 
     * @param cls   
     * @return 
     */
    public SymTabEntry get(QName qname, Class cls) {
        Vector v = (Vector) symbolTable.get(qname);
        if (v == null) {
            return null;
        } else {
            for (int i = 0; i < v.size(); ++i) {
                SymTabEntry entry = (SymTabEntry) v.elementAt(i);
                if (cls.isInstance(entry)) {
                    return entry;
                }
            }
            return null;
        }
    }    // get
    /**
     * Get the type entry for the given qname.
     * 
     * @param qname           
     * @param wantElementType boolean that indicates type or element (for type= or ref=)
     * @return 
     */
    public TypeEntry getTypeEntry(QName qname, boolean wantElementType) {
        if (wantElementType) {
            return getElement(qname);
        } else {
            return getType(qname);
        }
    }    // getTypeEntry
    /**
     * Get the Type TypeEntry with the given QName.  If it doesn't
     * exist, return null.
     * 
     * @param qname 
     * @return 
     */
    public Type getType(QName qname) {
        return (Type) typeTypeEntries.get(qname);
    }    // getType
    /**
     * Get the Element TypeEntry with the given QName.  If it doesn't
     * exist, return null.
     * 
     * @param qname 
     * @return 
     */
    public Element getElement(QName qname) {
        return (Element) elementTypeEntries.get(qname);
    }    // getElement
    /**
     * Get the MessageEntry with the given QName.  If it doesn't exist, return null.
     * 
     * @param qname 
     * @return 
     */
    public MessageEntry getMessageEntry(QName qname) {
        return (MessageEntry) get(qname, MessageEntry.class);
    }    // getMessageEntry
    /**
     * Get the PortTypeEntry with the given QName.  If it doesn't exist, return null.
     * 
     * @param qname 
     * @return 
     */
    public PortTypeEntry getPortTypeEntry(QName qname) {
        return (PortTypeEntry) get(qname, PortTypeEntry.class);
    }    // getPortTypeEntry
    /**
     * Get the BindingEntry with the given QName.  If it doesn't exist, return null.
     * 
     * @param qname 
     * @return 
     */
    public BindingEntry getBindingEntry(QName qname) {
        return (BindingEntry) get(qname, BindingEntry.class);
    }    // getBindingEntry
    /**
     * Get the ServiceEntry with the given QName.  If it doesn't exist, return null.
     * 
     * @param qname 
     * @return 
     */
    public ServiceEntry getServiceEntry(QName qname) {
        return (ServiceEntry) get(qname, ServiceEntry.class);
    }    // getServiceEntry
    /**
     * Get the list of all the XML schema types in the symbol table.  In other words, all entries
     * that are instances of TypeEntry.
     * 
     * @return 
     * @deprecated use specialized get{Element,Type}Index() methods instead
     */
    public Vector getTypes() {
        Vector v = new Vector();
        v.addAll(elementTypeEntries.values());
        v.addAll(typeTypeEntries.values());
        return v;
    }    // getTypes
    /**
     * Return an unmodifiable map of qnames -> Elements in the symbol
     * table.
     * 
     * @return an unmodifiable Map value
     */
    public Map getElementIndex() {
        return elementIndex;
    }
    /**
     * Return an unmodifiable map of qnames -> Elements in the symbol
     * table.
     * 
     * @return an unmodifiable Map value
     */
    public Map getTypeIndex() {
        return typeIndex;
    }
    /**
     * Return the count of TypeEntries in the symbol table.
     * 
     * @return an int value
     */
    public int getTypeEntryCount() {
        return elementTypeEntries.size() + typeTypeEntries.size();
    }
    /**
     * Get the Definition.  The definition is null until
     * populate is called.
     * 
     * @return 
     */
    public Definition getDefinition() {
        return def;
    }    // getDefinition
    /**
     * Get the WSDL URI.  The WSDL URI is null until populate
     * is called, and ONLY if a WSDL URI is provided.
     * 
     * @return 
     */
    public String getWSDLURI() {
        return wsdlURI;
    }    // getWSDLURI
    /**
     * Are we wrapping literal soap body elements.
     * 
     * @return 
     */
    public boolean isWrapped() {
        return wrapped;
    }
    /**
     * Turn on/off element wrapping for literal soap body's.
     * 
     * @param wrapped 
     */
    public void setWrapped(boolean wrapped) {
        this.wrapped = wrapped;
    }
    /**
     * Dump the contents of the symbol table.  For debugging purposes only.
     * 
     * @param out 
     */
    public void dump(java.io.PrintStream out) {
        out.println();
        out.println(Messages.getMessage("symbolTable00"));
        out.println("-----------------------");
        Iterator it = symbolTable.values().iterator();
        while (it.hasNext()) {
            Vector v = (Vector) it.next();
            for (int i = 0; i < v.size(); ++i) {
                out.println(v.elementAt(i).getClass().getName());
                out.println(v.elementAt(i));
            }
        }
        out.println("-----------------------");
    }    // dump
    /**
     * Call this method if you have a uri for the WSDL document
     * 
     * @param uri wsdlURI the location of the WSDL file.
     * @throws IOException                  
     * @throws WSDLException                
     * @throws SAXException                 
     * @throws ParserConfigurationException 
     */
    public void populate(String uri)
            throws IOException, WSDLException, SAXException,
            ParserConfigurationException {
        populate(uri, null, null);
    }    // populate
    /**
     * Method populate
     * 
     * @param uri      
     * @param username 
     * @param password 
     * @throws IOException                  
     * @throws WSDLException                
     * @throws SAXException                 
     * @throws ParserConfigurationException 
     */
    public void populate(String uri, String username, String password)
            throws IOException, WSDLException, SAXException,
            ParserConfigurationException {
        if (verbose) {
            System.out.println(Messages.getMessage("parsing00", uri));
        }
        Document doc = XMLUtils.newDocument(uri, username, password);
        this.wsdlURI = uri;
        try {
            File f = new File(uri);
            if (f.exists()) {
                uri = f.toURL().toString();
            }
        } catch (Exception e) {
        }
        populate(uri, doc);
    }    // populate
    /**
     * Call this method if your WSDL document has already been parsed as an XML DOM document.
     * 
     * @param context context This is directory context for the Document.  If the Document were from file "/x/y/z.wsdl" then the context could be "/x/y" (even "/x/y/z.wsdl" would work).  If context is null, then the context becomes the current directory.
     * @param doc     doc This is the XML Document containing the WSDL.
     * @throws IOException                  
     * @throws SAXException                 
     * @throws WSDLException                
     * @throws ParserConfigurationException 
     */
    public void populate(String context, Document doc)
            throws IOException, SAXException, WSDLException,
            ParserConfigurationException {
        WSDLReader reader = WSDLFactory.newInstance().newWSDLReader();
        reader.setFeature("javax.wsdl.verbose", verbose);
        this.def = reader.readWSDL(context, doc);
        add(context, def, doc);
    }    // populate
    /**
     * Add the given Definition and Document information to the symbol table (including imported
     * symbols), populating it with SymTabEntries for each of the top-level symbols.  When the
     * symbol table has been populated, iterate through it, setting the isReferenced flag
     * appropriately for each entry.
     * 
     * @param context 
     * @param def     
     * @param doc     
     * @throws IOException                  
     * @throws SAXException                 
     * @throws WSDLException                
     * @throws ParserConfigurationException 
     */
    protected void add(String context, Definition def, Document doc)
            throws IOException, SAXException, WSDLException,
            ParserConfigurationException {
        URL contextURL = (context == null)
                ? null
                : getURL(null, context);
        populate(contextURL, def, doc, null);
        processTypes();
        checkForUndefined();
        populateParameters();
        setReferences(def, doc);    // uses wrapped flag set in populateParameters
    }    // add
    /**
     * Scan the Definition for undefined objects and throw an error.
     * 
     * @param def      
     * @param filename 
     * @throws IOException 
     */
    private void checkForUndefined(Definition def, String filename)
            throws IOException {
        if (def != null) {
            // Bindings
            Iterator ib = def.getBindings().values().iterator();
            while (ib.hasNext()) {
                Binding binding = (Binding) ib.next();
                if (binding.isUndefined()) {
                    if (filename == null) {
                        throw new IOException(
                                Messages.getMessage(
                                        "emitFailtUndefinedBinding01",
                                        binding.getQName().getLocalPart()));
                    } else {
                        throw new IOException(
                                Messages.getMessage(
                                        "emitFailtUndefinedBinding02",
                                        binding.getQName().getLocalPart(), filename));
                    }
                }
            }
            // portTypes
            Iterator ip = def.getPortTypes().values().iterator();
            while (ip.hasNext()) {
                PortType portType = (PortType) ip.next();
                if (portType.isUndefined()) {
                    if (filename == null) {
                        throw new IOException(
                                Messages.getMessage(
                                        "emitFailtUndefinedPort01",
                                        portType.getQName().getLocalPart()));
                    } else {
                        throw new IOException(
                                Messages.getMessage(
                                        "emitFailtUndefinedPort02",
                                        portType.getQName().getLocalPart(), filename));
                    }
                }
            }
            /*
             * tomj: This is a bad idea, faults seem to be undefined
             * / RJB reply:  this MUST be done for those systems that do something with
             * / messages.  Perhaps we have to do an extra step for faults?  I'll leave
             * / this commented for now, until someone uses this generator for something
             * / other than WSDL2Java.
             *           // Messages
             *           Iterator i = def.getMessages().values().iterator();
             *           while (i.hasNext()) {
             *               Message message = (Message) i.next();
             *               if (message.isUndefined()) {
             *                   throw new IOException(
             *                           Messages.getMessage("emitFailtUndefinedMessage01",
             *                                   message.getQName().getLocalPart()));
             *               }
             *           }
             */
        }
    }
    /**
     * Scan the symbol table for undefined types and throw an exception.
     * 
     * @throws IOException 
     */
    private void checkForUndefined() throws IOException {
        Iterator it = symbolTable.values().iterator();
        while (it.hasNext()) {
            Vector v = (Vector) it.next();
            for (int i = 0; i < v.size(); ++i) {
                SymTabEntry entry = (SymTabEntry) v.get(i);
                // Report undefined types
                if (entry instanceof UndefinedType) {
                    QName qn = entry.getQName();
                    // Special case dateTime/timeInstant that changed
                    // from version to version.
                    if ((qn.getLocalPart().equals(
                            "dateTime") && !qn.getNamespaceURI().equals(
                                    Constants.URI_2001_SCHEMA_XSD)) || (qn.getLocalPart().equals(
                                            "timeInstant") && qn.getNamespaceURI().equals(
                                                    Constants.URI_2001_SCHEMA_XSD))) {
                        throw new IOException(
                                Messages.getMessage(
                                        "wrongNamespace00", qn.getLocalPart(),
                                        qn.getNamespaceURI()));
                    }
                    // Check for a undefined XSD Schema Type and throw
                    // an unsupported message instead of undefined
                    if (SchemaUtils.isSimpleSchemaType(qn)) {
                        throw new IOException(
                                Messages.getMessage(
                                        "unsupportedSchemaType00", qn.getLocalPart()));
                    }
                    // last case, its some other undefined thing
                    throw new IOException(
                            Messages.getMessage(
                                    "undefined00", qn.toString()));
                }    // if undefined
                else if (entry instanceof UndefinedElement) {
                    throw new IOException(
                            Messages.getMessage(
                                    "undefinedElem00", entry.getQName().toString()));
                }
            }
        }
    }                // checkForUndefined
    /**
     * Add the given Definition and Document information to the symbol table (including imported
     * symbols), populating it with SymTabEntries for each of the top-level symbols.
     * NOTE:  filename is used only by checkForUndefined so that it can report which WSDL file
     * has the problem.  If we're on the primary WSDL file, then we don't know the name and
     * filename will be null.  But we know the names of all imported files.
     */
    private URLHashSet importedFiles = new URLHashSet();
    /**
     * Method populate
     * 
     * @param context  
     * @param def      
     * @param doc      
     * @param filename 
     * @throws IOException                  
     * @throws ParserConfigurationException 
     * @throws SAXException                 
     * @throws WSDLException                
     */
    private void populate(
            URL context, Definition def, Document doc, String filename)
            throws IOException, ParserConfigurationException, SAXException,
            WSDLException {
        if (doc != null) {
            populateTypes(context, doc);
            if (addImports) {
                // Add the symbols from any xsd:import'ed documents.
                lookForImports(context, doc);
            }
        }
        if (def != null) {
            checkForUndefined(def, filename);
            if (addImports) {
                // Add the symbols from the wsdl:import'ed WSDL documents
                Map imports = def.getImports();
                Object[] importKeys = imports.keySet().toArray();
                for (int i = 0; i < importKeys.length; ++i) {
                    Vector v = (Vector) imports.get(importKeys[i]);
                    for (int j = 0; j < v.size(); ++j) {
                        Import imp = (Import) v.get(j);
                        if (!importedFiles.contains(imp.getLocationURI())) {
                            importedFiles.add(imp.getLocationURI());
                            URL url = getURL(context, imp.getLocationURI());
                            populate(url, imp.getDefinition(),
                                    XMLUtils.newDocument(url.toString()),
                                    url.toString());
                        }
                    }
                }
            }
            populateMessages(def);
            populatePortTypes(def);
            populateBindings(def);
            populateServices(def);
        }
    }    // populate
    /**
     * This is essentially a call to "new URL(contextURL, spec)" with extra handling in case spec is
     * a file.
     * 
     * @param contextURL 
     * @param spec       
     * @return 
     * @throws IOException 
     */
    private static URL getURL(URL contextURL, String spec) throws IOException {
        // First, fix the slashes as windows filenames may have backslashes
        // in them, but the URL class wont do the right thing when we later
        // process this URL as the contextURL.
        String path = spec.replace('\\', '/');
        // See if we have a good URL.
        URL url = null;
        try {
            // first, try to treat spec as a full URL
            url = new URL(contextURL, path);
            // if we are deail with files in both cases, create a url
            // by using the directory of the context URL.
            if ((contextURL != null) && url.getProtocol().equals("file")
                    && contextURL.getProtocol().equals("file")) {
                url = getFileURL(contextURL, path);
            }
        } catch (MalformedURLException me) {
            // try treating is as a file pathname
            url = getFileURL(contextURL, path);
        }
        // Everything is OK with this URL, although a file url constructed
        // above may not exist.  This will be caught later when the URL is
        // accessed.
        return url;
    }    // getURL
    /**
     * Method getFileURL
     * 
     * @param contextURL 
     * @param path       
     * @return 
     * @throws IOException 
     */
    private static URL getFileURL(URL contextURL, String path)
            throws IOException {
        if (contextURL != null) {
            // get the parent directory of the contextURL, and append
            // the spec string to the end.
            String contextFileName = contextURL.getFile();
            URL parent = null;
            File parentFile = new File(contextFileName).getParentFile(); 
            if ( parentFile != null ) {
                parent = parentFile.toURL(); 
            }
            if (parent != null) {
                return new URL(parent, path);
            }
        }
        return new URL("file", "", path);
    }    // getFileURL
    /**
     * Recursively find all xsd:import'ed objects and call populate for each one.
     * 
     * @param context 
     * @param node    
     * @throws IOException                  
     * @throws ParserConfigurationException 
     * @throws SAXException                 
     * @throws WSDLException                
     */
    private void lookForImports(URL context, Node node)
            throws IOException, ParserConfigurationException, SAXException,
            WSDLException {
        NodeList children = node.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);
            if ("import".equals(child.getLocalName())) {
                NamedNodeMap attributes = child.getAttributes();
                Node namespace = attributes.getNamedItem("namespace");
                // skip XSD import of soap encoding
                if ((namespace != null)
                        && isKnownNamespace(namespace.getNodeValue())) {
                    continue;
                }
                Node importFile = attributes.getNamedItem("schemaLocation");
                if (importFile != null) {
                    URL url = getURL(context, importFile.getNodeValue());
                    if (!importedFiles.contains(url)) {
                        importedFiles.add(url);
                        String filename = url.toString();
                        populate(url, null, XMLUtils.newDocument(filename),
                                filename);
                    }
                }
            }
            lookForImports(context, child);
        }
    }    // lookForImports
    /**
     * Check if this is a known namespace (soap-enc or schema xsd or schema xsi or xml)
     * 
     * @param namespace 
     * @return true if this is a know namespace.
     */
    public boolean isKnownNamespace(String namespace) {
        if (Constants.isSOAP_ENC(namespace)) {
            return true;
        }
        if (Constants.isSchemaXSD(namespace)) {
            return true;
        }
        if (Constants.isSchemaXSI(namespace)) {
            return true;
        }
        if (namespace.equals(Constants.NS_URI_XML)) {
            return true;
        }
        return false;
    }
    /**
     * Populate the symbol table with all of the Types from the Document.
     * 
     * @param context 
     * @param doc     
     * @throws IOException                  
     * @throws SAXException                 
     * @throws WSDLException                
     * @throws ParserConfigurationException 
     */
    public void populateTypes(URL context, Document doc)
            throws IOException, SAXException, WSDLException,
            ParserConfigurationException {
        addTypes(context, doc, ABOVE_SCHEMA_LEVEL);
    }    // populateTypes
    /**
     * Utility method which walks the Document and creates Type objects for
     * each complexType, simpleType, attributeGroup or element referenced or defined.
     * 
     * What goes into the symbol table?  In general, only the top-level types
     * (ie., those just below
     * the schema tag).  But base types and references can
     * appear below the top level.  So anything
     * at the top level is added to the symbol table,
     * plus non-Element types (ie, base and refd)
     * that appear deep within other types.
     */
    private static final int ABOVE_SCHEMA_LEVEL = -1;
    /** Field SCHEMA_LEVEL */
    private static final int SCHEMA_LEVEL = 0;
    /**
     * Method addTypes
     * 
     * @param context 
     * @param node    
     * @param level   
     * @throws IOException                  
     * @throws ParserConfigurationException 
     * @throws WSDLException                
     * @throws SAXException                 
     */
    private void addTypes(URL context, Node node, int level)
            throws IOException, ParserConfigurationException, WSDLException,
            SAXException {
        if (node == null) {
            return;
        }
        // Get the kind of node (complexType, wsdl:part, etc.)
        String localPart = node.getLocalName();
        if (localPart != null) {
            boolean isXSD =
                    Constants.isSchemaXSD(node.getNamespaceURI());
            if (((isXSD && localPart.equals("complexType"))
                    || localPart.equals("simpleType"))) {
                // If an extension or restriction is present,
                // create a type for the reference
                Node re = SchemaUtils.getRestrictionOrExtensionNode(node);
                if ((re != null) && (Utils.getAttribute(re, "base") != null)) {
                    createTypeFromRef(re);
                }
                Node list = SchemaUtils.getListNode(node);
                if (list != null && Utils.getAttribute(list,"itemType") != null) {
                    createTypeFromRef(list);
                }
                Node union = SchemaUtils.getUnionNode(node);
                if (union != null) {
                    QName [] memberTypes = Utils.getMemberTypeQNames(union);
                    if (memberTypes != null) {
                        for (int i=0;i SCHEMA_LEVEL);
            } else if (isXSD && localPart.equals("attributeGroup")) {
                // bug 23145: support attributeGroup (Brook Richan)
                // Create a type entry for the referenced type
                createTypeFromRef(node);
                // Create a type representing an attributeGroup.
                createTypeFromDef(node, false, level > SCHEMA_LEVEL);
            }  else if (isXSD && localPart.equals("group")) {
                // Create a type entry for the referenced type
                createTypeFromRef(node);
                // Create a type representing an group
                createTypeFromDef(node, false, level > SCHEMA_LEVEL);
            } else if (isXSD && localPart.equals("attribute")) {
                // Create a type entry for the referenced type
                BooleanHolder forElement = new BooleanHolder();
                QName refQName = Utils.getTypeQName(node, forElement,
                        false);
                if ((refQName != null) && !forElement.value) {
                    createTypeFromRef(node);
                    // Get the symbol table entry and make sure it is a simple
                    // type
                    if (refQName != null) {
                        TypeEntry refType = getTypeEntry(refQName, false);
                        if ((refType != null)
                                && (refType instanceof Undefined)) {
                            // Don't know what the type is.
                            // It better be simple so set it as simple
                            refType.setSimpleType(true);
                        } else if ((refType == null)
                                || (!(refType instanceof BaseType)
                                && !refType.isSimpleType())) {
                            // Problem if not simple
                            throw new IOException(
                                    Messages.getMessage(
                                            "AttrNotSimpleType01",
                                            refQName.toString()));
                        }
                    }
                }
                createTypeFromDef(node, true, level > SCHEMA_LEVEL);
            } else if (isXSD && localPart.equals("any")) {
                // Map xsd:any element to special xsd:any "type"
                if (getType(Constants.XSD_ANY) == null) {
                    Type type = new BaseType(Constants.XSD_ANY);
                    symbolTablePut(type);
                }
            } else if (localPart.equals("part")
                    && Constants.isWSDL(node.getNamespaceURI())) {
                // This is a wsdl part.  Create an TypeEntry representing the reference
                createTypeFromRef(node);
            } else if (isXSD && localPart.equals("include")) {
                String includeName = Utils.getAttribute(node, "schemaLocation");
                if (includeName != null) {
                    URL url = getURL(context, includeName);
                    Document includeDoc = XMLUtils.newDocument(url.toString());
                    // Vidyanand : Fix for Bug #15124
                    org.w3c.dom.Element schemaEl =
                            includeDoc.getDocumentElement();
                    if (!schemaEl.hasAttribute("targetNamespace")) {
                        org.w3c.dom.Element parentSchemaEl =
                                (org.w3c.dom.Element) node.getParentNode();
                        if (parentSchemaEl.hasAttribute("targetNamespace")) {
                            // we need to set two things in here
                            // 1. targetNamespace
                            // 2. setup the xmlns= attribute
                            String tns =
                                    parentSchemaEl.getAttribute("targetNamespace");
                            schemaEl.setAttribute("targetNamespace", tns);
                            schemaEl.setAttribute("xmlns", tns);
                        }
                    }
                    populate(url, null, includeDoc, url.toString());
                }
            }
        }
        if (level == ABOVE_SCHEMA_LEVEL) {
            if ((localPart != null)
                && localPart.equals("schema")) {
                level = SCHEMA_LEVEL;
                String targetNamespace = ((org.w3c.dom.Element) node).getAttribute("targetNamespace");
                String elementFormDefault = ((org.w3c.dom.Element) node).getAttribute("elementFormDefault");
                if (targetNamespace != null && targetNamespace.length() > 0) {
                    elementFormDefault = (elementFormDefault == null || elementFormDefault.length() == 0) ?
                            "unqualified" : elementFormDefault;
                    if(elementFormDefaults.get(targetNamespace)==null) {
                        elementFormDefaults.put(targetNamespace, elementFormDefault);
                    }
                }
            }
        } else {
            ++level;
        }
        // Recurse through children nodes
        NodeList children = node.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            addTypes(context, children.item(i), level);
        }
    }    // addTypes
    /**
     * Create a TypeEntry from the indicated node, which defines a type
     * that represents a complexType, simpleType or element (for ref=).
     * 
     * @param node             
     * @param isElement        
     * @param belowSchemaLevel 
     * @throws IOException 
     */
    private void createTypeFromDef(
            Node node, boolean isElement, boolean belowSchemaLevel)
            throws IOException {
        // Get the QName of the node's name attribute value
        QName qName = Utils.getNodeNameQName(node);
        if (qName != null) {
            // If the qname is already registered as a base type,
            // don't create a defining type/element.
            if (!isElement && (btm.getBaseName(qName) != null)) {
                return;
            }
            // If the node has a type or ref attribute, get the
            // qname representing the type
            BooleanHolder forElement = new BooleanHolder();
            QName refQName = Utils.getTypeQName(node, forElement,
                    false);
            if (refQName != null) {
                // Error check - bug 12362
                if (qName.getLocalPart().length() == 0) {
                    String name = Utils.getAttribute(node, "name");
                    if (name == null) {
                        name = "unknown";
                    }
                    throw new IOException(Messages.getMessage("emptyref00",
                            name));
                }
                // Now get the TypeEntry
                TypeEntry refType = getTypeEntry(refQName, forElement.value);
                if (!belowSchemaLevel) {
                    if (refType == null) {
                        throw new IOException(
                                Messages.getMessage(
                                        "absentRef00", refQName.toString(),
                                        qName.toString()));
                    }
                    symbolTablePut(new DefinedElement(qName, refType, node,
                            ""));
                }
            } else {
                // Flow to here indicates no type= or ref= attribute.
                // See if this is an array or simple type definition.
                IntHolder numDims = new IntHolder();
                BooleanHolder underlTypeNillable = new BooleanHolder();
                // If we're supposed to unwrap arrays, supply someplace to put the "inner" QName
                // so we can propagate it into the appropriate metadata container.
                QNameHolder itemQName = wrapArrays ? null : new QNameHolder();
                BooleanHolder forElement2 = new BooleanHolder();
                numDims.value = 0;
                QName arrayEQName = 
                        SchemaUtils.getArrayComponentQName(node,
                                                           numDims,
                                                           underlTypeNillable,
                                                           itemQName,
                                                           forElement2,
                                                           this);
                if (arrayEQName != null) {
                    // Get the TypeEntry for the array element type
                    refQName = arrayEQName;
                    TypeEntry refType = getTypeEntry(refQName, forElement2.value);
                    if (refType == null) {
//                        arrayTypeQNames.add(refQName);
                        // Not defined yet, add one
                        String baseName = btm.getBaseName(refQName);
                        if (baseName != null) {
                            refType = new BaseType(refQName);
                        } else if(forElement2.value) {
                            refType = new UndefinedElement(refQName);
                        } else {
                            refType = new UndefinedType(refQName);
                        }
                        symbolTablePut(refType);
                    }
                    // Create a defined type or element that references refType
                    String dims = "";
                    while (numDims.value > 0) {
                        dims += "[]";
                        numDims.value--;
                    }
                    TypeEntry defType = null;
                    if (isElement) {
                        if (!belowSchemaLevel) {
                            defType =
                               new DefinedElement(qName, refType, node, dims);
                            // Save component type for ArraySerializer
                            defType.setComponentType(arrayEQName);
                            if (itemQName != null)
                                defType.setItemQName(itemQName.value);
                        }
                    } else {
                        defType = new DefinedType(qName, refType, node, dims);
                        // Save component type for ArraySerializer
                        defType.setComponentType(arrayEQName);
                        defType.setUnderlTypeNillable(underlTypeNillable.value);
                        if (itemQName != null)
                            defType.setItemQName(itemQName.value);
                    }
                    if (defType != null) {
                        symbolTablePut(defType);
                    }
                } else {
                    // Create a TypeEntry representing this  type/element
                    String baseName = btm.getBaseName(qName);
                    if (baseName != null) {
                        symbolTablePut(new BaseType(qName));
                    } else {
                        // Create a type entry, set whether it should
                        // be mapped as a simple type, and put it in the
                        // symbol table.
                        TypeEntry te = null;
                        TypeEntry parentType = null;
                        if (!isElement) {
                            te = new DefinedType(qName, node);
                            // check if we are an anonymous type underneath
                            // an element.  If so, we point the refType of the
                            // element to us (the real type).
                            if (qName.getLocalPart().indexOf(ANON_TOKEN) >= 0) {
                                Node parent = node.getParentNode();
                                QName parentQName =
                                        Utils.getNodeNameQName(parent);
                                parentType = getElement(parentQName);
                            }
                        } else {
                            if (!belowSchemaLevel) {
                                te = new DefinedElement(qName, node);
                            }
                        }
                        if (te != null) {
                            if (SchemaUtils.isSimpleTypeOrSimpleContent(node)) {
                                te.setSimpleType(true);
                            }
                            te = (TypeEntry)symbolTablePut(te);
                            if (parentType != null) {
                                parentType.setRefType(te);
                            }
                        }
                    }
                }
            }
        }
    }    // createTypeFromDef
    /**
     * Node may contain a reference (via type=, ref=, or element= attributes) to
     * another type.  Create a Type object representing this referenced type.
     *
     * @param node
     * @throws IOException
     */
    protected void createTypeFromRef(Node node) throws IOException {
        // Get the QName of the node's type attribute value
        BooleanHolder forElement = new BooleanHolder();
        QName qName = Utils.getTypeQName(node, forElement, false);
        if (qName == null || (Constants.isSchemaXSD(qName.getNamespaceURI()) &&
                qName.getLocalPart().equals("simpleRestrictionModel"))) {
            return;
        }
        // Error check - bug 12362
        if (qName.getLocalPart().length() == 0) {
            String name = Utils.getAttribute(node, "name");
            if (name == null) {
                name = "unknown";
            }
            throw new IOException(Messages.getMessage("emptyref00", name));
        }
        // Get Type or Element depending on whether type attr was used.
        TypeEntry type = getTypeEntry(qName, forElement.value);
        // A symbol table entry is created if the TypeEntry is not found
        if (type == null) {
            // See if this is a special QName for collections
            if (qName.getLocalPart().indexOf("[") > 0) {
                QName containedQName = Utils.getTypeQName(node,
                        forElement, true);
                TypeEntry containedTE = getTypeEntry(containedQName,
                        forElement.value);
                if (!forElement.value) {
                    // Case of type and maxOccurs
                    if (containedTE == null) {
                        // Collection Element Type not defined yet, add one.
                        String baseName = btm.getBaseName(containedQName);
                        if (baseName != null) {
                            containedTE = new BaseType(containedQName);
                        } else {
                            containedTE = new UndefinedType(containedQName);
                        }
                        symbolTablePut(containedTE);
                    }
                    boolean wrapped = qName.getLocalPart().endsWith("wrapped");
                    symbolTablePut(new CollectionType(qName, containedTE,
                            node, "[]", wrapped));
                } else {
                    // Case of ref and maxOccurs
                    if (containedTE == null) {
                        containedTE = new UndefinedElement(containedQName);
                        symbolTablePut(containedTE);
                    }
                    symbolTablePut(new CollectionElement(qName,
                            containedTE, node,
                            "[]"));
                }
            } else {
                // Add a BaseType or Undefined Type/Element
                String baseName = btm.getBaseName(qName);
                if (baseName != null) {
                    symbolTablePut(new BaseType(qName));
                    // bugzilla 23145: handle attribute groups
                    // soap/encoding is treated as a "known" schema
                    // so now let's act like we know it
                } else if (qName.equals(Constants.SOAP_COMMON_ATTRS11)) {
                    symbolTablePut(new BaseType(qName));
                    // the 1.1 commonAttributes type contains two attributes
                    // make sure those attributes' types are in the symbol table
                    // attribute name = "id" type = "xsd:ID"
                    if (getTypeEntry(Constants.XSD_ID, false) == null) {
                        symbolTablePut(new BaseType(Constants.XSD_ID));
                    }
                    // attribute name = "href" type = "xsd:anyURI"
                    if (getTypeEntry(Constants.XSD_ANYURI, false) == null) {
                        symbolTablePut(new BaseType(Constants.XSD_ANYURI));
                    }
                } else if (qName.equals(Constants.SOAP_COMMON_ATTRS12)) {
                    symbolTablePut(new BaseType(qName));
                    // the 1.2 commonAttributes type contains one attribute
                    // make sure the attribute's type is in the symbol table
                    // attribute name = "id" type = "xsd:ID"
                    if (getTypeEntry(Constants.XSD_ID, false) == null) {
                        symbolTablePut(new BaseType(Constants.XSD_ID));
                    }
                } else if (qName.equals(Constants.SOAP_ARRAY_ATTRS11)) {
                    symbolTablePut(new BaseType(qName));
                    // the 1.1 arrayAttributes type contains two attributes
                    // make sure the attributes' types are in the symbol table
                    // attribute name = "arrayType" type = "xsd:string"
                    if (getTypeEntry(Constants.XSD_STRING, false) == null) {
                        symbolTablePut(new BaseType(Constants.XSD_STRING));
                    }
                    // attribute name = "offset" type = "soapenc:arrayCoordinate"
                    // which is really an xsd:string
                } else if (qName.equals(Constants.SOAP_ARRAY_ATTRS12)) {
                    symbolTablePut(new BaseType(qName));
                    // the 1.2 arrayAttributes type contains two attributes
                    // make sure the attributes' types are in the symbol table
                    // attribute name = "arraySize" type = "2003soapenc:arraySize"
                    // which is really a hairy beast that is not
                    // supported, yet; so let's just use string
                    if (getTypeEntry(Constants.XSD_STRING, false) == null) {
                        symbolTablePut(new BaseType(Constants.XSD_STRING));
                    }
                    // attribute name = "itemType" type = "xsd:QName"
                    if (getTypeEntry(Constants.XSD_QNAME, false) == null) {
                        symbolTablePut(new BaseType(Constants.XSD_QNAME));
                    }
                } else if (forElement.value == false) {
                    symbolTablePut(new UndefinedType(qName));
                } else {
                    symbolTablePut(new UndefinedElement(qName));
                }
            }
        }
    }    // createTypeFromRef
    /**
     * Populate the symbol table with all of the MessageEntry's from the Definition.
     *
     * @param def
     * @throws IOException
     */
    private void populateMessages(Definition def) throws IOException {
        Iterator i = def.getMessages().values().iterator();
        while (i.hasNext()) {
            Message message = (Message) i.next();
            MessageEntry mEntry = new MessageEntry(message);
            symbolTablePut(mEntry);
        }
    }    // populateMessages
    /**
     * ensures that a message in a <input>, <output>,
     * or <fault> element in an <operation>
     * element is valid. In particular, ensures that
     * 
     * - an attribute 
message is present (according to the
     * XML Schema for WSDL 1.1 message is required
     * 
     *  - the value of attribute 
message (a QName) refers to
     * an already defined message
     *  
     * 
     * Note: this method should throw a javax.wsdl.WSDLException rather than
     * a java.io.IOException
     *
     * @param message the message object
     * @throws IOException thrown, if the message is not valid
     */
    protected void ensureOperationMessageValid(Message message)
            throws IOException {
        // make sure the message is not null (i.e. there is an
        // attribute 'message ')
        //
        if (message == null) {
            throw new IOException(
                    ",        © 2015 - 2025 Weber Informatics LLC | Privacy Policy