| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* |
| * $Id: QName.java 468655 2006-10-28 07:12:06Z minchau $ |
| */ |
| package org.apache.xml.utils; |
| |
| import java.util.Stack; |
| import java.util.StringTokenizer; |
| |
| import org.apache.xml.res.XMLErrorResources; |
| import org.apache.xml.res.XMLMessages; |
| |
| import org.w3c.dom.Element; |
| |
| /** |
| * Class to represent a qualified name: "The name of an internal XSLT object, |
| * specifically a named template (see [7 Named Templates]), a mode (see [6.7 Modes]), |
| * an attribute set (see [8.1.4 Named Attribute Sets]), a key (see [14.2 Keys]), |
| * a locale (see [14.3 Number Formatting]), a variable or a parameter (see |
| * [12 Variables and Parameters]) is specified as a QName. If it has a prefix, |
| * then the prefix is expanded into a URI reference using the namespace declarations |
| * in effect on the attribute in which the name occurs. The expanded name |
| * consisting of the local part of the name and the possibly null URI reference |
| * is used as the name of the object. The default namespace is not used for |
| * unprefixed names." |
| * @xsl.usage general |
| */ |
| public class QName implements java.io.Serializable |
| { |
| static final long serialVersionUID = 467434581652829920L; |
| |
| /** |
| * The local name. |
| * @serial |
| */ |
| protected String _localName; |
| |
| /** |
| * The namespace URI. |
| * @serial |
| */ |
| protected String _namespaceURI; |
| |
| /** |
| * The namespace prefix. |
| * @serial |
| */ |
| protected String _prefix; |
| |
| /** |
| * The XML namespace. |
| */ |
| public static final String S_XMLNAMESPACEURI = |
| "http://www.w3.org/XML/1998/namespace"; |
| |
| /** |
| * The cached hashcode, which is calculated at construction time. |
| * @serial |
| */ |
| private int m_hashCode; |
| |
| /** |
| * Constructs an empty QName. |
| * 20001019: Try making this public, to support Serializable? -- JKESS |
| */ |
| public QName(){} |
| |
| /** |
| * Constructs a new QName with the specified namespace URI and |
| * local name. |
| * |
| * @param namespaceURI The namespace URI if known, or null |
| * @param localName The local name |
| */ |
| public QName(String namespaceURI, String localName) |
| { |
| this(namespaceURI, localName, false); |
| } |
| |
| /** |
| * Constructs a new QName with the specified namespace URI and |
| * local name. |
| * |
| * @param namespaceURI The namespace URI if known, or null |
| * @param localName The local name |
| * @param validate If true the new QName will be validated and an IllegalArgumentException will |
| * be thrown if it is invalid. |
| */ |
| public QName(String namespaceURI, String localName, boolean validate) |
| { |
| |
| // This check was already here. So, for now, I will not add it to the validation |
| // that is done when the validate parameter is true. |
| if (localName == null) |
| throw new IllegalArgumentException(XMLMessages.createXMLMessage( |
| XMLErrorResources.ER_ARG_LOCALNAME_NULL, null)); //"Argument 'localName' is null"); |
| |
| if (validate) |
| { |
| if (!XML11Char.isXML11ValidNCName(localName)) |
| { |
| throw new IllegalArgumentException(XMLMessages.createXMLMessage( |
| XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName"); |
| } |
| } |
| |
| _namespaceURI = namespaceURI; |
| _localName = localName; |
| m_hashCode = toString().hashCode(); |
| } |
| |
| /** |
| * Constructs a new QName with the specified namespace URI, prefix |
| * and local name. |
| * |
| * @param namespaceURI The namespace URI if known, or null |
| * @param prefix The namespace prefix is known, or null |
| * @param localName The local name |
| * |
| */ |
| public QName(String namespaceURI, String prefix, String localName) |
| { |
| this(namespaceURI, prefix, localName, false); |
| } |
| |
| /** |
| * Constructs a new QName with the specified namespace URI, prefix |
| * and local name. |
| * |
| * @param namespaceURI The namespace URI if known, or null |
| * @param prefix The namespace prefix is known, or null |
| * @param localName The local name |
| * @param validate If true the new QName will be validated and an IllegalArgumentException will |
| * be thrown if it is invalid. |
| */ |
| public QName(String namespaceURI, String prefix, String localName, boolean validate) |
| { |
| |
| // This check was already here. So, for now, I will not add it to the validation |
| // that is done when the validate parameter is true. |
| if (localName == null) |
| throw new IllegalArgumentException(XMLMessages.createXMLMessage( |
| XMLErrorResources.ER_ARG_LOCALNAME_NULL, null)); //"Argument 'localName' is null"); |
| |
| if (validate) |
| { |
| if (!XML11Char.isXML11ValidNCName(localName)) |
| { |
| throw new IllegalArgumentException(XMLMessages.createXMLMessage( |
| XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName"); |
| } |
| |
| if ((null != prefix) && (!XML11Char.isXML11ValidNCName(prefix))) |
| { |
| throw new IllegalArgumentException(XMLMessages.createXMLMessage( |
| XMLErrorResources.ER_ARG_PREFIX_INVALID,null )); //"Argument 'prefix' not a valid NCName"); |
| } |
| |
| } |
| _namespaceURI = namespaceURI; |
| _prefix = prefix; |
| _localName = localName; |
| m_hashCode = toString().hashCode(); |
| } |
| |
| /** |
| * Construct a QName from a string, without namespace resolution. Good |
| * for a few odd cases. |
| * |
| * @param localName Local part of qualified name |
| * |
| */ |
| public QName(String localName) |
| { |
| this(localName, false); |
| } |
| |
| /** |
| * Construct a QName from a string, without namespace resolution. Good |
| * for a few odd cases. |
| * |
| * @param localName Local part of qualified name |
| * @param validate If true the new QName will be validated and an IllegalArgumentException will |
| * be thrown if it is invalid. |
| */ |
| public QName(String localName, boolean validate) |
| { |
| |
| // This check was already here. So, for now, I will not add it to the validation |
| // that is done when the validate parameter is true. |
| if (localName == null) |
| throw new IllegalArgumentException(XMLMessages.createXMLMessage( |
| XMLErrorResources.ER_ARG_LOCALNAME_NULL, null)); //"Argument 'localName' is null"); |
| |
| if (validate) |
| { |
| if (!XML11Char.isXML11ValidNCName(localName)) |
| { |
| throw new IllegalArgumentException(XMLMessages.createXMLMessage( |
| XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName"); |
| } |
| } |
| _namespaceURI = null; |
| _localName = localName; |
| m_hashCode = toString().hashCode(); |
| } |
| |
| /** |
| * Construct a QName from a string, resolving the prefix |
| * using the given namespace stack. The default namespace is |
| * not resolved. |
| * |
| * @param qname Qualified name to resolve |
| * @param namespaces Namespace stack to use to resolve namespace |
| */ |
| public QName(String qname, Stack namespaces) |
| { |
| this(qname, namespaces, false); |
| } |
| |
| /** |
| * Construct a QName from a string, resolving the prefix |
| * using the given namespace stack. The default namespace is |
| * not resolved. |
| * |
| * @param qname Qualified name to resolve |
| * @param namespaces Namespace stack to use to resolve namespace |
| * @param validate If true the new QName will be validated and an IllegalArgumentException will |
| * be thrown if it is invalid. |
| */ |
| public QName(String qname, Stack namespaces, boolean validate) |
| { |
| |
| String namespace = null; |
| String prefix = null; |
| int indexOfNSSep = qname.indexOf(':'); |
| |
| if (indexOfNSSep > 0) |
| { |
| prefix = qname.substring(0, indexOfNSSep); |
| |
| if (prefix.equals("xml")) |
| { |
| namespace = S_XMLNAMESPACEURI; |
| } |
| // Do we want this? |
| else if (prefix.equals("xmlns")) |
| { |
| return; |
| } |
| else |
| { |
| int depth = namespaces.size(); |
| |
| for (int i = depth - 1; i >= 0; i--) |
| { |
| NameSpace ns = (NameSpace) namespaces.elementAt(i); |
| |
| while (null != ns) |
| { |
| if ((null != ns.m_prefix) && prefix.equals(ns.m_prefix)) |
| { |
| namespace = ns.m_uri; |
| i = -1; |
| |
| break; |
| } |
| |
| ns = ns.m_next; |
| } |
| } |
| } |
| |
| if (null == namespace) |
| { |
| throw new RuntimeException( |
| XMLMessages.createXMLMessage( |
| XMLErrorResources.ER_PREFIX_MUST_RESOLVE, |
| new Object[]{ prefix })); //"Prefix must resolve to a namespace: "+prefix); |
| } |
| } |
| |
| _localName = (indexOfNSSep < 0) |
| ? qname : qname.substring(indexOfNSSep + 1); |
| |
| if (validate) |
| { |
| if ((_localName == null) || (!XML11Char.isXML11ValidNCName(_localName))) |
| { |
| throw new IllegalArgumentException(XMLMessages.createXMLMessage( |
| XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName"); |
| } |
| } |
| _namespaceURI = namespace; |
| _prefix = prefix; |
| m_hashCode = toString().hashCode(); |
| } |
| |
| /** |
| * Construct a QName from a string, resolving the prefix |
| * using the given namespace context and prefix resolver. |
| * The default namespace is not resolved. |
| * |
| * @param qname Qualified name to resolve |
| * @param namespaceContext Namespace Context to use |
| * @param resolver Prefix resolver for this context |
| */ |
| public QName(String qname, Element namespaceContext, |
| PrefixResolver resolver) |
| { |
| this(qname, namespaceContext, resolver, false); |
| } |
| |
| /** |
| * Construct a QName from a string, resolving the prefix |
| * using the given namespace context and prefix resolver. |
| * The default namespace is not resolved. |
| * |
| * @param qname Qualified name to resolve |
| * @param namespaceContext Namespace Context to use |
| * @param resolver Prefix resolver for this context |
| * @param validate If true the new QName will be validated and an IllegalArgumentException will |
| * be thrown if it is invalid. |
| */ |
| public QName(String qname, Element namespaceContext, |
| PrefixResolver resolver, boolean validate) |
| { |
| |
| _namespaceURI = null; |
| |
| int indexOfNSSep = qname.indexOf(':'); |
| |
| if (indexOfNSSep > 0) |
| { |
| if (null != namespaceContext) |
| { |
| String prefix = qname.substring(0, indexOfNSSep); |
| |
| _prefix = prefix; |
| |
| if (prefix.equals("xml")) |
| { |
| _namespaceURI = S_XMLNAMESPACEURI; |
| } |
| |
| // Do we want this? |
| else if (prefix.equals("xmlns")) |
| { |
| return; |
| } |
| else |
| { |
| _namespaceURI = resolver.getNamespaceForPrefix(prefix, |
| namespaceContext); |
| } |
| |
| if (null == _namespaceURI) |
| { |
| throw new RuntimeException( |
| XMLMessages.createXMLMessage( |
| XMLErrorResources.ER_PREFIX_MUST_RESOLVE, |
| new Object[]{ prefix })); //"Prefix must resolve to a namespace: "+prefix); |
| } |
| } |
| else |
| { |
| |
| // TODO: error or warning... |
| } |
| } |
| |
| _localName = (indexOfNSSep < 0) |
| ? qname : qname.substring(indexOfNSSep + 1); |
| |
| if (validate) |
| { |
| if ((_localName == null) || (!XML11Char.isXML11ValidNCName(_localName))) |
| { |
| throw new IllegalArgumentException(XMLMessages.createXMLMessage( |
| XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName"); |
| } |
| } |
| |
| m_hashCode = toString().hashCode(); |
| } |
| |
| |
| /** |
| * Construct a QName from a string, resolving the prefix |
| * using the given namespace stack. The default namespace is |
| * not resolved. |
| * |
| * @param qname Qualified name to resolve |
| * @param resolver Prefix resolver for this context |
| */ |
| public QName(String qname, PrefixResolver resolver) |
| { |
| this(qname, resolver, false); |
| } |
| |
| /** |
| * Construct a QName from a string, resolving the prefix |
| * using the given namespace stack. The default namespace is |
| * not resolved. |
| * |
| * @param qname Qualified name to resolve |
| * @param resolver Prefix resolver for this context |
| * @param validate If true the new QName will be validated and an IllegalArgumentException will |
| * be thrown if it is invalid. |
| */ |
| public QName(String qname, PrefixResolver resolver, boolean validate) |
| { |
| |
| String prefix = null; |
| _namespaceURI = null; |
| |
| int indexOfNSSep = qname.indexOf(':'); |
| |
| if (indexOfNSSep > 0) |
| { |
| prefix = qname.substring(0, indexOfNSSep); |
| |
| if (prefix.equals("xml")) |
| { |
| _namespaceURI = S_XMLNAMESPACEURI; |
| } |
| else |
| { |
| _namespaceURI = resolver.getNamespaceForPrefix(prefix); |
| } |
| |
| if (null == _namespaceURI) |
| { |
| throw new RuntimeException( |
| XMLMessages.createXMLMessage( |
| XMLErrorResources.ER_PREFIX_MUST_RESOLVE, |
| new Object[]{ prefix })); //"Prefix must resolve to a namespace: "+prefix); |
| } |
| _localName = qname.substring(indexOfNSSep + 1); |
| } |
| else if (indexOfNSSep == 0) |
| { |
| throw new RuntimeException( |
| XMLMessages.createXMLMessage( |
| XMLErrorResources.ER_NAME_CANT_START_WITH_COLON, |
| null)); |
| } |
| else |
| { |
| _localName = qname; |
| } |
| |
| if (validate) |
| { |
| if ((_localName == null) || (!XML11Char.isXML11ValidNCName(_localName))) |
| { |
| throw new IllegalArgumentException(XMLMessages.createXMLMessage( |
| XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName"); |
| } |
| } |
| |
| |
| m_hashCode = toString().hashCode(); |
| _prefix = prefix; |
| } |
| |
| /** |
| * Returns the namespace URI. Returns null if the namespace URI |
| * is not known. |
| * |
| * @return The namespace URI, or null |
| */ |
| public String getNamespaceURI() |
| { |
| return _namespaceURI; |
| } |
| |
| /** |
| * Returns the namespace prefix. Returns null if the namespace |
| * prefix is not known. |
| * |
| * @return The namespace prefix, or null |
| */ |
| public String getPrefix() |
| { |
| return _prefix; |
| } |
| |
| /** |
| * Returns the local part of the qualified name. |
| * |
| * @return The local part of the qualified name |
| */ |
| public String getLocalName() |
| { |
| return _localName; |
| } |
| |
| /** |
| * Return the string representation of the qualified name, using the |
| * prefix if available, or the '{ns}foo' notation if not. Performs |
| * string concatenation, so beware of performance issues. |
| * |
| * @return the string representation of the namespace |
| */ |
| public String toString() |
| { |
| |
| return _prefix != null |
| ? (_prefix + ":" + _localName) |
| : (_namespaceURI != null |
| ? ("{"+_namespaceURI + "}" + _localName) : _localName); |
| } |
| |
| /** |
| * Return the string representation of the qualified name using the |
| * the '{ns}foo' notation. Performs |
| * string concatenation, so beware of performance issues. |
| * |
| * @return the string representation of the namespace |
| */ |
| public String toNamespacedString() |
| { |
| |
| return (_namespaceURI != null |
| ? ("{"+_namespaceURI + "}" + _localName) : _localName); |
| } |
| |
| |
| /** |
| * Get the namespace of the qualified name. |
| * |
| * @return the namespace URI of the qualified name |
| */ |
| public String getNamespace() |
| { |
| return getNamespaceURI(); |
| } |
| |
| /** |
| * Get the local part of the qualified name. |
| * |
| * @return the local part of the qualified name |
| */ |
| public String getLocalPart() |
| { |
| return getLocalName(); |
| } |
| |
| /** |
| * Return the cached hashcode of the qualified name. |
| * |
| * @return the cached hashcode of the qualified name |
| */ |
| public int hashCode() |
| { |
| return m_hashCode; |
| } |
| |
| /** |
| * Override equals and agree that we're equal if |
| * the passed object is a string and it matches |
| * the name of the arg. |
| * |
| * @param ns Namespace URI to compare to |
| * @param localPart Local part of qualified name to compare to |
| * |
| * @return True if the local name and uri match |
| */ |
| public boolean equals(String ns, String localPart) |
| { |
| |
| String thisnamespace = getNamespaceURI(); |
| |
| return getLocalName().equals(localPart) |
| && (((null != thisnamespace) && (null != ns)) |
| ? thisnamespace.equals(ns) |
| : ((null == thisnamespace) && (null == ns))); |
| } |
| |
| /** |
| * Override equals and agree that we're equal if |
| * the passed object is a QName and it matches |
| * the name of the arg. |
| * |
| * @return True if the qualified names are equal |
| */ |
| public boolean equals(Object object) |
| { |
| |
| if (object == this) |
| return true; |
| |
| if (object instanceof QName) { |
| QName qname = (QName) object; |
| String thisnamespace = getNamespaceURI(); |
| String thatnamespace = qname.getNamespaceURI(); |
| |
| return getLocalName().equals(qname.getLocalName()) |
| && (((null != thisnamespace) && (null != thatnamespace)) |
| ? thisnamespace.equals(thatnamespace) |
| : ((null == thisnamespace) && (null == thatnamespace))); |
| } |
| else |
| return false; |
| } |
| |
| /** |
| * Given a string, create and return a QName object |
| * |
| * |
| * @param name String to use to create QName |
| * |
| * @return a QName object |
| */ |
| public static QName getQNameFromString(String name) |
| { |
| |
| StringTokenizer tokenizer = new StringTokenizer(name, "{}", false); |
| QName qname; |
| String s1 = tokenizer.nextToken(); |
| String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null; |
| |
| if (null == s2) |
| qname = new QName(null, s1); |
| else |
| qname = new QName(s1, s2); |
| |
| return qname; |
| } |
| |
| /** |
| * This function tells if a raw attribute name is a |
| * xmlns attribute. |
| * |
| * @param attRawName Raw name of attribute |
| * |
| * @return True if the attribute starts with or is equal to xmlns |
| */ |
| public static boolean isXMLNSDecl(String attRawName) |
| { |
| |
| return (attRawName.startsWith("xmlns") |
| && (attRawName.equals("xmlns") |
| || attRawName.startsWith("xmlns:"))); |
| } |
| |
| /** |
| * This function tells if a raw attribute name is a |
| * xmlns attribute. |
| * |
| * @param attRawName Raw name of attribute |
| * |
| * @return Prefix of attribute |
| */ |
| public static String getPrefixFromXMLNSDecl(String attRawName) |
| { |
| |
| int index = attRawName.indexOf(':'); |
| |
| return (index >= 0) ? attRawName.substring(index + 1) : ""; |
| } |
| |
| /** |
| * Returns the local name of the given node. |
| * |
| * @param qname Input name |
| * |
| * @return Local part of the name if prefixed, or the given name if not |
| */ |
| public static String getLocalPart(String qname) |
| { |
| |
| int index = qname.indexOf(':'); |
| |
| return (index < 0) ? qname : qname.substring(index + 1); |
| } |
| |
| /** |
| * Returns the local name of the given node. |
| * |
| * @param qname Input name |
| * |
| * @return Prefix of name or empty string if none there |
| */ |
| public static String getPrefixPart(String qname) |
| { |
| |
| int index = qname.indexOf(':'); |
| |
| return (index >= 0) ? qname.substring(0, index) : ""; |
| } |
| } |