Project dom4j 1.5.2 [5/2/05 10:13 PM]
 
Coverage - org/dom4j/tree/NamespaceStack.java
  /*
   * 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 $
   */