Utils.java |
/* * The Apache Software License, Version 1.1 * * * Copyright (c) 2001-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Axis" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package org.jboss.axis.wsdl.symbolTable; import org.jboss.axis.Constants; import org.jboss.axis.utils.JavaUtils; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import javax.xml.namespace.QName; import javax.xml.rpc.holders.BooleanHolder; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Vector; /** * This class contains static utility methods for the emitter. * * @author Rich Scheuerle (scheu@us.ibm.com) * @author Tom Jordahl (tomj@macromedia.com) */ public class Utils { /** * cache of namespaces -> maps of localNames -> QNames */ static final Map nsmap = new HashMap(); /** * Find or create a QName with the specified namespace/localName. * * @param namespace * @param localName */ static QName findQName(String namespace, String localName) { QName qname = null; // get the inner map, using the namespace as a key Map ln2qn = (Map)nsmap.get(namespace); if (null == ln2qn) { // cache miss ln2qn = new HashMap(); nsmap.put(namespace, ln2qn); qname = new QName(namespace, localName); ln2qn.put(localName, qname); } else { // cache hit qname = (QName)ln2qn.get(localName); if (null == qname) { // cache miss qname = new QName(namespace, localName); ln2qn.put(localName, qname); } else { // cache hit } } return qname; } /** * getNillableQName returns the QName to use if the nillable=true * attribute is used. * For example, in JAX-RPC: * The QName "xsd:int" maps to a java int. * However if an element with a type="xsd:int" also has the * "nillable=true" attribute, the type should be an Integer (not an int). * So in these circumstances, this routine is called with xsd:int to * get a suitable qname (soapenc:int) which maps to Integer. * * @param qName QName */ public static QName getNillableQName(QName qName) { QName rc = qName; if (Constants.isSchemaXSD(rc.getNamespaceURI())) { String localName = rc.getLocalPart(); if (localName.equals("int") || localName.equals("long") || localName.equals("short") || localName.equals("float") || localName.equals("double") || localName.equals("boolean") || localName.equals("byte")) { rc = findQName(Constants.URI_DEFAULT_SOAP_ENC, qName.getLocalPart()); } else if (localName.equals("base64Binary")) { rc = findQName(Constants.URI_DEFAULT_SOAP_ENC, "base64"); } else if (localName.equals("hexBinary")) { rc = findQName(Constants.URI_DEFAULT_SCHEMA_XSD, "hexBinary"); } } return rc; } /** * Given a node, return the value of the given attribute. * If the attribute does not exist, searching continues through ancestor nodes until found. * This method is useful for finding attributes that pertain to a group of contained * nodes (i.e. xlmns, xmlns:tns, targetNamespace, name) */ public static String getScopedAttribute(Node node, String attr) { if (node == null) { return null; } if (node.getAttributes() == null) return getScopedAttribute(node.getParentNode(), attr); Node attrNode = node.getAttributes().getNamedItem(attr); if (attrNode != null) { return attrNode.getNodeValue(); } else { return getScopedAttribute(node.getParentNode(), attr); } } /** * Given a node, return the value of the given attribute. * Returns null if the attribute is not found */ public static String getAttribute(Node node, String attr) { if (node == null || node.getAttributes() == null) { return null; } Node attrNode = node.getAttributes().getNamedItem(attr); if (attrNode != null) { return attrNode.getNodeValue(); } else { return null; } } /** * Given a node, return the attributes that have the specified local name. * Returns null if the attribute is not found */ public static Vector getAttributesWithLocalName(Node node, String localName) { Vector v = new Vector(); if (node == null) { return v; } NamedNodeMap map = node.getAttributes(); if (map != null) { for (int i = 0; i < map.getLength(); i++) { Node attrNode = map.item(i); if (attrNode != null && attrNode.getLocalName().equals(localName)) { v.add(attrNode); } } } return v; } /** * An xml element may have a name. * For example <.element name="foo" type="b:bar">. * has the name "element". This routine gets the full QName of the element. */ public static QName getNodeQName(Node node) { if (node == null) { return null; } String localName = node.getLocalName(); if (localName == null) { return null; } String namespace = node.getNamespaceURI(); return (findQName(namespace, localName)); } /** * XML nodes may have a name attribute. * For example <.element name="foo" type="b:bar">. * has the name attribute value "foo". This routine gets the QName of the name attribute value. */ public static QName getNodeNameQName(Node node) { if (node == null) { return null; } String localName = null; String namespace = null; // First try to get the name directly localName = getAttribute(node, "name"); // If this fails and the node has a ref, use the ref name. if (localName == null) { QName ref = getTypeQNameFromAttr(node, "ref"); if (ref != null) { localName = ref.getLocalPart(); namespace = ref.getNamespaceURI(); } } // This routine may be called for complexType elements. In some cases, // the complexType may be anonymous, which is why the getScopedAttribute // method is used. if (localName == null) { localName = ""; Node search = node.getParentNode(); while (search != null) { QName kind = getNodeQName(search); if (kind.getLocalPart().equals("schema")) { search = null; } else if (kind.getLocalPart().equals("element") || kind.getLocalPart().equals("attribute")) { localName = SymbolTable.ANON_TOKEN + getNodeNameQName(search).getLocalPart(); search = search.getParentNode(); } else if (kind.getLocalPart().equals("complexType") || kind.getLocalPart().equals("simpleType")) { localName = getNodeNameQName(search).getLocalPart() + localName; search = null; } else { search = search.getParentNode(); } } } if (localName == null) return null; // Build and return the QName if (namespace == null) { namespace = getScopedAttribute(node, "targetNamespace"); } return (findQName(namespace, localName)); } /** * An XML element or attribute node has several ways of * identifying the type of the element or attribute: * - use the type attribute to reference a complexType/simpleType * - use the ref attribute to reference another element * - use of an anonymous type (i.e. a nested type underneath itself) * - a wsdl:part can use the element attribute. * - an extension can use the base attribute. * <p/> * This routine returns a QName representing this "type". * The forElement value is also returned to indicate whether the * QName represents an element (i.e. obtained using the ref attribute) * or a type. * <p/> * Other attributes affect the QName that is returned. * If the "minOccurs" and "maxOccurs" are set such that the * type is a collection of "types", then an artificial qname is * returned to represent the collection. * * @param node of the reference * @param forElement output parameter is set to true if QName is for an element * (i.e. ref= or element= attribute was used). * @param ignoreMaxOccurs indicates whether minOccurs/maxOccurs affects the QName * @return QName representing the type of this element */ public static QName getTypeQName(Node node, BooleanHolder forElement, boolean ignoreMaxOccurs) { if (node == null) return null; forElement.value = false; // Assume QName returned is for a type // Try getting the QName of the type attribute. // Note this also retrieves the type if an anonymous type is used. QName qName = getTypeQNameFromAttr(node, "type"); // If not successful, try using the ref attribute. if (qName == null) { forElement.value = true; qName = getTypeQNameFromAttr(node, "ref"); } // If the node has "type"/"ref" and "maxOccurs" then the type is really // a collection. There is no qname in the wsdl which we can use to represent // the collection, so we need to invent one. // The local part of the qname is changed to <local>[minOccurs, maxOccurs] // The namespace uri is changed to the targetNamespace of this node if (!ignoreMaxOccurs) { if (qName != null) { String maxOccursValue = getAttribute(node, "maxOccurs"); String minOccursValue = getAttribute(node, "minOccurs"); if (maxOccursValue == null) { maxOccursValue = "1"; } if (minOccursValue == null) { minOccursValue = "1"; } if (minOccursValue.equals("0") && maxOccursValue.equals("1")) { // If we have a minoccurs="0"/maxoccurs="1", this is just // like a nillable single value, so treat it as such. qName = getNillableQName(qName); } else if (!maxOccursValue.equals("1") || !minOccursValue.equals("1")) { String localPart = qName.getLocalPart(); localPart += "[" + maxOccursValue + "]"; qName = findQName(qName.getNamespaceURI(), localPart); } } } // A WSDL Part uses the element attribute instead of the ref attribute if (qName == null) { forElement.value = true; qName = getTypeQNameFromAttr(node, "element"); } // "base" references a "type" if (qName == null) { forElement.value = false; qName = getTypeQNameFromAttr(node, "base"); } return qName; } /** * Gets the QName of the type of the node via the specified attribute * name. * <p/> * If "type", the QName represented by the type attribute's value is * returned. If the type attribute is not set, the anonymous type * or anyType is returned if no other type information is available. * Note that the QName returned in these cases is affected by * the presence of the nillable attribute. * <p/> * If "ref", the QName represented by the ref attribute's value is * returned. * <p/> * If "base" or "element", the QName represented by the base/element * attribute's value is returned. * * @param node in the dom * @param typeAttrName (type, base, element, ref) */ private static QName getTypeQNameFromAttr(Node node, String typeAttrName) { if (node == null) { return null; } // Get the raw prefixed value String prefixedName = getAttribute(node, typeAttrName); // If "type" was specified but there is no type attribute, // check for an anonymous type. If no anonymous type // then the type is anyType. if (prefixedName == null && typeAttrName.equals("type")) { if (getAttribute(node, "ref") == null && getAttribute(node, "base") == null && getAttribute(node, "element") == null) { // Try getting the anonymous qname QName anonQName = SchemaUtils.getElementAnonQName(node); if (anonQName == null) { anonQName = SchemaUtils.getAttributeAnonQName(node); } if (anonQName != null) { return anonQName; } // Try returning anyType QName nodeName = getNodeQName(node); if (nodeName != null && Constants.isSchemaXSD(nodeName.getNamespaceURI()) && (nodeName.getLocalPart().equals("element") || nodeName.getLocalPart().equals("attribute"))) { return Constants.XSD_ANYTYPE; } } } // Return null if not found if (prefixedName == null) { return null; } // Change the prefixed name into a full qname QName qName = getQNameFromPrefixedName(node, prefixedName); // An alternate qname is returned if nillable if (typeAttrName.equals("type")) { if (JavaUtils.isTrueExplicitly(getAttribute(node, "nillable"))) { qName = getNillableQName(qName); } } return qName; } /** * Convert a prefixed name into a qname */ public static QName getQNameFromPrefixedName(Node node, String prefixedName) { String localName = prefixedName.substring(prefixedName.lastIndexOf(":") + 1); String namespace = null; // Associate the namespace prefix with a namespace if (prefixedName.length() == localName.length()) { namespace = getScopedAttribute(node, "xmlns"); // Get namespace for unqualified reference } else { namespace = getScopedAttribute(node, "xmlns:" + prefixedName.substring(0, prefixedName.lastIndexOf(":"))); } return (findQName(namespace, localName)); } /** * This method returns a set of all types that are derived * from this type via an extension of a complexType */ public static HashSet getDerivedTypes(TypeEntry type, SymbolTable symbolTable) { HashSet types = new HashSet(); if (type != null && type.getNode() != null) { getDerivedTypes(type, types, symbolTable); } else if (Constants.isSchemaXSD(type.getQName().getNamespaceURI()) && (type.getQName().getLocalPart().equals("anyType") || type.getQName().getLocalPart().equals("any"))) { // All types are derived from anyType types.addAll(symbolTable.getTypeIndex().values()); } return types; } // getNestedTypes private static void getDerivedTypes(TypeEntry type, HashSet types, SymbolTable symbolTable) { // If all types are in the set, return if (types.size() == symbolTable.getTypeEntryCount()) { return; } // Search the dictionary for derived types of type for (Iterator it = symbolTable.getTypeIndex().values().iterator(); it.hasNext();) { Type t = (Type)it.next(); if (t instanceof DefinedType && t.getNode() != null && !types.contains(t) && (((DefinedType)t).getComplexTypeExtensionBase(symbolTable) == type)) { types.add(t); getDerivedTypes(t, types, symbolTable); } } } // getDerivedTypes /** * This method returns a set of all the nested types. * Nested types are types declared within this TypeEntry (or descendents) * plus any extended types and the extended type nested types * The elements of the returned HashSet are Types. * * @param type is the type entry to consider * @param symbolTable is the symbolTable * @param derivedFlag should be set if all dependendent derived types should also be * returned. */ public static HashSet getNestedTypes(TypeEntry type, SymbolTable symbolTable, boolean derivedFlag) { HashSet types = new HashSet(); getNestedTypes(type, types, symbolTable, derivedFlag); return types; } // getNestedTypes private static void getNestedTypes(TypeEntry type, HashSet types, SymbolTable symbolTable, boolean derivedFlag) { if (type == null) { return; } // If all types are in the set, return if (types.size() == symbolTable.getTypeEntryCount()) { return; } // Process types derived from this type if (derivedFlag) { HashSet derivedTypes = getDerivedTypes(type, symbolTable); Iterator it = derivedTypes.iterator(); while (it.hasNext()) { TypeEntry derivedType = (TypeEntry)it.next(); if (!types.contains(derivedType)) { types.add(derivedType); getNestedTypes(derivedType, types, symbolTable, derivedFlag); } } } // Continue only if the node exists if (type.getNode() == null) { return; } Node node = type.getNode(); // Process types declared in this type Vector v = SchemaUtils.getContainedElementDeclarations(node, symbolTable); if (v != null) { for (int i = 0; i < v.size(); i++) { ElementDecl elem = (ElementDecl)v.get(i); if (!types.contains(elem.getType())) { types.add(elem.getType()); getNestedTypes(elem.getType(), types, symbolTable, derivedFlag); } } } // Process attributes declared in this type v = SchemaUtils.getContainedAttributeTypes(node, symbolTable); if (v != null) { for (int i = 0; i < v.size(); i += 2) { if (!types.contains(v.get(i))) { types.add(v.get(i)); getNestedTypes(((TypeEntry)v.get(i)), types, symbolTable, derivedFlag); } } } // Process referenced types if (type.getRefType() != null && !types.contains(type.getRefType())) { types.add(type.getRefType()); getNestedTypes(type.getRefType(), types, symbolTable, derivedFlag); } /* Anonymous processing and should be automatically handled by the reference processing above // Get the anonymous type of the element QName anonQName = SchemaUtils.getElementAnonQName(node); if (anonQName != null) { TypeEntry anonType = symbolTable.getType(anonQName); if (anonType != null && !types.contains(anonType)) { types.add(anonType); } } // Get the anonymous type of an attribute anonQName = SchemaUtils.getAttributeAnonQName(node); if (anonQName != null) { TypeEntry anonType = symbolTable.getType(anonQName); if (anonType != null && !types.contains(anonType)) { types.add(anonType); } } */ // Process extended types TypeEntry extendType = SchemaUtils.getComplexElementExtensionBase(node, symbolTable); if (extendType != null) { if (!types.contains(extendType)) { types.add(extendType); getNestedTypes(extendType, types, symbolTable, derivedFlag); } } /* Array component processing should be automatically handled by the reference processing above. // Process array components QName componentQName = SchemaUtils.getArrayComponentQName(node, new IntHolder(0)); TypeEntry componentType = symbolTable.getType(componentQName); if (componentType == null) { componentType = symbolTable.getElement(componentQName); } if (componentType != null) { if (!types.contains(componentType)) { types.add(componentType); getNestedTypes(componentType, types, symbolTable, derivedFlag); } } */ } // getNestedTypes /** * Generate an XML prefixed attribute value with a corresponding xmlns * declaration for the prefix. If there is no namespace, * don't prefix the name or emit the xmlns attribute. * <p/> * Caller should provide the enclosing quotes. * <p/> * Usage: println("name=\"" + genXMLQNameString(qname, "foo") + "\"" */ public static String genQNameAttributeString(QName qname, String prefix) { if (qname.getNamespaceURI() == null || qname.getNamespaceURI().equals("")) return qname.getLocalPart(); return prefix + ":" + qname.getLocalPart() + "\" xmlns:" + prefix + "=\"" + qname.getNamespaceURI(); } }
Utils.java |