/* * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved. * * This software is open source. * See the bottom of this file for the licence. */ package org.orbeon.dom4j.tree; import org.orbeon.dom4j.DocumentFactory; import org.orbeon.dom4j.Namespace; import org.orbeon.dom4j.QName; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; /** * NamespaceStack implements a stack of namespaces and optionally maintains a * cache of all the fully qualified names (<code>QName</code>) which are in * scope. This is useful when building or navigating a <i>dom4j </i> document. * * @author <a href="mailto:jstrachan@apache.org">James Strachan </a> * @version $Revision: 1.13 $ */ public class NamespaceStack { /** The factory used to create new <code>Namespace</code> instances */ private DocumentFactory documentFactory; /** The Stack of namespaces */ private ArrayList namespaceStack = new ArrayList(); /** The cache of qualifiedNames to QNames per namespace context */ private ArrayList namespaceCacheList = new ArrayList(); /** * A cache of current namespace context cache of mapping from qualifiedName * to QName */ private Map currentNamespaceCache; /** * A cache of mapping from qualifiedName to QName before any namespaces are * declared */ private Map rootNamespaceCache = new HashMap(); /** Caches the default namespace defined via xmlns="" */ private Namespace defaultNamespace; public NamespaceStack() { this.documentFactory = DocumentFactory.getInstance(); } public NamespaceStack(DocumentFactory documentFactory) { this.documentFactory = documentFactory; } /** * Pushes the given namespace onto the stack so that its prefix becomes * available. * * @param namespace * is the <code>Namespace</code> to add to the stack. */ public void push(Namespace namespace) { namespaceStack.add(namespace); namespaceCacheList.add(null); currentNamespaceCache = null; String prefix = namespace.getPrefix(); if ((prefix == null) || (prefix.length() == 0)) { defaultNamespace = namespace; } } /** * Pops the most recently used <code>Namespace</code> from the stack * * @return Namespace popped from the stack */ public Namespace pop() { return remove(namespaceStack.size() - 1); } /** * DOCUMENT ME! * * @return the number of namespaces on the stackce stack. */ public int size() { return namespaceStack.size(); } /** * Clears the stack */ public void clear() { namespaceStack.clear(); namespaceCacheList.clear(); rootNamespaceCache.clear(); currentNamespaceCache = null; } /** * DOCUMENT ME! * * @param index * DOCUMENT ME! * * @return the namespace at the specified index on the stack */ public Namespace getNamespace(int index) { return (Namespace) namespaceStack.get(index); } /** * DOCUMENT ME! * * @param prefix * DOCUMENT ME! * * @return the namespace for the given prefix or null if it could not be * found. */ public Namespace getNamespaceForPrefix(String prefix) { if (prefix == null) { prefix = ""; } for (int i = namespaceStack.size() - 1; i >= 0; i--) { Namespace namespace = (Namespace) namespaceStack.get(i); if (prefix.equals(namespace.getPrefix())) { return namespace; } } return null; } /** * DOCUMENT ME! * * @param prefix * DOCUMENT ME! * * @return the URI for the given prefix or null if it could not be found. */ public String getURI(String prefix) { Namespace namespace = getNamespaceForPrefix(prefix); return (namespace != null) ? namespace.getURI() : null; } /** * DOCUMENT ME! * * @param namespace * DOCUMENT ME! * * @return true if the given prefix is in the stack. */ public boolean contains(Namespace namespace) { String prefix = namespace.getPrefix(); Namespace current = null; if ((prefix == null) || (prefix.length() == 0)) { current = getDefaultNamespace(); } else { current = getNamespaceForPrefix(prefix); } if (current == null) { return false; } if (current == namespace) { return true; } return namespace.getURI().equals(current.getURI()); } public QName getQName(String namespaceURI, String localName, String qualifiedName) { if (localName == null) { localName = qualifiedName; } else if (qualifiedName == null) { qualifiedName = localName; } if (namespaceURI == null) { namespaceURI = ""; } String prefix = ""; int index = qualifiedName.indexOf(":"); if (index > 0) { prefix = qualifiedName.substring(0, index); if (localName.trim().length() == 0) { localName = qualifiedName.substring(index + 1); } } else if (localName.trim().length() == 0) { localName = qualifiedName; } Namespace namespace = createNamespace(prefix, namespaceURI); return pushQName(localName, qualifiedName, namespace, prefix); } public QName getAttributeQName(String namespaceURI, String localName, String qualifiedName) { if (qualifiedName == null) { qualifiedName = localName; } Map map = getNamespaceCache(); QName answer = (QName) map.get(qualifiedName); if (answer != null) { return answer; } if (localName == null) { localName = qualifiedName; } if (namespaceURI == null) { namespaceURI = ""; } Namespace namespace = null; String prefix = ""; int index = qualifiedName.indexOf(":"); if (index > 0) { prefix = qualifiedName.substring(0, index); namespace = createNamespace(prefix, namespaceURI); if (localName.trim().length() == 0) { localName = qualifiedName.substring(index + 1); } } else { // attributes with no prefix have no namespace namespace = Namespace.NO_NAMESPACE; if (localName.trim().length() == 0) { localName = qualifiedName; } } answer = pushQName(localName, qualifiedName, namespace, prefix); map.put(qualifiedName, answer); return answer; } /** * Adds a namepace to the stack with the given prefix and URI * * @param prefix * DOCUMENT ME! * @param uri * DOCUMENT ME! */ public void push(String prefix, String uri) { if (uri == null) { uri = ""; } Namespace namespace = createNamespace(prefix, uri); push(namespace); } /** * Adds a new namespace to the stack * * @param prefix * DOCUMENT ME! * @param uri * DOCUMENT ME! * * @return DOCUMENT ME! */ public Namespace addNamespace(String prefix, String uri) { Namespace namespace = createNamespace(prefix, uri); push(namespace); return namespace; } /** * Pops a namepace from the stack with the given prefix and URI * * @param prefix * DOCUMENT ME! * * @return DOCUMENT ME! */ public Namespace pop(String prefix) { if (prefix == null) { prefix = ""; } Namespace namespace = null; for (int i = namespaceStack.size() - 1; i >= 0; i--) { Namespace ns = (Namespace) namespaceStack.get(i); if (prefix.equals(ns.getPrefix())) { remove(i); namespace = ns; break; } } if (namespace == null) { System.out.println("Warning: missing namespace prefix ignored: " + prefix); } return namespace; } public String toString() { return super.toString() + " Stack: " + namespaceStack.toString(); } public DocumentFactory getDocumentFactory() { return documentFactory; } public void setDocumentFactory(DocumentFactory documentFactory) { this.documentFactory = documentFactory; } public Namespace getDefaultNamespace() { if (defaultNamespace == null) { defaultNamespace = findDefaultNamespace(); } return defaultNamespace; } // Implementation methods // ------------------------------------------------------------------------- /** * Adds the QName to the stack of available QNames * * @param localName * DOCUMENT ME! * @param qualifiedName * DOCUMENT ME! * @param namespace * DOCUMENT ME! * @param prefix * DOCUMENT ME! * * @return DOCUMENT ME! */ protected QName pushQName(String localName, String qualifiedName, Namespace namespace, String prefix) { if ((prefix == null) || (prefix.length() == 0)) { this.defaultNamespace = null; } return createQName(localName, qualifiedName, namespace); } /** * Factory method to creeate new QName instances. By default this method * interns the QName * * @param localName * DOCUMENT ME! * @param qualifiedName * DOCUMENT ME! * @param namespace * DOCUMENT ME! * * @return DOCUMENT ME! */ protected QName createQName(String localName, String qualifiedName, Namespace namespace) { return documentFactory.createQName(localName, namespace); } /** * Factory method to creeate new Namespace instances. By default this method * interns the Namespace * * @param prefix * DOCUMENT ME! * @param namespaceURI * DOCUMENT ME! * * @return DOCUMENT ME! */ protected Namespace createNamespace(String prefix, String namespaceURI) { return documentFactory.createNamespace(prefix, namespaceURI); } /** * Attempts to find the current default namespace on the stack right now or * returns null if one could not be found * * @return DOCUMENT ME! */ protected Namespace findDefaultNamespace() { for (int i = namespaceStack.size() - 1; i >= 0; i--) { Namespace namespace = (Namespace) namespaceStack.get(i); if (namespace != null) { String prefix = namespace.getPrefix(); if ((prefix == null) || (namespace.getPrefix().length() == 0)) { return namespace; } } } return null; } /** * Removes the namespace at the given index of the stack * * @param index * DOCUMENT ME! * * @return DOCUMENT ME! */ protected Namespace remove(int index) { Namespace namespace = (Namespace) namespaceStack.remove(index); namespaceCacheList.remove(index); defaultNamespace = null; currentNamespaceCache = null; return namespace; } protected Map getNamespaceCache() { if (currentNamespaceCache == null) { int index = namespaceStack.size() - 1; if (index < 0) { currentNamespaceCache = rootNamespaceCache; } else { currentNamespaceCache = (Map) namespaceCacheList.get(index); if (currentNamespaceCache == null) { currentNamespaceCache = new HashMap(); namespaceCacheList.set(index, currentNamespaceCache); } } } return currentNamespaceCache; } } /* * 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 "DOM4J" must not be used to endorse or promote products derived * from this Software without prior written permission of MetaStuff, Ltd. For * written permission, please contact dom4j-info@metastuff.com. * * 4. Products derived from this Software may not be called "DOM4J" nor may * "DOM4J" appear in their names without prior written permission of MetaStuff, * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd. * * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org * * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. 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 METASTUFF, LTD. 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-2005 (C) MetaStuff, Ltd. All Rights Reserved. */