| /* |
| * 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.components.logger.LogFactory; |
| 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.apache.commons.logging.Log; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.EntityResolver; |
| import org.xml.sax.InputSource; |
| 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.extensions.soap12.SOAP12Binding; |
| import javax.wsdl.extensions.soap12.SOAP12Body; |
| import javax.wsdl.extensions.soap12.SOAP12Fault; |
| import javax.wsdl.extensions.soap12.SOAP12Header; |
| import javax.wsdl.extensions.soap12.SOAP12HeaderFault; |
| 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. |
| * <p> |
| * This symbolTable contains entries of the form <key, value> 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 { |
| private static final Log log = LogFactory.getLog(SymbolTable.class.getName()); |
| |
| // 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 |
| // <key, value> 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; |
| |
| private EntityResolver entityResolver; |
| |
| /** 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 <code>Map</code> value |
| */ |
| public Map getElementIndex() { |
| return elementIndex; |
| } |
| |
| /** |
| * Return an unmodifiable map of qnames -> Elements in the symbol |
| * table. |
| * |
| * @return an unmodifiable <code>Map</code> value |
| */ |
| public Map getTypeIndex() { |
| return typeIndex; |
| } |
| |
| /** |
| * Return the count of TypeEntries in the symbol table. |
| * |
| * @return an <code>int</code> 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; |
| } |
| |
| /** |
| * Get the entity resolver. |
| * |
| * @return the entity resolver, or <code>null</code> if no entity resolver is configured |
| */ |
| public EntityResolver getEntityResolver() { |
| return entityResolver; |
| } |
| |
| /** |
| * Set the entity resolver. This is used to load the WSDL file (unless it is supplied as a |
| * {@link Document}) and all imported WSDL and schema documents. |
| * |
| * @param entityResolver |
| * the entity resolver, or <code>null</code> to use a default entity resolver |
| */ |
| public void setEntityResolver(EntityResolver entityResolver) { |
| this.entityResolver = entityResolver; |
| } |
| |
| /** |
| * 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(new WSDLLocatorAdapter(context, |
| entityResolver != null ? entityResolver : NullEntityResolver.INSTANCE), |
| doc.getDocumentElement()); |
| |
| 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 |
| |
| private Document newDocument(String systemId) throws SAXException, IOException, ParserConfigurationException { |
| InputSource is = null; |
| if (entityResolver != null) { |
| is = entityResolver.resolveEntity(null, systemId); |
| } |
| if (is == null) { |
| is = new InputSource(systemId); |
| } |
| return XMLUtils.newDocument(is); |
| } |
| |
| /** |
| * 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(), |
| 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, 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. |
| * <p/> |
| * 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<memberTypes.length;i++) { |
| if (SchemaUtils.isSimpleSchemaType(memberTypes[i]) && |
| getType(memberTypes[i]) == null) { |
| symbolTablePut(new BaseType(memberTypes[i])); |
| } |
| } |
| } |
| } |
| |
| // This is a definition of a complex type. |
| // Create a Type. |
| createTypeFromDef(node, false, false); |
| } else if (isXSD && localPart.equals("element")) { |
| |
| // Create a type entry for the referenced type |
| createTypeFromRef(node); |
| |
| // 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); |
| } |
| |
| // Create a type representing an element. (This may |
| // seem like overkill, but is necessary to support ref= |
| // and element=. |
| createTypeFromDef(node, true, level > 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 = 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=<targetNamespace> 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 <code><input></code>, <code><output></code>, |
| * or <code><fault></code> element in an <code><operation></code> |
| * element is valid. In particular, ensures that |
| * <ol> |
| * <li>an attribute <code>message</code> is present (according to the |
| * XML Schema for WSDL 1.1 <code>message</code> is <strong>required</strong> |
| * <p> |
| * <li>the value of attribute <code>message</code> (a QName) refers to |
| * an already defined message |
| * </ol> |
| * <p> |
| * <strong>Note</strong>: this method should throw a <code>javax.wsdl.WSDLException</code> rather than |
| * a <code>java.io.IOException</code> |
| * |
| * @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( |
| "<input>,<output>, or <fault> in <operation ..> without attribute 'message' found. Attribute 'message' is required."); |
| } |
| |
| // make sure the value of the attribute refers to an |
| // already defined message |
| // |
| if (message.isUndefined()) { |
| throw new IOException( |
| "<input ..>, <output ..> or <fault ..> in <portType> with undefined message found. message name is '" |
| + message.getQName().toString() + "'"); |
| } |
| } |
| |
| /** |
| * ensures that an an element <code><operation></code> within |
| * an element <code><portType></code> is valid. Throws an exception |
| * if the operation is not valid. |
| * <p> |
| * <strong>Note</strong>: this method should throw a <code>javax.wsdl.WSDLException</code> |
| * rather than a <code>java.io.IOException</code> |
| * |
| * @param operation the operation element |
| * @throws IOException thrown, if the element is not valid. |
| * @throws IllegalArgumentException thrown, if operation is null |
| */ |
| protected void ensureOperationValid(Operation operation) |
| throws IOException { |
| |
| if (operation == null) { |
| throw new IllegalArgumentException( |
| "parameter 'operation' must not be null"); |
| } |
| |
| Input input = operation.getInput(); |
| Message message; |
| |
| if (input != null) { |
| message = input.getMessage(); |
| if (message == null) { |
| throw new IOException( |
| "No 'message' attribute in <input> for operation '" + |
| operation.getName() + "'"); |
| } |
| ensureOperationMessageValid(message); |
| } |
| |
| Output output = operation.getOutput(); |
| |
| if (output != null) { |
| message = output.getMessage(); |
| if (message == null) { |
| throw new IOException( |
| "No 'message' attribute in <output> for operation '" + |
| operation.getName() + "'"); |
| } |
| ensureOperationMessageValid(output.getMessage()); |
| } |
| |
| Map faults = operation.getFaults(); |
| |
| if (faults != null) { |
| Iterator it = faults.values().iterator(); |
| |
| while (it.hasNext()) { |
| Fault fault = (Fault)it.next(); |
| message = fault.getMessage(); |
| if (message == null) { |
| throw new IOException( |
| "No 'message' attribute in <fault> named '" + |
| fault.getName() + "' for operation '" + |
| operation.getName() + "'"); |
| } |
| ensureOperationMessageValid(message); |
| } |
| } |
| } |
| |
| /** |
| * ensures that an an element <code><portType></code> |
| * is valid. Throws an exception if the portType is not valid. |
| * <p> |
| * <strong>Note</strong>: this method should throw a <code>javax.wsdl.WSDLException</code> |
| * rather than a <code>java.io.IOException</code> |
| * |
| * @param portType the portType element |
| * @throws IOException thrown, if the element is not valid. |
| * @throws IllegalArgumentException thrown, if operation is null |
| */ |
| protected void ensureOperationsOfPortTypeValid(PortType portType) |
| throws IOException { |
| |
| if (portType == null) { |
| throw new IllegalArgumentException( |
| "parameter 'portType' must not be null"); |
| } |
| |
| List operations = portType.getOperations(); |
| |
| // no operations defined ? -> valid according to the WSDL 1.1 schema |
| // |
| if ((operations == null) || (operations.size() == 0)) { |
| return; |
| } |
| |
| // check operations defined in this portType |
| // |
| Iterator it = operations.iterator(); |
| |
| while (it.hasNext()) { |
| Operation operation = (Operation) it.next(); |
| |
| ensureOperationValid(operation); |
| } |
| } |
| |
| /** |
| * Populate the symbol table with all of the PortTypeEntry's from the Definition. |
| * |
| * @param def |
| * @throws IOException |
| */ |
| private void populatePortTypes(Definition def) throws IOException { |
| |
| Iterator i = def.getPortTypes().values().iterator(); |
| |
| while (i.hasNext()) { |
| PortType portType = (PortType) i.next(); |
| |
| if (log.isDebugEnabled()) { |
| log.debug("Processing port type " + portType.getQName()); |
| } |
| |
| // If the portType is undefined, then we're parsing a Definition |
| // that didn't contain a portType, merely a binding that referred |
| // to a non-existent port type. Don't bother with it. |
| if (!portType.isUndefined()) { |
| ensureOperationsOfPortTypeValid(portType); |
| |
| PortTypeEntry ptEntry = new PortTypeEntry(portType); |
| |
| symbolTablePut(ptEntry); |
| } |
| } |
| } // populatePortTypes |
| |
| /** |
| * Create the parameters and store them in the bindingEntry. |
| * |
| * @throws IOException |
| */ |
| private void populateParameters() throws IOException { |
| |
| Iterator it = symbolTable.values().iterator(); |
| |
| while (it.hasNext()) { |
| Vector v = (Vector) it.next(); |
| |
| for (int i = 0; i < v.size(); ++i) { |
| if (v.get(i) instanceof BindingEntry) { |
| BindingEntry bEntry = (BindingEntry) v.get(i); |
| |
| // Skip non-soap bindings |
| if (bEntry.getBindingType() != BindingEntry.TYPE_SOAP) { |
| continue; |
| } |
| |
| Binding binding = bEntry.getBinding(); |
| Collection bindOperations = bEntry.getOperations(); |
| PortType portType = binding.getPortType(); |
| HashMap parameters = new HashMap(); |
| Iterator operations = |
| portType.getOperations().iterator(); |
| |
| // get parameters |
| while (operations.hasNext()) { |
| Operation operation = (Operation) operations.next(); |
| |
| // See if the PortType operation has a corresponding |
| // Binding operation and report an error if it doesn't. |
| if (!bindOperations.contains(operation)) { |
| throw new IOException( |
| Messages.getMessage( |
| "emitFailNoMatchingBindOperation01", |
| operation.getName(), |
| portType.getQName().getLocalPart())); |
| } |
| |
| if (log.isDebugEnabled()) { |
| log.debug("Processing operation " + operation.getName()); |
| } |
| |
| String namespace = |
| portType.getQName().getNamespaceURI(); |
| Parameters parms = getOperationParameters(operation, |
| namespace, bEntry); |
| parameters.put(operation, parms); |
| } |
| |
| bEntry.setParameters(parameters); |
| } |
| } |
| } |
| } // populateParameters |
| |
| /** |
| * For the given operation, this method returns the parameter info conveniently collated. |
| * There is a bit of processing that is needed to write the interface, stub, and skeleton. |
| * Rather than do that processing 3 times, it is done once, here, and stored in the |
| * Parameters object. |
| * |
| * @param operation |
| * @param namespace |
| * @param bindingEntry |
| * @return |
| * @throws IOException |
| */ |
| public Parameters getOperationParameters( |
| Operation operation, String namespace, BindingEntry bindingEntry) |
| throws IOException { |
| |
| Parameters parameters = new Parameters(); |
| |
| // The input and output Vectors of Parameters |
| Vector inputs = new Vector(); |
| Vector outputs = new Vector(); |
| List parameterOrder = operation.getParameterOrdering(); |
| |
| // Handle parameterOrder="", which is techinically illegal |
| if ((parameterOrder != null) && parameterOrder.isEmpty()) { |
| parameterOrder = null; |
| } |
| |
| Input input = operation.getInput(); |
| Output output = operation.getOutput(); |
| |
| parameters.mep = operation.getStyle(); |
| |
| // All input parts MUST be in the parameterOrder list. It is an error otherwise. |
| if (parameterOrder != null && !wrapped) { |
| if (input != null) { |
| Message inputMsg = input.getMessage(); |
| Map allInputs = inputMsg.getParts(); |
| Collection orderedInputs = |
| inputMsg.getOrderedParts(parameterOrder); |
| |
| if (allInputs.size() != orderedInputs.size()) { |
| throw new IOException( |
| Messages.getMessage("emitFail00", operation.getName())); |
| } |
| } |
| } |
| |
| boolean literalInput = false; |
| boolean literalOutput = false; |
| |
| if (bindingEntry != null) { |
| literalInput = (bindingEntry.getInputBodyType(operation) |
| == Use.LITERAL); |
| literalOutput = (bindingEntry.getOutputBodyType(operation) |
| == Use.LITERAL); |
| } |
| |
| // Collect all the input parameters |
| if ((input != null) && (input.getMessage() != null)) { |
| getParametersFromParts(inputs, |
| input.getMessage().getOrderedParts(null), |
| literalInput, operation.getName(), |
| bindingEntry); |
| } |
| |
| // Collect all the output parameters |
| if ((output != null) && (output.getMessage() != null)) { |
| getParametersFromParts(outputs, |
| output.getMessage().getOrderedParts(null), |
| literalOutput, operation.getName(), |
| bindingEntry); |
| } |
| |
| if (parameterOrder != null && !wrapped) { |
| |
| // Construct a list of the parameters in the parameterOrder list, determining the |
| // mode of each parameter and preserving the parameterOrder list. |
| for (int i = 0; i < parameterOrder.size(); ++i) { |
| String name = (String) parameterOrder.get(i); |
| |
| // index in the inputs Vector of the given name, -1 if it doesn't exist. |
| int index = getPartIndex(name, inputs); |
| |
| // index in the outputs Vector of the given name, -1 if it doesn't exist. |
| int outdex = getPartIndex(name, outputs); |
| |
| if (index >= 0) { |
| |
| // The mode of this parameter is either in or inout |
| addInishParm(inputs, outputs, index, outdex, parameters, |
| true); |
| } else if (outdex >= 0) { |
| addOutParm(outputs, outdex, parameters, true); |
| } else { |
| System.err.println(Messages.getMessage("noPart00", name)); |
| } |
| } |
| } |
| |
| // Some special case logic for JAX-RPC, but also to make things |
| // nicer for the user. |
| // If we have a single input and output with the same name |
| // instead of: void echo(StringHolder inout) |
| // Do this: string echo(string in) |
| if (wrapped && (inputs.size() == 1) && (outputs.size() == 1) |
| && |
| Utils.getLastLocalPart(((Parameter) inputs.get(0)).getName()).equals( |
| Utils.getLastLocalPart(((Parameter) outputs.get(0)).getName())) |
| ) { |
| |
| // add the input and make sure its a IN not an INOUT |
| addInishParm(inputs, null, 0, -1, parameters, false); |
| } else { |
| |
| // Get the mode info about those parts that aren't in the |
| // parameterOrder list. Since they're not in the parameterOrder list, |
| // the order is, first all in (and inout) parameters, then all out |
| // parameters, in the order they appear in the messages. |
| for (int i = 0; i < inputs.size(); i++) { |
| Parameter p = (Parameter) inputs.get(i); |
| int outdex = getPartIndex(p.getName(), outputs); |
| |
| addInishParm(inputs, outputs, i, outdex, parameters, false); |
| } |
| } |
| |
| // Now that the remaining in and inout parameters are collected, |
| // determine the status of outputs. If there is only 1, then it |
| // is the return value. If there are more than 1, then they are |
| // out parameters. |
| if (outputs.size() == 1) { |
| parameters.returnParam = (Parameter) outputs.get(0); |
| |
| parameters.returnParam.setMode(Parameter.OUT); |
| |
| if (parameters.returnParam.getType() instanceof DefinedElement) { |
| parameters.returnParam.setQName( |
| parameters.returnParam.getType().getQName()); |
| } |
| |
| ++parameters.outputs; |
| } else { |
| for (int i = 0; i < outputs.size(); i++) { |
| addOutParm(outputs, i, parameters, false); |
| } |
| } |
| |
| parameters.faults = operation.getFaults(); |
| |
| // before we return the paramters, |
| // make sure we dont have a duplicate name |
| Vector used = new Vector(parameters.list.size()); |
| Iterator i = parameters.list.iterator(); |
| |
| while (i.hasNext()) { |
| Parameter parameter = (Parameter) i.next(); |
| int count = 2; |
| |
| while (used.contains(parameter.getName())) { |
| |
| // duplicate, add a suffix and try again |
| parameter.setName(parameter.getName() |
| + Integer.toString(count++)); |
| } |
| |
| used.add(parameter.getName()); |
| } |
| |
| return parameters; |
| } // parameters |
| |
| /** |
| * Return the index of the given name in the given Vector, -1 if it doesn't exist. |
| * |
| * @param name |
| * @param v |
| * @return |
| */ |
| private int getPartIndex(String name, Vector v) { |
| name = Utils.getLastLocalPart(name); |
| for (int i = 0; i < v.size(); i++) { |
| String paramName = ((Parameter) v.get(i)).getName(); |
| paramName = Utils.getLastLocalPart(paramName); |
| if (name.equals(paramName)) { |
| return i; |
| } |
| } |
| |
| return -1; |
| } // getPartIndex |
| |
| /** |
| * Add an in or inout parameter to the parameters object. |
| * |
| * @param inputs |
| * @param outputs |
| * @param index |
| * @param outdex |
| * @param parameters |
| * @param trimInput |
| */ |
| private void addInishParm(Vector inputs, Vector outputs, int index, |
| int outdex, Parameters parameters, |
| boolean trimInput) { |
| |
| Parameter p = (Parameter) inputs.get(index); |
| |
| // If this is an element, we want the XML to reflect the element name |
| // not the part name. Same check is made in addOutParam below. |
| if (p.getType() instanceof DefinedElement) { |
| DefinedElement de = (DefinedElement) p.getType(); |
| |
| p.setQName(de.getQName()); |
| } |
| |
| // If this is a collection we want the XML to reflect the type in |
| // the collection, not foo[unbounded]. |
| // Same check is made in addOutParam below. |
| if (p.getType() instanceof CollectionElement) { |
| p.setQName(p.getType().getRefType().getQName()); |
| } |
| |
| // Should we remove the given parameter type/name entries from the Vector? |
| if (trimInput) { |
| inputs.remove(index); |
| } |
| |
| // At this point we know the name and type of the parameter, and that it's at least an |
| // in parameter. Now check to see whether it's also in the outputs Vector. If it is, |
| // then it's an inout parameter. |
| if (outdex >= 0) { |
| Parameter outParam = (Parameter) outputs.get(outdex); |
| |
| TypeEntry paramEntry = p.getType(); |
| TypeEntry outParamEntry = outParam.getType(); |
| // String paramLastLocalPart = Utils.getLastLocalPart(paramEntry.getQName().getLocalPart()); |
| // String outParamLastLocalPart = Utils.getLastLocalPart(outParamEntry.getQName().getLocalPart()); |
| if (paramEntry.equals(outParamEntry)) { |
| outputs.remove(outdex); |
| p.setMode(Parameter.INOUT); |
| |
| ++parameters.inouts; |
| } |
| /* |
| else if (paramLastLocalPart.equals(outParamLastLocalPart)) { |
| outputs.remove(outdex); |
| p.setMode(Parameter.INOUT); |
| |
| ++parameters.inouts; |
| if (paramEntry.isBaseType()) { |
| if (paramEntry.getBaseType().equals(outParamEntry.getBaseType())) { |
| outputs.remove(outdex); |
| p.setMode(Parameter.INOUT); |
| |
| ++parameters.inouts; |
| } |
| } |
| else if (paramEntry.getRefType() != null) { |
| if (paramEntry.getRefType().equals(outParamEntry.getRefType())) { |
| outputs.remove(outdex); |
| p.setMode(Parameter.INOUT); |
| |
| ++parameters.inouts; |
| } |
| } |
| else { |
| ++parameters.inputs; |
| } |
| } |
| */ |
| else { |
| |
| // If we're here, we have both an input and an output |
| // part with the same name but different types.... guess |
| // it's not really an inout.... |
| // |
| // throw new IOException(Messages.getMessage("differentTypes00", |
| // new String[] { p.getName(), |
| // p.getType().getQName().toString(), |
| // outParam.getType().getQName().toString() |
| // } |
| // )); |
| // There is some controversy about this, and the specs are |
| // a bit vague about what should happen if the types don't |
| // agree. Throwing an error is not correct with document/lit |
| // operations, as part names get resused (i.e. "body"). |
| // See WSDL 1.1 section 2.4.6, |
| // WSDL 1.2 working draft 9 July 2002 section 2.3.1 |
| ++parameters.inputs; |
| } |
| } else { |
| ++parameters.inputs; |
| } |
| |
| parameters.list.add(p); |
| } // addInishParm |
| |
| /** |
| * Add an output parameter to the parameters object. |
| * |
| * @param outputs |
| * @param outdex |
| * @param parameters |
| * @param trim |
| */ |
| private void addOutParm(Vector outputs, int outdex, Parameters parameters, |
| boolean trim) { |
| |
| Parameter p = (Parameter) outputs.get(outdex); |
| |
| // If this is an element, we want the XML to reflect the element name |
| // not the part name. Same check is made in addInishParam above. |
| if (p.getType() instanceof DefinedElement) { |
| DefinedElement de = (DefinedElement) p.getType(); |
| |
| p.setQName(de.getQName()); |
| } |
| |
| // If this is a collection we want the XML to reflect the type in |
| // the collection, not foo[unbounded]. |
| // Same check is made in addInishParam above. |
| if (p.getType() instanceof CollectionElement) { |
| p.setQName(p.getType().getRefType().getQName()); |
| } |
| |
| if (trim) { |
| outputs.remove(outdex); |
| } |
| |
| p.setMode(Parameter.OUT); |
| |
| ++parameters.outputs; |
| |
| parameters.list.add(p); |
| } // addOutParm |
| |
| /** |
| * This method returns a vector containing Parameters which represent |
| * each Part (shouldn't we call these "Parts" or something?) |
| * |
| * This routine does the wrapped doc/lit processing. |
| * It is also used for generating Faults, and this really confuses things |
| * but we need to do the same processing for the fault messages. |
| * |
| * This whole method is waaaay too complex. |
| * It needs rewriting (for instance, we sometimes new up |
| * a Parameter, then ignore it in favor of another we new up.) |
| * |
| * @param v The output vector of parameters |
| * @param parts The parts of the message |
| * @param literal Are we in a literal operation (or fault)? |
| * @param opName The operation (or fault) name |
| * @param bindingEntry The binding for this operation - can be NULL if we are looking at a fault |
| * @throws IOException when encountering an error in the WSDL |
| */ |
| public void getParametersFromParts(Vector v, |
| Collection parts, |
| boolean literal, |
| String opName, |
| BindingEntry bindingEntry) |
| throws IOException { |
| |
| // Determine if there's only one element. For wrapped |
| // style, we normally only have 1 part which is an |
| // element. But with MIME we could have any number of |
| // types along with that single element. As long as |
| // there's only ONE element, and it's the same name as |
| // the operation, we can unwrap it. |
| int numberOfElements = 0; |
| boolean possiblyWrapped = false; |
| Iterator i = parts.iterator(); |
| |
| while (i.hasNext()) { |
| Part part = (Part) i.next(); |
| |
| if (part.getElementName() != null) { |
| ++numberOfElements; |
| |
| if (part.getElementName().getLocalPart().equals(opName)) { |
| possiblyWrapped = true; |
| } |
| } |
| } |
| |
| // Try to sense "wrapped" document literal mode |
| // if we haven't been told not to. |
| // Criteria: |
| // - If there is a single element part, |
| // - That part is an element |
| // - That element has the same name as the operation |
| // - That element has no attributes (check done below) |
| |
| if (!nowrap && literal && (numberOfElements == 1) && possiblyWrapped) { |
| wrapped = true; |
| } |
| |
| i = parts.iterator(); |
| |
| while (i.hasNext()) { |
| Parameter param = new Parameter(); |
| Part part = (Part) i.next(); |
| QName elementName = part.getElementName(); |
| QName typeName = part.getTypeName(); |
| String partName = part.getName(); |
| |
| // if we are either: |
| // 1. encoded |
| // 2. literal & not wrapped. |
| if (!literal || !wrapped || (elementName == null)) { |
| param.setName(partName); |
| |
| // Add this type or element name |
| if (typeName != null) { |
| param.setType(getType(typeName)); |
| } else if (elementName != null) { |
| |
| // Just an FYI: The WSDL spec says that for use=encoded |
| // that parts reference an abstract type using the type attr |
| // but we kinda do the right thing here, so let it go. |
| // if (!literal) |
| // error... |
| param.setType(getElement(elementName)); |
| } else { |
| |
| // no type or element |
| throw new IOException( |
| Messages.getMessage( |
| "noTypeOrElement00", new String[]{partName, |
| opName})); |
| } |
| |
| fillParamInfo(param, bindingEntry, opName, partName); |
| v.add(param); |
| |
| continue; // next part |
| } |
| |
| // flow to here means wrapped literal ! |
| // See if we can map all the XML types to java(?) types |
| // if we can, we use these as the types |
| Node node = null; |
| TypeEntry typeEntry = null; |
| |
| if ((typeName != null) |
| && (bindingEntry == null || bindingEntry.getMIMETypes().size() == 0)) { |
| |
| // Since we can't (yet?) make the Axis engine generate the right |
| // XML for literal parts that specify the type attribute, |
| // (unless they're MIME types) abort processing with an |
| // error if we encounter this case |
| // |
| // node = getTypeEntry(typeName, false).getNode(); |
| String bindingName = (bindingEntry == null) |
| ? "unknown" |
| : bindingEntry.getBinding().getQName().toString(); |
| |
| throw new IOException(Messages.getMessage("literalTypePart00", |
| new String[]{ |
| partName, |
| opName, |
| bindingName})); |
| } |
| |
| // Get the node which corresponds to the type entry for this |
| // element. i.e.: |
| // <part name="part" element="foo:bar"/> |
| // ... |
| // <schema targetNamespace="foo"> |
| // <element name="bar"...> <--- This one |
| typeEntry = getTypeEntry(elementName, true); |
| node = typeEntry.getNode(); |
| |
| // Check if this element is of the form: |
| // <element name="foo" type="tns:foo_type"/> |
| BooleanHolder forElement = new BooleanHolder(); |
| QName type = Utils.getTypeQName(node, forElement, |
| false); |
| if ((type != null) && !forElement.value) { |
| |
| // If in fact we have such a type, go get the node that |
| // corresponds to THAT definition. |
| typeEntry = getTypeEntry(type, false); |
| node = typeEntry.getNode(); |
| } |
| |
| Vector vTypes = null; |
| |
| // If we have nothing at this point, we're in trouble. |
| if (node == null) { |
| // If node is null, that means the element in question has no type declaration, |
| // therefore is not a wrapper element. |
| wrapped = false; |
| if (verbose) { |
| System.out.println(Messages.getMessage("cannotDoWrappedMode00", elementName.toString())); |
| } |
| } else { |
| |
| // check for attributes |
| if (typeEntry.getContainedAttributes() != null) { |
| // can't do wrapped mode |
| wrapped = false; |
| } |
| |
| if (!SchemaUtils.isWrappedType(node)) { |
| // mark the type entry as not just literal referenced |
| // This doesn't work, but it may help in the future. |
| // The problem is "wrapped" is a symbol table wide flag, |
| // which means if one operation breaks the rules |
| // implemented in isWrappedType(), then everything goes bad |
| // For example, see bug Axis-1900. |
| typeEntry.setOnlyLiteralReference(false); |
| wrapped = false; |
| } |
| |
| // Get the nested type entries. |
| // TODO - If we are unable to represent any of the types in the |
| // element, we need to use SOAPElement/SOAPBodyElement. |
| // I don't believe getContainedElementDecl does the right thing yet. |
| vTypes = typeEntry.getContainedElements(); |
| } |
| |
| // IF we got the type entries and we didn't find attributes |
| // THEN use the things in this element as the parameters |
| if ((vTypes != null) && wrapped) { |
| |
| // add the elements in this list |
| for (int j = 0; j < vTypes.size(); j++) { |
| ElementDecl elem = (ElementDecl) vTypes.elementAt(j); |
| Parameter p = new Parameter(); |
| |
| p.setQName(elem.getQName()); |
| // If the parameter is a anonymous complex type, the parameter |
| // name should just be the name of the element, not >foo>element |
| String paramName = p.getName(); |
| final int gt = paramName.lastIndexOf(ANON_TOKEN); |
| if (gt != 1) { |
| paramName = paramName.substring(gt+1); |
| } |
| p.setName(paramName); |
| p.setType(elem.getType()); |
| p.setOmittable(elem.getMinOccursIs0()); |
| p.setNillable(elem.getNillable()); |
| fillParamInfo(p, bindingEntry, opName, partName); |
| v.add(p); |
| } |
| } else { |
| |
| // - we were unable to get the types OR |
| // - we found attributes |
| // so we can't use wrapped mode. |
| param.setName(partName); |
| |
| if (typeName != null) { |
| param.setType(getType(typeName)); |
| } else if (elementName != null) { |
| param.setType(getElement(elementName)); |
| } |
| |
| fillParamInfo(param, bindingEntry, opName, partName); |
| v.add(param); |
| } |
| } // while |
| } // getParametersFromParts |
| |
| /** |
| * Set the header information for this paramter |
| * |
| * @param param Parameter to modify |
| * @param bindingEntry Binding info for this operation/parameter |
| * @param opName the operation we are processing |
| * @param partName the part we are processing |
| */ |
| private void fillParamInfo(Parameter param, BindingEntry bindingEntry, |
| String opName, String partName) { |
| |
| // If we don't have a binding, can't do anything |
| if (bindingEntry == null) |
| return; |
| |
| setMIMEInfo(param, bindingEntry.getMIMEInfo(opName, partName)); |
| |
| boolean isHeader = false; |
| |
| // Is this parameter in an Input header? |
| if (bindingEntry.isInHeaderPart(opName, partName)) { |
| isHeader = true; |
| param.setInHeader(true); |
| } |
| |
| // Is this parameter in an Output header? |
| if (bindingEntry.isOutHeaderPart(opName, partName)) { |
| isHeader = true; |
| param.setOutHeader(true); |
| } |
| |
| // If this parameter is part of a header, find the binding operation |
| // that we are processing and get the QName of the parameter. |
| if (isHeader && (bindingEntry.getBinding() != null)) { |
| List list = bindingEntry.getBinding().getBindingOperations(); |
| |
| for (int i = 0; (list != null) && (i < list.size()); i++) { |
| BindingOperation operation = (BindingOperation) list.get(i); |
| |
| if (operation.getName().equals(opName)) { |
| if (param.isInHeader()) { |
| QName qName = getBindedParameterName( |
| operation.getBindingInput().getExtensibilityElements(), |
| param); |
| |
| if(qName!= null) { |
| param.setQName(qName); |
| } |
| } else if (param.isOutHeader()) { |
| QName qName = getBindedParameterName( |
| operation.getBindingOutput().getExtensibilityElements(), |
| param); |
| |
| if(qName!= null) { |
| param.setQName(qName); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Method getBindedParameterName |
| * |
| * @param elements |
| * @param p |
| * @return |
| */ |
| private QName getBindedParameterName(List elements, Parameter p) { |
| |
| // If the parameter can either be in the message header or in the |
| // message body. |
| // When it is in the header, there may be a SOAPHeader (soap:header) |
| // with its part name. The namespace used is the one of the soap:header. |
| // When it is in the body, if there is a SOAPBody with its part name, |
| // the namespace used is the one of this soap:body. |
| // |
| // If the parameter is in the body and there is a soap:body with no parts, |
| // its namespace is used for the parameter. |
| QName paramName = null; |
| String defaultNamespace = null; |
| String parameterPartName = p.getName(); |
| |
| for (Iterator k = elements.iterator(); k.hasNext();) { |
| ExtensibilityElement element = (ExtensibilityElement) k.next(); |
| |
| if (element instanceof SOAPBody) { |
| SOAPBody bodyElement = (SOAPBody) element; |
| List parts = bodyElement.getParts(); |
| |
| if ((parts == null) || (parts.size() == 0)) { |
| defaultNamespace = bodyElement.getNamespaceURI(); |
| } else { |
| boolean found = false; |
| |
| for (Iterator l = parts.iterator(); l.hasNext();) { |
| Object o = l.next(); |
| |
| if(o instanceof String) { |
| if (parameterPartName.equals((String)o)) { |
| paramName = |
| new QName(bodyElement.getNamespaceURI(), |
| parameterPartName); |
| found = true; |
| break; |
| } |
| } |
| } |
| |
| if (found) { |
| break; |
| } |
| } |
| } else if (element instanceof SOAPHeader) { |
| SOAPHeader headerElement = (SOAPHeader) element; |
| String part = headerElement.getPart(); |
| |
| if (parameterPartName.equals(part)) { |
| paramName = new QName(headerElement.getNamespaceURI(), |
| parameterPartName); |
| break; |
| } |
| } |
| } |
| |
| if ((paramName == null) && (!p.isInHeader()) && (!p.isOutHeader())) { |
| if (defaultNamespace != null) { |
| paramName = new QName(defaultNamespace, parameterPartName); |
| } else { |
| paramName = p.getQName(); |
| } |
| } |
| |
| return paramName; |
| } |
| |
| /** |
| * Set the MIME type. This can be determine in one of two ways: |
| * 1. From WSDL 1.1 MIME constructs on the binding (passed in); |
| * 2. From AXIS-specific xml MIME types. |
| * |
| * @param p |
| * @param mimeInfo |
| */ |
| private void setMIMEInfo(Parameter p, MimeInfo mimeInfo) { |
| |
| // If there is no binding MIME construct (ie., the mimeType parameter is |
| // null), then get the MIME type from the AXIS-specific xml MIME type. |
| if (mimeInfo == null && p.getType() != null) { |
| QName mimeQName = p.getType().getQName(); |
| |
| if (mimeQName.getNamespaceURI().equals(Constants.NS_URI_XMLSOAP)) { |
| if (Constants.MIME_IMAGE.equals(mimeQName)) { |
| mimeInfo = new MimeInfo("image/jpeg", ""); |
| } else if (Constants.MIME_PLAINTEXT.equals(mimeQName)) { |
| mimeInfo = new MimeInfo("text/plain", ""); |
| } else if (Constants.MIME_MULTIPART.equals(mimeQName)) { |
| mimeInfo = new MimeInfo("multipart/related", ""); |
| } else if (Constants.MIME_SOURCE.equals(mimeQName)) { |
| mimeInfo = new MimeInfo("text/xml", ""); |
| } else if (Constants.MIME_OCTETSTREAM.equals(mimeQName)) { |
| mimeInfo = new MimeInfo("application/octet-stream", ""); |
| } |
| } |
| } |
| |
| p.setMIMEInfo(mimeInfo); |
| } // setMIMEType |
| |
| /** |
| * Populate the symbol table with all of the BindingEntry's from the Definition. |
| * |
| * @param def |
| * @throws IOException |
| */ |
| private void populateBindings(Definition def) throws IOException { |
| |
| Iterator i = def.getBindings().values().iterator(); |
| |
| while (i.hasNext()) { |
| Binding binding = (Binding) i.next(); |
| BindingEntry bEntry = new BindingEntry(binding); |
| |
| symbolTablePut(bEntry); |
| |
| Iterator extensibilityElementsIterator = |
| binding.getExtensibilityElements().iterator(); |
| |
| while (extensibilityElementsIterator.hasNext()) { |
| Object obj = extensibilityElementsIterator.next(); |
| |
| if (obj instanceof SOAPBinding) { |
| bEntry.setBindingType(BindingEntry.TYPE_SOAP); |
| |
| SOAPBinding sb = (SOAPBinding) obj; |
| String style = sb.getStyle(); |
| |
| if ("rpc".equalsIgnoreCase(style)) { |
| bEntry.setBindingStyle(Style.RPC); |
| } |
| } else if (obj instanceof HTTPBinding) { |
| HTTPBinding hb = (HTTPBinding) obj; |
| |
| if (hb.getVerb().equalsIgnoreCase("post")) { |
| bEntry.setBindingType(BindingEntry.TYPE_HTTP_POST); |
| } else { |
| bEntry.setBindingType(BindingEntry.TYPE_HTTP_GET); |
| } |
| } else if (obj instanceof SOAP12Binding) { |
| bEntry.setBindingType(BindingEntry.TYPE_SOAP); |
| |
| SOAP12Binding sb = (SOAP12Binding) obj; |
| String style = sb.getStyle(); |
| |
| if ("rpc".equalsIgnoreCase(style)) { |
| bEntry.setBindingStyle(Style.RPC); |
| } |
| } |
| } |
| |
| // Step through the binding operations, setting the following as appropriate: |
| // - hasLiteral |
| // - body types |
| // - mimeTypes |
| // - headers |
| HashMap attributes = new HashMap(); |
| List bindList = binding.getBindingOperations(); |
| HashMap faultMap = new HashMap(); // name to SOAPFault from WSDL4J |
| |
| for (Iterator opIterator = bindList.iterator(); |
| opIterator.hasNext();) { |
| BindingOperation bindOp = |
| (BindingOperation) opIterator.next(); |
| Operation operation = bindOp.getOperation(); |
| BindingInput bindingInput = bindOp.getBindingInput(); |
| BindingOutput bindingOutput = bindOp.getBindingOutput(); |
| String opName = bindOp.getName(); |
| |
| // First, make sure the binding operation matches a portType operation |
| String inputName = (bindingInput == null) |
| ? null |
| : bindingInput.getName(); |
| String outputName = (bindingOutput == null) |
| ? null |
| : bindingOutput.getName(); |
| |
| if (binding.getPortType().getOperation( |
| opName, inputName, outputName) == null) { |
| throw new IOException(Messages.getMessage("unmatchedOp", |
| new String[]{ |
| opName, |
| inputName, |
| outputName})); |
| } |
| |
| ArrayList faults = new ArrayList(); |
| |
| // input |
| if (bindingInput != null) { |
| if (bindingInput.getExtensibilityElements() != null) { |
| Iterator inIter = |
| bindingInput.getExtensibilityElements().iterator(); |
| |
| fillInBindingInfo(bEntry, operation, inIter, faults, |
| true); |
| } |
| } |
| |
| // output |
| if (bindingOutput != null) { |
| if (bindingOutput.getExtensibilityElements() != null) { |
| Iterator outIter = |
| bindingOutput.getExtensibilityElements().iterator(); |
| |
| fillInBindingInfo(bEntry, operation, outIter, faults, |
| false); |
| } |
| } |
| |
| // faults |
| faultsFromSOAPFault(binding, bindOp, operation, faults); |
| |
| // Add this fault name and info to the map |
| faultMap.put(bindOp, faults); |
| |
| Use inputBodyType = bEntry.getInputBodyType(operation); |
| Use outputBodyType = bEntry.getOutputBodyType(operation); |
| |
| // Associate the portType operation that goes with this binding |
| // with the body types. |
| attributes.put(bindOp.getOperation(), |
| new BindingEntry.OperationAttr(inputBodyType, |
| outputBodyType, faultMap)); |
| |
| // If the input or output body uses literal, flag the binding as using literal. |
| // NOTE: should I include faultBodyType in this check? |
| if ((inputBodyType == Use.LITERAL) |
| || (outputBodyType == Use.LITERAL)) { |
| bEntry.setHasLiteral(true); |
| } |
| |
| bEntry.setFaultBodyTypeMap(operation, faultMap); |
| } // binding operations |
| |
| bEntry.setFaults(faultMap); |
| } |
| } // populateBindings |
| |
| /** |
| * Fill in some binding information: bodyType, mimeType, header info. |
| * |
| * @param bEntry |
| * @param operation |
| * @param it |
| * @param faults |
| * @param input |
| * @throws IOException |
| */ |
| private void fillInBindingInfo( |
| BindingEntry bEntry, Operation operation, Iterator it, ArrayList faults, boolean input) |
| throws IOException { |
| |
| for (; it.hasNext();) { |
| Object obj = it.next(); |
| |
| if (obj instanceof SOAPBody) { |
| setBodyType(((SOAPBody) obj).getUse(), bEntry, operation, |
| input); |
| } else if (obj instanceof SOAPHeader) { |
| SOAPHeader header = (SOAPHeader) obj; |
| |
| setBodyType(header.getUse(), bEntry, operation, input); |
| |
| // Note, this only works for explicit headers - those whose |
| // parts come from messages used in the portType's operation |
| // input/output clauses - it does not work for implicit |
| // headers - those whose parts come from messages not used in |
| // the portType's operation's input/output clauses. I don't |
| // know what we're supposed to emit for implicit headers. |
| bEntry.setHeaderPart(operation.getName(), header.getPart(), |
| input |
| ? BindingEntry.IN_HEADER |
| : BindingEntry.OUT_HEADER); |
| |
| // Add any soap:headerFault info to the faults array |
| Iterator headerFaults = header.getSOAPHeaderFaults().iterator(); |
| |
| while (headerFaults.hasNext()) { |
| SOAPHeaderFault headerFault = |
| (SOAPHeaderFault) headerFaults.next(); |
| |
| faults.add(new FaultInfo(headerFault, this)); |
| } |
| } else if (obj instanceof MIMEMultipartRelated) { |
| bEntry.setBodyType( |
| operation, |
| addMIMETypes( |
| bEntry, (MIMEMultipartRelated) obj, operation), input); |
| } else if (obj instanceof SOAP12Body) { |
| setBodyType(((SOAP12Body) obj).getUse(), bEntry, operation, |
| input); |
| } else if (obj instanceof SOAP12Header) { |
| SOAP12Header header = (SOAP12Header) obj; |
| |
| setBodyType(header.getUse(), bEntry, operation, input); |
| |
| // Note, this only works for explicit headers - those whose |
| // parts come from messages used in the portType's operation |
| // input/output clauses - it does not work for implicit |
| // headers - those whose parts come from messages not used in |
| // the portType's operation's input/output clauses. I don't |
| // know what we're supposed to emit for implicit headers. |
| bEntry.setHeaderPart(operation.getName(), header.getPart(), |
| input |
| ? BindingEntry.IN_HEADER |
| : BindingEntry.OUT_HEADER); |
| |
| // Add any soap12:headerFault info to the faults array |
| Iterator headerFaults = header.getSOAP12HeaderFaults().iterator(); |
| |
| while (headerFaults.hasNext()) { |
| SOAP12HeaderFault headerFault = |
| (SOAP12HeaderFault) headerFaults.next(); |
| |
| faults.add(new FaultInfo(headerFault.getMessage(), headerFault.getPart(), |
| headerFault.getUse(), headerFault.getNamespaceURI(), this)); |
| } |
| } else if (obj instanceof UnknownExtensibilityElement) { |
| UnknownExtensibilityElement unkElement = |
| (UnknownExtensibilityElement) obj; |
| QName name = |
| unkElement.getElementType(); |
| |
| if (name.getNamespaceURI().equals(Constants.URI_DIME_WSDL) |
| && name.getLocalPart().equals("message")) { |
| fillInDIMEInformation(unkElement, input, operation, bEntry); |
| } |
| } |
| } |
| } // fillInBindingInfo |
| |
| /** |
| * Fill in DIME information |
| * |
| * @param unkElement |
| * @param input |
| * @param operation |
| * @param bEntry |
| */ |
| private void fillInDIMEInformation(UnknownExtensibilityElement unkElement, |
| boolean input, Operation operation, |
| BindingEntry bEntry) { |
| |
| String layout = unkElement.getElement().getAttribute("layout"); |
| |
| // TODO: what to do with layout info? |
| if (layout.equals(Constants.URI_DIME_CLOSED_LAYOUT)) { |
| } else if (layout.equals(Constants.URI_DIME_OPEN_LAYOUT)) { |
| } |
| |
| Map parts = null; |
| |
| if (input) { |
| parts = operation.getInput().getMessage().getParts(); |
| } else { |
| parts = operation.getOutput().getMessage().getParts(); |
| } |
| |
| if (parts != null) { |
| Iterator iterator = parts.values().iterator(); |
| |
| while (iterator.hasNext()) { |
| Part part = (Part) iterator.next(); |
| |
| if (part != null) { |
| String dims = ""; |
| org.w3c.dom.Element element = null; |
| |
| if (part.getTypeName() != null) { |
| TypeEntry partType = getType(part.getTypeName()); |
| |
| if (partType.getDimensions().length() > 0) { |
| dims = partType.getDimensions(); |
| partType = partType.getRefType(); |
| } |
| |
| element = (org.w3c.dom.Element) partType.getNode(); |
| } else if (part.getElementName() != null) { |
| TypeEntry partElement = |
| getElement(part.getElementName()).getRefType(); |
| |
| element = (org.w3c.dom.Element) partElement.getNode(); |
| |
| QName name = getInnerCollectionComponentQName(element); |
| |
| if (name != null) { |
| dims += "[]"; |
| partElement = getType(name); |
| element = |
| (org.w3c.dom.Element) partElement.getNode(); |
| } else { |
| name = getInnerTypeQName(element); |
| |
| if (name != null) { |
| partElement = getType(name); |
| element = |
| (org.w3c.dom.Element) partElement.getNode(); |
| } |
| } |
| } |
| |
| if (element != null) { |
| org.w3c.dom.Element e = |
| (org.w3c.dom.Element) XMLUtils.findNode( |
| element, |
| new QName( |
| Constants.URI_DIME_CONTENT, "mediaType")); |
| |
| if (e != null) { |
| String value = e.getAttribute("value"); |
| |
| bEntry.setOperationDIME(operation.getName()); |
| bEntry.setMIMEInfo(operation.getName(), |
| part.getName(), value, dims); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Get the faults from the soap:fault clause. |
| * |
| * @param binding |
| * @param bindOp |
| * @param operation |
| * @param faults |
| * @throws IOException |
| */ |
| private void faultsFromSOAPFault( |
| Binding binding, BindingOperation bindOp, Operation operation, ArrayList faults) |
| throws IOException { |
| |
| Iterator faultMapIter = bindOp.getBindingFaults().values().iterator(); |
| |
| for (; faultMapIter.hasNext();) { |
| BindingFault bFault = (BindingFault) faultMapIter.next(); |
| |
| // Set default entry for this fault |
| String faultName = bFault.getName(); |
| |
| // Check to make sure this fault is named |
| if ((faultName == null) || (faultName.length() == 0)) { |
| throw new IOException( |
| Messages.getMessage( |
| "unNamedFault00", bindOp.getName(), |
| binding.getQName().toString())); |
| } |
| |
| boolean foundSOAPFault = false; |
| String soapFaultUse = ""; |
| String soapFaultNamespace = ""; |
| Iterator faultIter = |
| bFault.getExtensibilityElements().iterator(); |
| |
| for (; faultIter.hasNext();) { |
| Object obj = faultIter.next(); |
| |
| if (obj instanceof SOAPFault) { |
| foundSOAPFault = true; |
| soapFaultUse = ((SOAPFault) obj).getUse(); |
| soapFaultNamespace = ((SOAPFault) obj).getNamespaceURI(); |
| |
| break; |
| } else if (obj instanceof SOAP12Fault) { |
| foundSOAPFault = true; |
| soapFaultUse = ((SOAP12Fault) obj).getUse(); |
| soapFaultNamespace = ((SOAP12Fault) obj).getNamespaceURI(); |
| |
| break; |
| } |
| } |
| |
| // Check to make sure we have a soap:fault element |
| if (!foundSOAPFault) { |
| throw new IOException( |
| Messages.getMessage( |
| "missingSoapFault00", faultName, bindOp.getName(), |
| binding.getQName().toString())); |
| } |
| |
| // TODO error checking: |
| // if use=literal, no use of namespace on the soap:fault |
| // if use=encoded, no use of element on the part |
| // Check this fault to make sure it matches the one |
| // in the matching portType Operation |
| Fault opFault = operation.getFault(bFault.getName()); |
| |
| if (opFault == null) { |
| throw new IOException(Messages.getMessage("noPortTypeFault", |
| new String[]{ |
| bFault.getName(), |
| bindOp.getName(), |
| binding.getQName().toString()})); |
| } |
| |
| // put the updated entry back in the map |
| faults.add(new FaultInfo(opFault, Use.getUse(soapFaultUse), |
| soapFaultNamespace, this)); |
| } |
| } // faultsFromSOAPFault |
| |
| /** |
| * Set the body type. |
| * |
| * @param use |
| * @param bEntry |
| * @param operation |
| * @param input |
| */ |
| private void setBodyType(String use, |
| BindingEntry bEntry, |
| Operation operation, |
| boolean input) |
| { |
| |
| if (use == null) { |
| // Deprecated |
| // throw new IOException(Messages.getMessage("noUse", |
| // operation.getName())); |
| // for WS-I BP 1.0 R2707. |
| // Set default of use to literal. |
| use = "literal"; |
| } |
| |
| if (use.equalsIgnoreCase("literal")) { |
| bEntry.setBodyType(operation, Use.LITERAL, input); |
| } |
| } // setBodyType |
| |
| /** |
| * Add the parts that are really MIME types as MIME types. |
| * A side effect is to return the body Type of the given |
| * MIMEMultipartRelated object. |
| * |
| * @param bEntry |
| * @param mpr |
| * @param op |
| * @return |
| * @throws IOException |
| */ |
| private Use addMIMETypes( |
| BindingEntry bEntry, MIMEMultipartRelated mpr, Operation op) |
| throws IOException { |
| |
| Use bodyType = Use.ENCODED; |
| List parts = mpr.getMIMEParts(); |
| Iterator i = parts.iterator(); |
| |
| while (i.hasNext()) { |
| MIMEPart part = (MIMEPart) i.next(); |
| List elems = part.getExtensibilityElements(); |
| Iterator j = elems.iterator(); |
| |
| while (j.hasNext()) { |
| Object obj = j.next(); |
| |
| if (obj instanceof MIMEContent) { |
| MIMEContent content = (MIMEContent) obj; |
| TypeEntry typeEntry = findPart(op, content.getPart()); |
| if (typeEntry == null) { |
| throw new RuntimeException(Messages.getMessage("cannotFindPartForOperation00", content.getPart(), |
| op.getName(), content.getType())); |
| } |
| String dims = typeEntry.getDimensions(); |
| |
| if ((dims.length() <= 0) |
| && (typeEntry.getRefType() != null)) { |
| Node node = typeEntry.getRefType().getNode(); |
| |
| if (getInnerCollectionComponentQName(node) != null) { |
| dims += "[]"; |
| } |
| } |
| |
| String type = content.getType(); |
| |
| if ((type == null) || (type.length() == 0)) { |
| type = "text/plain"; |
| } |
| |
| bEntry.setMIMEInfo(op.getName(), content.getPart(), type, |
| dims); |
| } else if (obj instanceof SOAPBody) { |
| String use = ((SOAPBody) obj).getUse(); |
| |
| if (use == null) { |
| throw new IOException( |
| Messages.getMessage("noUse", op.getName())); |
| } |
| |
| if (use.equalsIgnoreCase("literal")) { |
| bodyType = Use.LITERAL; |
| } |
| } else if (obj instanceof SOAP12Body) { |
| String use = ((SOAP12Body) obj).getUse(); |
| |
| if (use == null) { |
| throw new IOException( |
| Messages.getMessage("noUse", op.getName())); |
| } |
| |
| if (use.equalsIgnoreCase("literal")) { |
| bodyType = Use.LITERAL; |
| } |
| } |
| } |
| } |
| |
| return bodyType; |
| } // addMIMETypes |
| |
| /** |
| * Method findPart |
| * |
| * @param operation |
| * @param partName |
| * @return |
| */ |
| private TypeEntry findPart(Operation operation, String partName) { |
| |
| Map parts = operation.getInput().getMessage().getParts(); |
| Iterator iterator = parts.values().iterator(); |
| TypeEntry part = findPart(iterator, partName); |
| |
| if (part == null) { |
| parts = operation.getOutput().getMessage().getParts(); |
| iterator = parts.values().iterator(); |
| part = findPart(iterator, partName); |
| } |
| |
| return part; |
| } |
| |
| /** |
| * Method findPart |
| * |
| * @param iterator |
| * @param partName |
| * @return |
| */ |
| private TypeEntry findPart(Iterator iterator, String partName) { |
| |
| while (iterator.hasNext()) { |
| Part part = (Part) iterator.next(); |
| |
| if (part != null) { |
| String typeName = part.getName(); |
| |
| if (partName.equals(typeName)) { |
| if (part.getTypeName() != null) { |
| return getType(part.getTypeName()); |
| } else if (part.getElementName() != null) { |
| return getElement(part.getElementName()); |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Populate the symbol table with all of the ServiceEntry's from the Definition. |
| * |
| * @param def |
| * @throws IOException |
| */ |
| |
| private void populateServices(Definition def) throws IOException { |
| String originalName = null; |
| Iterator i = def.getServices().values().iterator(); |
| |
| while (i.hasNext()) { |
| Service service = (Service) i.next(); |
| originalName = service.getQName().getLocalPart(); |
| // do a bit of name validation |
| if ((service.getQName() == null) |
| || (service.getQName().getLocalPart() == null) |
| || service.getQName().getLocalPart().equals("")) { |
| throw new IOException(Messages.getMessage("BadServiceName00")); |
| } |
| |
| // behave as though backslashes were never there |
| service.setQName(BackslashUtil.getQNameWithBackslashlessLocal(service.getQName())); |
| ServiceEntry sEntry = new ServiceEntry(service); |
| // we'll need this later when it is time print a backslash escaped service name |
| sEntry.setOriginalServiceName(originalName); |
| symbolTablePut(sEntry); |
| populatePorts(service.getPorts()); |
| } |
| } // populateServices |
| |
| |
| /** |
| * populates the symbol table with port elements defined within a <service> |
| * element. |
| * |
| * @param ports a map of name->port pairs (i.e. what is returned by service.getPorts() |
| * @throws IOException thrown, if an IO or WSDL error is detected |
| * @see javax.wsdl.Service#getPorts() |
| * @see javax.wsdl.Port |
| */ |
| private void populatePorts(Map ports) throws IOException { |
| |
| if (ports == null) { |
| return; |
| } |
| |
| Iterator it = ports.values().iterator(); |
| |
| while (it.hasNext()) { |
| Port port = (Port) it.next(); |
| String portName = port.getName(); |
| Binding portBinding = port.getBinding(); |
| |
| // make sure there is a port name. The 'name' attribute for WSDL ports is |
| // mandatory |
| // |
| if (portName == null) { |
| |
| // REMIND: should rather be a javax.wsdl.WSDLException ? |
| throw new IOException( |
| Messages.getMessage("missingPortNameException")); |
| } |
| |
| // make sure there is a binding for the port. The 'binding' attribute for |
| // WSDL ports is mandatory |
| // |
| if (portBinding == null) { |
| |
| // REMIND: should rather be a javax.wsdl.WSDLException ? |
| throw new IOException( |
| Messages.getMessage("missingBindingException")); |
| } |
| |
| // make sure the port name is unique among all port names defined in this |
| // WSDL document. |
| // |
| // NOTE: there's a flaw in com.ibm.wsdl.xml.WSDLReaderImpl#parsePort() and |
| // com.ibm.wsdl.xml.WSDLReaderImpl#addPort(). These methods do not enforce |
| // the port name exists and is unique. Actually, if two port definitions with |
| // the same name exist within the same service element, only *one* port |
| // element is present after parsing and the following exception is not thrown. |
| // |
| // If two ports with the same name exist in different service elements, |
| // the exception below is thrown. This is conformant to the WSDL 1.1 spec (sec 2.6) |
| // , which states: "The name attribute provides a unique name among all ports |
| // defined within in the enclosing WSDL document." |
| // |
| // |
| if (existsPortWithName(new QName(portName))) { |
| |
| // REMIND: should rather be a javax.wsdl.WSDLException ? |
| throw new IOException( |
| Messages.getMessage("twoPortsWithSameName", portName)); |
| } |
| |
| PortEntry portEntry = new PortEntry(port); |
| |
| symbolTablePut(portEntry); |
| } |
| } |
| |
| /** |
| * Set each SymTabEntry's isReferenced flag. The default is false. If no other symbol |
| * references this symbol, then leave it false, otherwise set it to true. |
| * (An exception to the rule is that derived types are set as referenced if |
| * their base type is referenced. This is necessary to support generation and |
| * registration of derived types.) |
| * |
| * @param def |
| * @param doc |
| */ |
| private void setReferences(Definition def, Document doc) { |
| |
| Map stuff = def.getServices(); |
| |
| if (stuff.isEmpty()) { |
| stuff = def.getBindings(); |
| |
| if (stuff.isEmpty()) { |
| stuff = def.getPortTypes(); |
| |
| if (stuff.isEmpty()) { |
| stuff = def.getMessages(); |
| |
| if (stuff.isEmpty()) { |
| for (Iterator i = elementTypeEntries.values().iterator(); |
| i.hasNext();) { |
| setTypeReferences((TypeEntry) i.next(), doc, false); |
| } |
| |
| for (Iterator i = typeTypeEntries.values().iterator(); |
| i.hasNext();) { |
| setTypeReferences((TypeEntry) i.next(), doc, false); |
| } |
| } else { |
| Iterator i = stuff.values().iterator(); |
| |
| while (i.hasNext()) { |
| Message message = (Message) i.next(); |
| MessageEntry mEntry = |
| getMessageEntry(message.getQName()); |
| |
| setMessageReferences(mEntry, def, doc, false); |
| } |
| } |
| } else { |
| Iterator i = stuff.values().iterator(); |
| |
| while (i.hasNext()) { |
| PortType portType = (PortType) i.next(); |
| PortTypeEntry ptEntry = |
| getPortTypeEntry(portType.getQName()); |
| |
| setPortTypeReferences(ptEntry, null, def, doc); |
| } |
| } |
| } else { |
| Iterator i = stuff.values().iterator(); |
| |
| while (i.hasNext()) { |
| Binding binding = (Binding) i.next(); |
| BindingEntry bEntry = getBindingEntry(binding.getQName()); |
| |
| setBindingReferences(bEntry, def, doc); |
| } |
| } |
| } else { |
| Iterator i = stuff.values().iterator(); |
| |
| while (i.hasNext()) { |
| Service service = (Service) i.next(); |
| ServiceEntry sEntry = getServiceEntry(service.getQName()); |
| |
| setServiceReferences(sEntry, def, doc); |
| } |
| } |
| } // setReferences |
| |
| /** |
| * Set the isReferenced flag to true on the given TypeEntry and all |
| * SymTabEntries that it refers to. |
| * |
| * @param entry |
| * @param doc |
| * @param literal |
| */ |
| private void setTypeReferences(TypeEntry entry, Document doc, |
| boolean literal) { |
| |
| // Check to see if already processed. |
| if ((entry.isReferenced() && !literal) |
| || (entry.isOnlyLiteralReferenced() && literal)) { |
| return; |
| } |
| |
| if (wrapped) { |
| |
| // If this type is ONLY referenced from a literal usage in a binding, |
| // then isOnlyLiteralReferenced should return true. |
| if (!entry.isReferenced() && literal) { |
| entry.setOnlyLiteralReference(true); |
| } |
| |
| // If this type was previously only referenced as a literal type, |
| // but now it is referenced in a non-literal manner, turn off the |
| // onlyLiteralReference flag. |
| else if (entry.isOnlyLiteralReferenced() && !literal) { |
| entry.setOnlyLiteralReference(false); |
| } |
| } |
| |
| // If we don't want to emit stuff from imported files, only set the |
| // isReferenced flag if this entry exists in the immediate WSDL file. |
| Node node = entry.getNode(); |
| |
| if (addImports || (node == null) || (node.getOwnerDocument() == doc)) { |
| entry.setIsReferenced(true); |
| |
| if (entry instanceof DefinedElement) { |
| BooleanHolder forElement = new BooleanHolder(); |
| QName referentName = Utils.getTypeQName(node, |
| forElement, false); |
| |
| if (referentName != null) { |
| TypeEntry referent = getTypeEntry(referentName, |
| forElement.value); |
| |
| if (referent != null) { |
| setTypeReferences(referent, doc, literal); |
| } |
| } |
| |
| // If the Defined Element has an anonymous type, |
| // process it with the current literal flag setting. |
| QName anonQName = |
| SchemaUtils.getElementAnonQName(entry.getNode()); |
| |
| if (anonQName != null) { |
| TypeEntry anonType = getType(anonQName); |
| |
| if (anonType != null) { |
| setTypeReferences(anonType, doc, literal); |
| |
| return; |
| } |
| } |
| } |
| } |
| |
| HashSet nestedTypes = entry.getNestedTypes(this, true); |
| Iterator it = nestedTypes.iterator(); |
| |
| while (it.hasNext()) { |
| TypeEntry nestedType = (TypeEntry) it.next(); |
| TypeEntry refType = entry.getRefType(); |
| |
| if (nestedType == null) { |
| continue; |
| } |
| |
| // If this entry has a referenced type that isn't |
| // the same as the nested type |
| // AND the OnlyLiteral reference switch is on |
| // THEN turn the OnlyLiteral reference switch off. |
| // If only someone had put a comment here saying why we do this... |
| if ((refType != null) |
| && !refType.equals(nestedType) |
| && nestedType.isOnlyLiteralReferenced()) { |
| nestedType.setOnlyLiteralReference(false); |
| } |
| |
| if (!nestedType.isReferenced()) { |
| |
| // setTypeReferences(nestedType, doc, literal); |
| if (nestedType != entry) { |
| setTypeReferences(nestedType, doc, false); |
| } |
| } |
| } |
| } // setTypeReferences |
| |
| /** |
| * Set the isReferenced flag to true on the given MessageEntry and all |
| * SymTabEntries that it refers to. |
| * |
| * @param entry |
| * @param def |
| * @param doc |
| * @param literal |
| */ |
| private void setMessageReferences(MessageEntry entry, Definition def, |
| Document doc, boolean literal) { |
| |
| // If we don't want to emit stuff from imported files, only set the |
| // isReferenced flag if this entry exists in the immediate WSDL file. |
| Message message = entry.getMessage(); |
| |
| if (addImports) { |
| entry.setIsReferenced(true); |
| } else { |
| |
| // NOTE: I thought I could have simply done: |
| // if (def.getMessage(message.getQName()) != null) |
| // but that method traces through all imported messages. |
| Map messages = def.getMessages(); |
| |
| if (messages.containsValue(message)) { |
| entry.setIsReferenced(true); |
| } |
| } |
| |
| // Set all the message's types |
| Iterator parts = message.getParts().values().iterator(); |
| |
| while (parts.hasNext()) { |
| Part part = (Part) parts.next(); |
| TypeEntry type = getType(part.getTypeName()); |
| |
| if (type != null) { |
| setTypeReferences(type, doc, literal); |
| } |
| |
| type = getElement(part.getElementName()); |
| |
| if (type != null) { |
| setTypeReferences(type, doc, literal); |
| |
| TypeEntry refType = type.getRefType(); |
| |
| if (refType != null) { |
| setTypeReferences(refType, doc, literal); |
| } |
| } |
| } |
| } // setMessageReference |
| |
| /** |
| * Set the isReferenced flag to true on the given PortTypeEntry and all |
| * SymTabEntries that it refers to. |
| * |
| * @param entry |
| * @param bEntry |
| * @param def |
| * @param doc |
| */ |
| private void setPortTypeReferences(PortTypeEntry entry, |
| BindingEntry bEntry, Definition def, |
| Document doc) { |
| |
| // If we don't want to emit stuff from imported files, only set the |
| // isReferenced flag if this entry exists in the immediate WSDL file. |
| PortType portType = entry.getPortType(); |
| |
| if (addImports) { |
| entry.setIsReferenced(true); |
| } else { |
| |
| // NOTE: I thought I could have simply done: |
| // if (def.getPortType(portType.getQName()) != null) |
| // but that method traces through all imported portTypes. |
| Map portTypes = def.getPortTypes(); |
| |
| if (portTypes.containsValue(portType)) { |
| entry.setIsReferenced(true); |
| } |
| } |
| |
| // Set all the portType's messages |
| Iterator operations = portType.getOperations().iterator(); |
| |
| // For each operation, query its input, output, and fault messages |
| while (operations.hasNext()) { |
| Operation operation = (Operation) operations.next(); |
| Input input = operation.getInput(); |
| Output output = operation.getOutput(); |
| |
| // Find out if this reference is a literal reference or not. |
| boolean literalInput = false; |
| boolean literalOutput = false; |
| |
| if (bEntry != null) { |
| literalInput = bEntry.getInputBodyType(operation) |
| == Use.LITERAL; |
| literalOutput = bEntry.getOutputBodyType(operation) |
| == Use.LITERAL; |
| } |
| |
| // Query the input message |
| if (input != null) { |
| Message message = input.getMessage(); |
| |
| if (message != null) { |
| MessageEntry mEntry = getMessageEntry(message.getQName()); |
| |
| if (mEntry != null) { |
| setMessageReferences(mEntry, def, doc, literalInput); |
| } |
| } |
| } |
| |
| // Query the output message |
| if (output != null) { |
| Message message = output.getMessage(); |
| |
| if (message != null) { |
| MessageEntry mEntry = getMessageEntry(message.getQName()); |
| |
| if (mEntry != null) { |
| setMessageReferences(mEntry, def, doc, literalOutput); |
| } |
| } |
| } |
| |
| // Query the fault messages |
| Iterator faults = operation.getFaults().values().iterator(); |
| |
| while (faults.hasNext()) { |
| Message message = ((Fault) faults.next()).getMessage(); |
| |
| if (message != null) { |
| MessageEntry mEntry = getMessageEntry(message.getQName()); |
| |
| if (mEntry != null) { |
| setMessageReferences(mEntry, def, doc, false); |
| } |
| } |
| } |
| } |
| } // setPortTypeReferences |
| |
| /** |
| * Set the isReferenced flag to true on the given BindingEntry and all |
| * SymTabEntries that it refers to ONLY if this binding is a SOAP binding. |
| * |
| * @param entry |
| * @param def |
| * @param doc |
| */ |
| private void setBindingReferences(BindingEntry entry, Definition def, |
| Document doc) { |
| |
| if (entry.getBindingType() == BindingEntry.TYPE_SOAP) { |
| |
| // If we don't want to emit stuff from imported files, only set the |
| // isReferenced flag if this entry exists in the immediate WSDL file. |
| Binding binding = entry.getBinding(); |
| |
| if (addImports) { |
| entry.setIsReferenced(true); |
| } else { |
| |
| // NOTE: I thought I could have simply done: |
| // if (def.getBindng(binding.getQName()) != null) |
| // but that method traces through all imported bindings. |
| Map bindings = def.getBindings(); |
| |
| if (bindings.containsValue(binding)) { |
| entry.setIsReferenced(true); |
| } |
| } |
| |
| // Set all the binding's portTypes |
| PortType portType = binding.getPortType(); |
| PortTypeEntry ptEntry = getPortTypeEntry(portType.getQName()); |
| |
| if (ptEntry != null) { |
| setPortTypeReferences(ptEntry, entry, def, doc); |
| } |
| } |
| } // setBindingReferences |
| |
| /** |
| * Set the isReferenced flag to true on the given ServiceEntry and all |
| * SymTabEntries that it refers to. |
| * |
| * @param entry |
| * @param def |
| * @param doc |
| */ |
| private void setServiceReferences(ServiceEntry entry, Definition def, |
| Document doc) { |
| |
| // If we don't want to emit stuff from imported files, only set the |
| // isReferenced flag if this entry exists in the immediate WSDL file. |
| Service service = entry.getService(); |
| |
| if (addImports) { |
| entry.setIsReferenced(true); |
| } else { |
| |
| // NOTE: I thought I could have simply done: |
| // if (def.getService(service.getQName()) != null) |
| // but that method traces through all imported services. |
| Map services = def.getServices(); |
| |
| if (services.containsValue(service)) { |
| entry.setIsReferenced(true); |
| } |
| } |
| |
| // Set all the service's bindings |
| Iterator ports = service.getPorts().values().iterator(); |
| |
| while (ports.hasNext()) { |
| Port port = (Port) ports.next(); |
| Binding binding = port.getBinding(); |
| |
| if (binding != null) { |
| BindingEntry bEntry = getBindingEntry(binding.getQName()); |
| |
| if (bEntry != null) { |
| setBindingReferences(bEntry, def, doc); |
| } |
| } |
| } |
| } // setServiceReferences |
| |
| /** |
| * Put the given SymTabEntry into the symbol table, if appropriate. |
| * |
| * @param entry |
| * @throws IOException |
| */ |
| private SymTabEntry symbolTablePut(SymTabEntry entry) throws IOException { |
| |
| QName name = entry.getQName(); |
| |
| SymTabEntry e = get(name, entry.getClass()); |
| |
| if (e == null) { |
| e = entry; |
| |
| // An entry of the given qname of the given type doesn't exist yet. |
| if ((entry instanceof Type) |
| && (get(name, UndefinedType.class) != null)) { |
| |
| // A undefined type exists in the symbol table, which means |
| // that the type is used, but we don't yet have a definition for |
| // the type. Now we DO have a definition for the type, so |
| // replace the existing undefined type with the real type. |
| if (((TypeEntry) get(name, UndefinedType.class)).isSimpleType() |
| && !((TypeEntry) entry).isSimpleType()) { |
| |
| // Problem if the undefined type was used in a |
| // simple type context. |
| throw new IOException( |
| Messages.getMessage( |
| "AttrNotSimpleType01", name.toString())); |
| } |
| |
| Vector v = (Vector) symbolTable.get(name); |
| |
| for (int i = 0; i < v.size(); ++i) { |
| Object oldEntry = v.elementAt(i); |
| |
| if (oldEntry instanceof UndefinedType) { |
| |
| // Replace it in the symbol table |
| v.setElementAt(entry, i); |
| |
| // Replace it in the types index |
| typeTypeEntries.put(name, entry); |
| |
| // Update all of the entries that refer to the unknown type |
| ((UndefinedType) oldEntry).update((Type) entry); |
| } |
| } |
| } else if ((entry instanceof Element) |
| && (get(name, UndefinedElement.class) != null)) { |
| |
| // A undefined element exists in the symbol table, which means |
| // that the element is used, but we don't yet have a definition for |
| // the element. Now we DO have a definition for the element, so |
| // replace the existing undefined element with the real element. |
| Vector v = (Vector) symbolTable.get(name); |
| |
| for (int i = 0; i < v.size(); ++i) { |
| Object oldEntry = v.elementAt(i); |
| |
| if (oldEntry instanceof UndefinedElement) { |
| |
| // Replace it in the symbol table |
| v.setElementAt(entry, i); |
| |
| // Replace it in the elements index |
| elementTypeEntries.put(name, entry); |
| |
| // Update all of the entries that refer to the unknown type |
| ((Undefined) oldEntry).update((Element) entry); |
| } |
| } |
| } else { |
| |
| // Add this entry to the symbol table |
| Vector v = (Vector) symbolTable.get(name); |
| |
| if (v == null) { |
| v = new Vector(); |
| |
| symbolTable.put(name, v); |
| } |
| |
| v.add(entry); |
| |
| // add TypeEntries to specialized indices for |
| // fast lookups during reference resolution. |
| if (entry instanceof Element) { |
| elementTypeEntries.put(name, entry); |
| } else if (entry instanceof Type) { |
| typeTypeEntries.put(name, entry); |
| } |
| } |
| } else { |
| if (!quiet) { |
| System.out.println(Messages.getMessage("alreadyExists00", |
| "" + name)); |
| } |
| } |
| |
| return e; |
| } // symbolTablePut |
| |
| /** |
| * checks whether there exists a WSDL port with a given name in the current |
| * symbol table |
| * |
| * @param name the QName of the port. Note: only the local part of the qname is relevant, |
| * since port names are not qualified with a namespace. They are of type nmtoken in WSDL 1.1 |
| * and of type ncname in WSDL 1.2 |
| * @return true, if there is a port element with the specified name; false, otherwise |
| */ |
| protected boolean existsPortWithName(QName name) { |
| |
| Vector v = (Vector) symbolTable.get(name); |
| |
| if (v == null) { |
| return false; |
| } |
| |
| Iterator it = v.iterator(); |
| |
| while (it.hasNext()) { |
| Object o = it.next(); |
| |
| if (o instanceof PortEntry) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Method getInnerCollectionComponentQName |
| * |
| * @param node |
| * @return |
| */ |
| private QName getInnerCollectionComponentQName(Node node) { |
| |
| if (node == null) { |
| return null; |
| } |
| |
| QName name = SchemaUtils.getCollectionComponentQName(node, new QNameHolder(), new BooleanHolder(), this); |
| |
| if (name != null) { |
| return name; |
| } |
| |
| // Dive into the node if necessary |
| NodeList children = node.getChildNodes(); |
| |
| for (int i = 0; i < children.getLength(); i++) { |
| name = getInnerCollectionComponentQName(children.item(i)); |
| |
| if (name != null) { |
| return name; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Method getInnerTypeQName |
| * |
| * @param node |
| * @return |
| */ |
| private static QName getInnerTypeQName(Node node) { |
| |
| if (node == null) { |
| return null; |
| } |
| |
| BooleanHolder forElement = new BooleanHolder(); |
| QName name = Utils.getTypeQName(node, forElement, true); |
| |
| if (name != null) { |
| return name; |
| } |
| |
| // Dive into the node if necessary |
| NodeList children = node.getChildNodes(); |
| |
| for (int i = 0; i < children.getLength(); i++) { |
| name = getInnerTypeQName(children.item(i)); |
| |
| if (name != null) { |
| return name; |
| } |
| } |
| |
| return null; |
| } |
| |
| protected void processTypes() { |
| for (Iterator i = typeTypeEntries.values().iterator(); i.hasNext(); ) { |
| Type type = (Type) i.next(); |
| Node node = type.getNode(); |
| |
| // Process the attributes |
| Vector attributes = |
| SchemaUtils.getContainedAttributeTypes(node, this); |
| |
| if (attributes != null) { |
| type.setContainedAttributes(attributes); |
| } |
| |
| // Process the elements |
| Vector elements = |
| SchemaUtils.getContainedElementDeclarations(node, this); |
| |
| if (elements != null) { |
| type.setContainedElements(elements); |
| } |
| } |
| } |
| |
| public List getMessageEntries() { |
| List messageEntries = new ArrayList(); |
| Iterator iter = symbolTable.values().iterator(); |
| while (iter.hasNext()) { |
| Vector v = (Vector)iter.next(); |
| for (int i = 0; i < v.size(); ++i) { |
| SymTabEntry entry = (SymTabEntry)v.elementAt(i); |
| if (entry instanceof MessageEntry) { |
| messageEntries.add(entry); |
| } |
| } |
| } |
| |
| return messageEntries; |
| } |
| |
| public void setWrapArrays(boolean wrapArrays) { |
| this.wrapArrays = wrapArrays; |
| } |
| |
| public Map getElementFormDefaults() { |
| return elementFormDefaults; |
| } |
| } |