/** * Redistribution and use of this software and associated documentation * ("Software"), with or without modification, are permitted provided * that the following conditions are met: * * 1. Redistributions of source code must retain copyright * statements and notices. Redistributions must also contain a * copy of this document. * * 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 name "Exolab" must not be used to endorse or promote * products derived from this Software without prior written * permission of Intalio, Inc. For written permission, * please contact info@exolab.org. * * 4. Products derived from this Software may not be called "Exolab" * nor may "Exolab" appear in their names without prior written * permission of Intalio, Inc. Exolab is a registered * trademark of Intalio, Inc. * * 5. Due credit should be given to the Exolab Project * (http://www.exolab.org/). * * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS * ``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 * INTALIO, INC. 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. * * Copyright 2001-2003 (C) Intalio, Inc. All Rights Reserved. * * $Id$ */ package org.exolab.castor.xml; import org.xml.sax.ContentHandler; import org.xml.sax.helpers.AttributeListImpl; import org.xml.sax.SAXException; import java.util.Enumeration; import java.util.Vector; /** * A class for handling Namespace declaration and scoping * * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a> * @version $Revision$ $Date: 2004-09-09 23:04:08 -0600 (Thu, 09 Sep 2004) $ **/ public final class Namespaces { /** * The reserved XML Namespace Prefix */ public static final String XML_NAMESPACE_PREFIX = "xml"; /** * The reserved XML 1.0 Namespace URI */ public static final String XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace"; /** * The first namespace in this set of Namespaces **/ private Namespace _first = null; /** * The last namespace in this set of Namespaces **/ private Namespace _last = null; private Namespaces _parent = null; /** * The CDATA type..uses for SAX attributes */ private static final String CDATA = "CDATA"; /** * The namespace declaration String **/ private static final String XMLNS = "xmlns"; /** * Creates a new Namespaces instance **/ public Namespaces() { super(); } //-- Namespaces /** * Creates a new Namespaces instance **/ public Namespaces(Namespaces parent) { super(); _parent = parent; } //-- Namespaces /** * Adds the given namespace declaration to this Namespaces * * @param prefix the namespace prefix * @param uri the namespace URI to be associated with the given prefix **/ public synchronized void addNamespace(String prefix, String uri) { if (uri == null) { throw new IllegalArgumentException("Namespace URI must not be null"); } //-- adjust prefix to prevent null value if (prefix == null) prefix = ""; //-- Make sure prefix is not equal to "xml" if (XML_NAMESPACE_PREFIX.equalsIgnoreCase(prefix)) { if (!XML_NAMESPACE.equals(uri)) { String err = "The prefix 'xml' is reserved (XML 1.0 Specification) " + "and cannot be declared."; throw new IllegalArgumentException(err); } //-- if we make it here, just ignore it (it's already supported internally) return; } //-- make sure URI is not equal to the XML 1.0 namespace else if (XML_NAMESPACE.equals(uri)) { String err = "The namespace '" + XML_NAMESPACE; err += "' is reserved (XML 1.0 Specification) and cannot be declared."; throw new IllegalArgumentException(err); } if (_first == null) { _first = new Namespace(prefix, uri); _last = _first; } //-- check for existing namespace declaration for //-- prefix else { boolean found = false; Namespace ns = _first; while (ns != null) { if (ns.prefix.equals(prefix)) { found = true; ns.uri = uri; break; } ns = ns.next; } if (!found) { _last.next = new Namespace(prefix, uri); _last = _last.next; } } } //-- method: addNamespace /** * Creates a new Namespaces instance with this Namespaces as the parent **/ public Namespaces createNamespaces() { return new Namespaces(this); } //-- method: createNamespaces /** * Returns an Enumeration of local namespace URIs for this Namespaces. * * @return an Enumeration of local namespace URIs. **/ public Enumeration getLocalNamespaces() { return new NamespaceEnumerator(_first); } //-- getLocalNamespace /** * Returns the Namespace URI associated with the given prefix * * @param prefix the namespace prefix to lookup * @return the namespace URI associated with the given prefix **/ public String getNamespaceURI(String prefix) { //-- adjust prefix to prevent null value if (prefix == null) prefix = ""; Namespace ns = _first; while (ns != null) { if (ns.prefix.equals(prefix)) { return ns.uri; } ns = ns.next; } if (_parent != null) { return _parent.getNamespaceURI(prefix); } //-- handle built-in namespace URIs if (XML_NAMESPACE_PREFIX.equals(prefix)) { return XML_NAMESPACE; } return null; } //-- method: getNamespaceURI /** * Returns the Namespace prefix associated with the given URI. * If multiple namespace prefixes have been declared, then * the first one found is returned. To obtain all prefixes see * <code>#getNamespacePrefixes</code>. * * @param nsURI the namespace URI to lookup * @return the namespace prefix associated with the given URI **/ public String getNamespacePrefix(String nsURI) { //-- prevent null value if (nsURI == null) throw new IllegalArgumentException("Namespace URI must not be null."); Namespace ns = _first; while (ns != null) { if (ns.uri.equals(nsURI)) { return ns.prefix; } ns = ns.next; } if (_parent != null) { return _parent.getNamespacePrefix(nsURI); } //-- handle built-in namespace prefixes if (XML_NAMESPACE.equals(nsURI)) { return XML_NAMESPACE_PREFIX; } return null; } //-- method: getNamespacePrefix /** * Returns all namespace prefixes declared locally * * @return an Enumeration of locally declared namespace prefixes */ public Enumeration getLocalNamespacePrefixes() { return new NamespaceEnumerator(_first, NamespaceEnumerator.PREFIX); } //-- method: getLocalNamespacePrefixes /** * Returns all namespace prefixes associated with the given URI, * including those from parent scopes. * * @param nsURI the namespace URI to lookup * @return the namespace prefixes associated with the given URI **/ public String[] getNamespacePrefixes(String nsURI) { return getNamespacePrefixes(nsURI, false); } //-- method: getNamespacePrefixes /** * Returns the Namespace prefixes associated with the given URI. * * @param nsURI the namespace URI to lookup * @param local a boolean that when true indicates only the local * scope is searched. * @return the namespace prefixes associated with the given URI **/ public String[] getNamespacePrefixes(String nsURI, boolean local) { //-- prevent null value if (nsURI == null) throw new IllegalArgumentException("Namespace URI must not be null."); Vector prefixes = new Vector(3); getNamespacePrefixes(nsURI, local, prefixes); String[] pArray = new String[prefixes.size()]; prefixes.copyInto(pArray); return pArray; } //-- method: getNamespacePrefixes /** * Returns the Namespace prefix associated with the given URI. * Or null if no prefix has been declared. This method will * ignore the default namespace. This is useful when dealing * with attributes that do not use the default namespace. * * @param nsURI the namespace URI to lookup * @return the namespace prefix associated with the given URI **/ public String getNonDefaultNamespacePrefix(String nsURI) { //-- adjust prefix to prevent null value if (nsURI == null) throw new IllegalArgumentException("Namespace URI must not be null."); Namespace ns = _first; while (ns != null) { if (ns.uri.equals(nsURI)) { if (ns.prefix.length() > 0) { return ns.prefix; } } ns = ns.next; } if (_parent != null) { return _parent.getNonDefaultNamespacePrefix(nsURI); } //-- handle built-in namespace prefixes if (XML_NAMESPACE.equals(nsURI)) { return XML_NAMESPACE_PREFIX; } return null; } //-- method: getNonDefaultNamespacePrefix /** * Returns the parent Namespaces for this Namespaces instance. * * @return the parent Namespaces for this Namespaces instance. **/ public Namespaces getParent() { return _parent; } //-- method: getParent /** * Removes the namespace declaration for the given prefix. * This is a local action only, the namespace declaration * will not be removed from any parent Namespaces object. * * @param prefix the namespace prefix to remove the binding of * @return true if the namespace declaration was removed, * otherwise false. */ public synchronized boolean removeNamespace(String prefix) { if (prefix == null) return false; Namespace ns = _first; Namespace previous = null; while (ns != null) { if (ns.prefix.equals(prefix)) { if (ns == _first) { _first = _first.next; if (_last == ns) { _last = null; } } else { previous.next = ns.next; if (_last == ns) { _last = previous; } } return true; } previous = ns; ns = ns.next; } return false; } //-- method: removeNamespace /** * Sets the parent Namespaces for this Namespaces instance. * * @param namespaces the parent Namespaces **/ public void setParent(Namespaces namespaces) { _parent = namespaces; } //-- method: setParent /** * Calls the given ContentHandler's endPrefixMapping method * for each locally declared namespace * * @param handler the ContentHandler */ public void sendEndEvents(ContentHandler handler) throws SAXException { Namespace ns = _first; while (ns != null) { handler.endPrefixMapping(ns.prefix); ns = ns.next; } } //-- sendEndEvents /** * Calls the given ContentHandler's startPrefixMapping method * for each locally declared namespace * * @param handler the ContentHandler */ public void sendStartEvents(ContentHandler handler) throws SAXException { Namespace ns = _first; while (ns != null) { handler.startPrefixMapping(ns.prefix, ns.uri); ns = ns.next; } } //-- sendStartEvents /** * Declare the namespaces of this stack in as attributes. * @param atts the Attribute List to fill in. */ public void declareAsAttributes(AttributeListImpl atts, boolean localOnly) { Namespace ns = _first; String attName = null; while (ns != null) { if (ns.prefix != null) { int len = ns.prefix.length(); if (len > 0) { StringBuffer buf = new StringBuffer(6+len); buf.append(XMLNS); buf.append(':'); buf.append(ns.prefix); attName = buf.toString(); atts.addAttribute(attName, CDATA, ns.uri); } //case with no prefix but a nsURI else { atts.addAttribute(XMLNS, CDATA, ns.uri); } } //ns.prefix!=null else { atts.addAttribute(XMLNS, CDATA, ns.uri); } ns = ns.next; } if ((!localOnly) && (_parent != null)) { _parent.declareAsAttributes(atts, false); } } //method:declareAsAttributes /** * Adds the namespace prefixes associated with the given URI to the * given Vector. * * @param nsURI the namespace URI to lookup * @param local a boolean that when true indicates only the local * scope is searched. * @param prefixes the Vector to add the prefixes to **/ private void getNamespacePrefixes(String nsURI, boolean local, Vector prefixes) { Namespace ns = _first; while (ns != null) { if (ns.uri.equals(nsURI)) { prefixes.addElement(ns.prefix); } ns = ns.next; } if ((_parent != null) && (!local)) { _parent.getNamespacePrefixes(nsURI, local, prefixes); } } //-- method: getNamespacePrefixes /** * An internal class used to represent a namespace **/ class Namespace { String prefix = null; String uri = null; Namespace next = null; Namespace() { super(); } Namespace(String prefix, String uri) { this.prefix = prefix; this.uri = uri; } } //-- class: Namespace /** * A simple Enumeration for Namespace objects */ static class NamespaceEnumerator implements java.util.Enumeration { public static final int URI = 0; public static final int PREFIX = 1; private Namespace _namespace = null; private int _returnType = URI; NamespaceEnumerator(Namespace namespace) { _namespace = namespace; } NamespaceEnumerator(Namespace namespace, int returnType) { _namespace = namespace; _returnType = returnType; } public boolean hasMoreElements() { return (_namespace != null); } public Object nextElement() { String obj = null; if (_namespace != null) { if (_returnType == URI) obj = _namespace.uri; else obj = _namespace.prefix; _namespace = _namespace.next; } return obj; } } //-- class: NamespaceEnumerator } //-- class: Namespaces