/**
* 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