| /* | ||
| * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved. | ||
| * | ||
| * This software is open source. | ||
| * See the bottom of this file for the licence. | ||
| * | ||
| * $Id: NamespaceStack.java,v 1.11 2004/06/25 08:03:41 maartenc Exp $ | ||
| */ | ||
| package org.dom4j.tree; | ||
| import java.util.ArrayList; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import org.dom4j.DocumentFactory; | ||
| import org.dom4j.Namespace; | ||
| import org.dom4j.QName; | ||
| /** 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.11 $ | ||
| */ | ||
| public class NamespaceStack { | ||
| /** The factory used to create new <code>Namespace</code> instances */ | ||
| private DocumentFactory documentFactory; | ||
| /** The Stack of namespaces */ | ||
| 11188x | private ArrayList namespaceStack = new ArrayList(); | |
| /** The cache of qualifiedNames to QNames per namespace context */ | ||
| 11188x | 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 */ | ||
| 11188x | private Map rootNamespaceCache = new HashMap(); | |
| /** Caches the default namespace defined via xmlns="" */ | ||
| private Namespace defaultNamespace; | ||
| 4254x | public NamespaceStack() { | |
| 4254x | this.documentFactory = DocumentFactory.getInstance(); | |
| 4254x | } | |
| 11188x | public NamespaceStack(DocumentFactory documentFactory) { | |
| 11188x | this.documentFactory = documentFactory; | |
| 11188x | } | |
| /** 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) { | ||
| 16360x | namespaceStack.add( namespace ); | |
| 16360x | namespaceCacheList.add( null ); | |
| 16360x | currentNamespaceCache = null; | |
| 16360x | String prefix = namespace.getPrefix(); | |
| 3/4 16360x | if ( prefix == null || prefix.length() == 0 ) { | |
| 4270x | defaultNamespace = namespace; | |
| } | ||
| 16360x | } | |
| /** Pops the most recently used <code>Namespace</code> from | ||
| * the stack | ||
| * | ||
| * @return Namespace popped from the stack | ||
| */ | ||
| public Namespace pop() { | ||
| 1020x | return remove( namespaceStack.size() - 1 ); | |
| } | ||
| /** @return the number of namespaces on the stackce stack. | ||
| */ | ||
| public int size() { | ||
| 145898x | return namespaceStack.size(); | |
| } | ||
| /** Clears the stack | ||
| */ | ||
| public void clear() { | ||
| 22366x | namespaceStack.clear(); | |
| 22366x | namespaceCacheList.clear(); | |
| 22366x | rootNamespaceCache.clear(); | |
| 22366x | currentNamespaceCache = null; | |
| 22366x | } | |
| /** @return the namespace at the specified index on the stack | ||
| */ | ||
| public Namespace getNamespace( int index ) { | ||
| 11090x | return (Namespace) namespaceStack.get( index ); | |
| } | ||
| /** @return the namespace for the given prefix or null | ||
| * if it could not be found. | ||
| */ | ||
| public Namespace getNamespaceForPrefix( String prefix ) { | ||
| 1/2 2008x | if ( prefix == null ) { | |
| 0x | prefix = ""; | |
| } | ||
| 2/2 3014x | for ( int i = namespaceStack.size() - 1; i >= 0; i-- ) { | |
| 2008x | Namespace namespace = (Namespace) namespaceStack.get(i); | |
| 2/2 2008x | if ( prefix.equals( namespace.getPrefix() ) ) { | |
| 1002x | return namespace; | |
| } | ||
| } | ||
| 1006x | return null; | |
| } | ||
| /** @return the URI for the given prefix or null if it | ||
| * could not be found. | ||
| */ | ||
| public String getURI( String prefix ) { | ||
| 0x | Namespace namespace = getNamespaceForPrefix( prefix ); | |
| 0/2 0x | return ( namespace != null ) ? namespace.getURI() : null; | |
| } | ||
| /** @return true if the given prefix is in the stack. | ||
| */ | ||
| public boolean contains( Namespace namespace ) { | ||
| 6630x | String prefix = namespace.getPrefix(); | |
| 6630x | Namespace current = null; | |
| 3/4 6630x | if ( prefix == null || prefix.length() == 0 ) { | |
| 4622x | current = getDefaultNamespace(); | |
| } | ||
| else { | ||
| 2008x | current = getNamespaceForPrefix( prefix ); | |
| } | ||
| 2/2 6630x | if ( current == null ) { | |
| 1006x | return false; | |
| } | ||
| 2/2 5624x | if ( current == namespace ) { | |
| 5610x | return true; | |
| } | ||
| 14x | return namespace.getURI().equals( current.getURI() ); | |
| } | ||
| public QName getQName( String namespaceURI, String localName, String qualifiedName ) { | ||
| 2/2 120522x | if ( localName == null ) { | |
| 22x | localName = qualifiedName; | |
| } | ||
| 1/2 120500x | else if ( qualifiedName == null ) { | |
| 0x | qualifiedName = localName; | |
| } | ||
| 2/2 120522x | if ( namespaceURI == null ) { | |
| 20x | namespaceURI = ""; | |
| } | ||
| 120522x | String prefix = ""; | |
| 120522x | int index = qualifiedName.indexOf(":"); | |
| 2/2 120522x | if (index > 0) { | |
| 22358x | prefix = qualifiedName.substring(0, index); | |
| 1/2 22358x | if (localName.trim().length() == 0) { | |
| 0x | localName = qualifiedName.substring(index+1); | |
| } | ||
| 1/2 98164x | } else if (localName.trim().length() == 0) { | |
| 0x | localName = qualifiedName; | |
| } | ||
| 120522x | Namespace namespace = createNamespace( prefix, namespaceURI ); | |
| 120522x | return pushQName( localName, qualifiedName, namespace, prefix ); | |
| } | ||
| public QName getAttributeQName( String namespaceURI, String localName, String qualifiedName ) { | ||
| 1/2 466x | if ( qualifiedName == null ) { | |
| 0x | qualifiedName = localName; | |
| } | ||
| 466x | Map map = getNamespaceCache(); | |
| 466x | QName answer = (QName) map.get( qualifiedName ); | |
| 2/2 466x | if ( answer != null ) { | |
| 322x | return answer; | |
| } | ||
| 1/2 144x | if ( localName == null ) { | |
| 0x | localName = qualifiedName; | |
| } | ||
| 1/2 144x | if ( namespaceURI == null ) { | |
| 0x | namespaceURI = ""; | |
| } | ||
| 144x | Namespace namespace = null; | |
| 144x | String prefix = ""; | |
| 144x | int index = qualifiedName.indexOf(":"); | |
| 2/2 144x | if (index > 0) { | |
| 46x | prefix = qualifiedName.substring(0, index); | |
| 46x | namespace = createNamespace( prefix, namespaceURI ); | |
| 1/2 46x | if ( localName.trim().length() == 0) { | |
| 0x | localName = qualifiedName.substring(index+1); | |
| } | ||
| } | ||
| else { | ||
| // attributes with no prefix have no namespace | ||
| 98x | namespace = Namespace.NO_NAMESPACE; | |
| 1/2 98x | if ( localName.trim().length() == 0) { | |
| 0x | localName = qualifiedName; | |
| } | ||
| } | ||
| 144x | answer = pushQName( localName, qualifiedName, namespace, prefix ); | |
| 144x | map.put( qualifiedName, answer ); | |
| 144x | return answer; | |
| } | ||
| /** Adds a namepace to the stack with the given prefix and URI */ | ||
| public void push( String prefix, String uri ) { | ||
| 1/2 11090x | if ( uri == null ) { | |
| 0x | uri = ""; | |
| } | ||
| 11090x | Namespace namespace = createNamespace( prefix, uri ); | |
| 11090x | push( namespace ); | |
| 11090x | } | |
| /** Adds a new namespace to the stack */ | ||
| public Namespace addNamespace( String prefix, String uri ) { | ||
| 4x | Namespace namespace = createNamespace( prefix, uri ); | |
| 4x | push( namespace ); | |
| 4x | return namespace; | |
| } | ||
| /** Pops a namepace from the stack with the given prefix and URI */ | ||
| public Namespace pop( String prefix ) { | ||
| 1/2 11090x | if ( prefix == null ) { | |
| 0x | prefix = ""; | |
| } | ||
| 11090x | Namespace namespace = null; | |
| 1/2 11124x | for (int i = namespaceStack.size() - 1; i >= 0; i-- ) { | |
| 11124x | Namespace ns = (Namespace) namespaceStack.get(i); | |
| 2/2 11124x | if ( prefix.equals( ns.getPrefix() ) ) { | |
| 11090x | remove(i); | |
| 11090x | namespace = ns; | |
| 11090x | break; | |
| } | ||
| } | ||
| 1/2 11090x | if ( namespace == null ) { | |
| 0x | System.out.println( "Warning: missing namespace prefix ignored: " + prefix ); | |
| } | ||
| 11090x | return namespace; | |
| } | ||
| public String toString() { | ||
| 0x | return super.toString() + " Stack: " + namespaceStack.toString(); | |
| } | ||
| public DocumentFactory getDocumentFactory() { | ||
| 0x | return documentFactory; | |
| } | ||
| public void setDocumentFactory(DocumentFactory documentFactory) { | ||
| 0x | this.documentFactory = documentFactory; | |
| 0x | } | |
| public Namespace getDefaultNamespace() { | ||
| 1/2 4622x | if ( defaultNamespace == null ) { | |
| 0x | defaultNamespace = findDefaultNamespace(); | |
| } | ||
| 4622x | return defaultNamespace; | |
| } | ||
| // Implementation methods | ||
| //------------------------------------------------------------------------- | ||
| /** Adds the QName to the stack of available QNames | ||
| */ | ||
| protected QName pushQName( String localName, String qualifiedName, Namespace namespace, String prefix ) { | ||
| 3/4 120666x | if ( prefix == null || prefix.length() == 0 ) { | |
| 98262x | this.defaultNamespace = null; | |
| } | ||
| 120666x | return createQName( localName, qualifiedName, namespace ); | |
| } | ||
| /** Factory method to creeate new QName instances. By default this method | ||
| * interns the QName | ||
| */ | ||
| protected QName createQName( String localName, String qualifiedName, Namespace namespace ) { | ||
| 120666x | return documentFactory.createQName( localName, namespace ); | |
| } | ||
| /** Factory method to creeate new Namespace instances. By default this method | ||
| * interns the Namespace | ||
| */ | ||
| protected Namespace createNamespace( String prefix, String namespaceURI ) { | ||
| 131662x | 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 | ||
| */ | ||
| protected Namespace findDefaultNamespace() { | ||
| 0/2 0x | for ( int i = namespaceStack.size() - 1; i >= 0; i-- ) { | |
| 0x | Namespace namespace = (Namespace) namespaceStack.get(i); | |
| 0/2 0x | if ( namespace != null ) { | |
| 0x | String prefix = namespace.getPrefix(); | |
| 0/4 0x | if ( prefix == null || namespace.getPrefix().length() == 0 ) { | |
| 0x | return namespace; | |
| } | ||
| } | ||
| } | ||
| 0x | return null; | |
| } | ||
| /** Removes the namespace at the given index of the stack */ | ||
| protected Namespace remove(int index) { | ||
| 12110x | Namespace namespace = (Namespace) namespaceStack.remove(index); | |
| 12110x | namespaceCacheList.remove(index); | |
| 12110x | defaultNamespace = null; | |
| 12110x | currentNamespaceCache = null; | |
| 12110x | return namespace; | |
| } | ||
| protected Map getNamespaceCache() { | ||
| 2/2 466x | if ( currentNamespaceCache == null ) { | |
| 110x | int index = namespaceStack.size() - 1; | |
| 2/2 110x | if ( index < 0 ) { | |
| 66x | currentNamespaceCache = rootNamespaceCache; | |
| } | ||
| else { | ||
| 44x | currentNamespaceCache = (Map) namespaceCacheList.get(index); | |
| 1/2 44x | if ( currentNamespaceCache == null ) { | |
| 44x | currentNamespaceCache = new HashMap(); | |
| 44x | namespaceCacheList.set(index, currentNamespaceCache); | |
| } | ||
| } | ||
| } | ||
| 466x | 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-2004 (C) MetaStuff, Ltd. All Rights Reserved. | ||
| * | ||
| * $Id: NamespaceStack.java,v 1.11 2004/06/25 08:03:41 maartenc Exp $ | ||
| */ |