View Javadoc

    1   /*
    2    * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
    3    *
    4    * This software is open source.
    5    * See the bottom of this file for the licence.
    6    */
    7
    8   package org.dom4j.tree;
    9
    10  import java.util.ArrayList;
    11  import java.util.HashMap;
    12  import java.util.Map;
    13
    14  import org.dom4j.DocumentFactory;
    15  import org.dom4j.Namespace;
    16  import org.dom4j.QName;
    17
    18  /***
    19   * NamespaceStack implements a stack of namespaces and optionally maintains a
    20   * cache of all the fully qualified names (<code>QName</code>) which are in
    21   * scope. This is useful when building or navigating a <i>dom4j </i> document.
    22   *
    23   * @author <a href="mailto:jstrachan@apache.org">James Strachan </a>
    24   * @version $Revision: 1.13 $
    25   */
    26  public class NamespaceStack {
    27      /*** The factory used to create new <code>Namespace</code> instances */
    28      private DocumentFactory documentFactory;
    29
    30      /*** The Stack of namespaces */
    31      private ArrayList namespaceStack = new ArrayList();
    32
    33      /*** The cache of qualifiedNames to QNames per namespace context */
    34      private ArrayList namespaceCacheList = new ArrayList();
    35
    36      /***
    37       * A cache of current namespace context cache of mapping from qualifiedName
    38       * to QName
    39       */
    40      private Map currentNamespaceCache;
    41
    42      /***
    43       * A cache of mapping from qualifiedName to QName before any namespaces are
    44       * declared
    45       */
    46      private Map rootNamespaceCache = new HashMap();
    47
    48      /*** Caches the default namespace defined via xmlns="" */
    49      private Namespace defaultNamespace;
    50
    51      public NamespaceStack() {
    52          this.documentFactory = DocumentFactory.getInstance();
    53      }
    54
    55      public NamespaceStack(DocumentFactory documentFactory) {
    56          this.documentFactory = documentFactory;
    57      }
    58
    59      /***
    60       * Pushes the given namespace onto the stack so that its prefix becomes
    61       * available.
    62       *
    63       * @param namespace
    64       *            is the <code>Namespace</code> to add to the stack.
    65       */
    66      public void push(Namespace namespace) {
    67          namespaceStack.add(namespace);
    68          namespaceCacheList.add(null);
    69          currentNamespaceCache = null;
    70
    71          String prefix = namespace.getPrefix();
    72
    73          if ((prefix == null) || (prefix.length() == 0)) {
    74              defaultNamespace = namespace;
    75          }
    76      }
    77
    78      /***
    79       * Pops the most recently used <code>Namespace</code> from the stack
    80       *
    81       * @return Namespace popped from the stack
    82       */
    83      public Namespace pop() {
    84          return remove(namespaceStack.size() - 1);
    85      }
    86
    87      /***
    88       * DOCUMENT ME!
    89       *
    90       * @return the number of namespaces on the stackce stack.
    91       */
    92      public int size() {
    93          return namespaceStack.size();
    94      }
    95
    96      /***
    97       * Clears the stack
    98       */
    99      public void clear() {
    100         namespaceStack.clear();
    101         namespaceCacheList.clear();
    102         rootNamespaceCache.clear();
    103         currentNamespaceCache = null;
    104     }
    105
    106     /***
    107      * DOCUMENT ME!
    108      *
    109      * @param index
    110      *            DOCUMENT ME!
    111      *
    112      * @return the namespace at the specified index on the stack
    113      */
    114     public Namespace getNamespace(int index) {
    115         return (Namespace) namespaceStack.get(index);
    116     }
    117
    118     /***
    119      * DOCUMENT ME!
    120      *
    121      * @param prefix
    122      *            DOCUMENT ME!
    123      *
    124      * @return the namespace for the given prefix or null if it could not be
    125      *         found.
    126      */
    127     public Namespace getNamespaceForPrefix(String prefix) {
    128         if (prefix == null) {
    129             prefix = "";
    130         }
    131
    132         for (int i = namespaceStack.size() - 1; i >= 0; i--) {
    133             Namespace namespace = (Namespace) namespaceStack.get(i);
    134
    135             if (prefix.equals(namespace.getPrefix())) {
    136                 return namespace;
    137             }
    138         }
    139
    140         return null;
    141     }
    142
    143     /***
    144      * DOCUMENT ME!
    145      *
    146      * @param prefix
    147      *            DOCUMENT ME!
    148      *
    149      * @return the URI for the given prefix or null if it could not be found.
    150      */
    151     public String getURI(String prefix) {
    152         Namespace namespace = getNamespaceForPrefix(prefix);
    153
    154         return (namespace != null) ? namespace.getURI() : null;
    155     }
    156
    157     /***
    158      * DOCUMENT ME!
    159      *
    160      * @param namespace
    161      *            DOCUMENT ME!
    162      *
    163      * @return true if the given prefix is in the stack.
    164      */
    165     public boolean contains(Namespace namespace) {
    166         String prefix = namespace.getPrefix();
    167         Namespace current = null;
    168
    169         if ((prefix == null) || (prefix.length() == 0)) {
    170             current = getDefaultNamespace();
    171         } else {
    172             current = getNamespaceForPrefix(prefix);
    173         }
    174
    175         if (current == null) {
    176             return false;
    177         }
    178
    179         if (current == namespace) {
    180             return true;
    181         }
    182
    183         return namespace.getURI().equals(current.getURI());
    184     }
    185
    186     public QName getQName(String namespaceURI, String localName,
    187             String qualifiedName) {
    188         if (localName == null) {
    189             localName = qualifiedName;
    190         } else if (qualifiedName == null) {
    191             qualifiedName = localName;
    192         }
    193
    194         if (namespaceURI == null) {
    195             namespaceURI = "";
    196         }
    197
    198         String prefix = "";
    199         int index = qualifiedName.indexOf(":");
    200
    201         if (index > 0) {
    202             prefix = qualifiedName.substring(0, index);
    203
    204             if (localName.trim().length() == 0) {
    205                 localName = qualifiedName.substring(index + 1);
    206             }
    207         } else if (localName.trim().length() == 0) {
    208             localName = qualifiedName;
    209         }
    210
    211         Namespace namespace = createNamespace(prefix, namespaceURI);
    212
    213         return pushQName(localName, qualifiedName, namespace, prefix);
    214     }
    215
    216     public QName getAttributeQName(String namespaceURI, String localName,
    217             String qualifiedName) {
    218         if (qualifiedName == null) {
    219             qualifiedName = localName;
    220         }
    221
    222         Map map = getNamespaceCache();
    223         QName answer = (QName) map.get(qualifiedName);
    224
    225         if (answer != null) {
    226             return answer;
    227         }
    228
    229         if (localName == null) {
    230             localName = qualifiedName;
    231         }
    232
    233         if (namespaceURI == null) {
    234             namespaceURI = "";
    235         }
    236
    237         Namespace namespace = null;
    238         String prefix = "";
    239         int index = qualifiedName.indexOf(":");
    240
    241         if (index > 0) {
    242             prefix = qualifiedName.substring(0, index);
    243             namespace = createNamespace(prefix, namespaceURI);
    244
    245             if (localName.trim().length() == 0) {
    246                 localName = qualifiedName.substring(index + 1);
    247             }
    248         } else {
    249             // attributes with no prefix have no namespace
    250             namespace = Namespace.NO_NAMESPACE;
    251
    252             if (localName.trim().length() == 0) {
    253                 localName = qualifiedName;
    254             }
    255         }
    256
    257         answer = pushQName(localName, qualifiedName, namespace, prefix);
    258         map.put(qualifiedName, answer);
    259
    260         return answer;
    261     }
    262
    263     /***
    264      * Adds a namepace to the stack with the given prefix and URI
    265      *
    266      * @param prefix
    267      *            DOCUMENT ME!
    268      * @param uri
    269      *            DOCUMENT ME!
    270      */
    271     public void push(String prefix, String uri) {
    272         if (uri == null) {
    273             uri = "";
    274         }
    275
    276         Namespace namespace = createNamespace(prefix, uri);
    277         push(namespace);
    278     }
    279
    280     /***
    281      * Adds a new namespace to the stack
    282      *
    283      * @param prefix
    284      *            DOCUMENT ME!
    285      * @param uri
    286      *            DOCUMENT ME!
    287      *
    288      * @return DOCUMENT ME!
    289      */
    290     public Namespace addNamespace(String prefix, String uri) {
    291         Namespace namespace = createNamespace(prefix, uri);
    292         push(namespace);
    293
    294         return namespace;
    295     }
    296
    297     /***
    298      * Pops a namepace from the stack with the given prefix and URI
    299      *
    300      * @param prefix
    301      *            DOCUMENT ME!
    302      *
    303      * @return DOCUMENT ME!
    304      */
    305     public Namespace pop(String prefix) {
    306         if (prefix == null) {
    307             prefix = "";
    308         }
    309
    310         Namespace namespace = null;
    311
    312         for (int i = namespaceStack.size() - 1; i >= 0; i--) {
    313             Namespace ns = (Namespace) namespaceStack.get(i);
    314
    315             if (prefix.equals(ns.getPrefix())) {
    316                 remove(i);
    317                 namespace = ns;
    318
    319                 break;
    320             }
    321         }
    322
    323         if (namespace == null) {
    324             System.out.println("Warning: missing namespace prefix ignored: "
    325                     + prefix);
    326         }
    327
    328         return namespace;
    329     }
    330
    331     public String toString() {
    332         return super.toString() + " Stack: " + namespaceStack.toString();
    333     }
    334
    335     public DocumentFactory getDocumentFactory() {
    336         return documentFactory;
    337     }
    338
    339     public void setDocumentFactory(DocumentFactory documentFactory) {
    340         this.documentFactory = documentFactory;
    341     }
    342
    343     public Namespace getDefaultNamespace() {
    344         if (defaultNamespace == null) {
    345             defaultNamespace = findDefaultNamespace();
    346         }
    347
    348         return defaultNamespace;
    349     }
    350
    351     // Implementation methods
    352     // -------------------------------------------------------------------------
    353
    354     /***
    355      * Adds the QName to the stack of available QNames
    356      *
    357      * @param localName
    358      *            DOCUMENT ME!
    359      * @param qualifiedName
    360      *            DOCUMENT ME!
    361      * @param namespace
    362      *            DOCUMENT ME!
    363      * @param prefix
    364      *            DOCUMENT ME!
    365      *
    366      * @return DOCUMENT ME!
    367      */
    368     protected QName pushQName(String localName, String qualifiedName,
    369             Namespace namespace, String prefix) {
    370         if ((prefix == null) || (prefix.length() == 0)) {
    371             this.defaultNamespace = null;
    372         }
    373
    374         return createQName(localName, qualifiedName, namespace);
    375     }
    376
    377     /***
    378      * Factory method to creeate new QName instances. By default this method
    379      * interns the QName
    380      *
    381      * @param localName
    382      *            DOCUMENT ME!
    383      * @param qualifiedName
    384      *            DOCUMENT ME!
    385      * @param namespace
    386      *            DOCUMENT ME!
    387      *
    388      * @return DOCUMENT ME!
    389      */
    390     protected QName createQName(String localName, String qualifiedName,
    391             Namespace namespace) {
    392         return documentFactory.createQName(localName, namespace);
    393     }
    394
    395     /***
    396      * Factory method to creeate new Namespace instances. By default this method
    397      * interns the Namespace
    398      *
    399      * @param prefix
    400      *            DOCUMENT ME!
    401      * @param namespaceURI
    402      *            DOCUMENT ME!
    403      *
    404      * @return DOCUMENT ME!
    405      */
    406     protected Namespace createNamespace(String prefix, String namespaceURI) {
    407         return documentFactory.createNamespace(prefix, namespaceURI);
    408     }
    409
    410     /***
    411      * Attempts to find the current default namespace on the stack right now or
    412      * returns null if one could not be found
    413      *
    414      * @return DOCUMENT ME!
    415      */
    416     protected Namespace findDefaultNamespace() {
    417         for (int i = namespaceStack.size() - 1; i >= 0; i--) {
    418             Namespace namespace = (Namespace) namespaceStack.get(i);
    419
    420             if (namespace != null) {
    421                 String prefix = namespace.getPrefix();
    422
    423                 if ((prefix == null) || (namespace.getPrefix().length() == 0)) {
    424                     return namespace;
    425                 }
    426             }
    427         }
    428
    429         return null;
    430     }
    431
    432     /***
    433      * Removes the namespace at the given index of the stack
    434      *
    435      * @param index
    436      *            DOCUMENT ME!
    437      *
    438      * @return DOCUMENT ME!
    439      */
    440     protected Namespace remove(int index) {
    441         Namespace namespace = (Namespace) namespaceStack.remove(index);
    442         namespaceCacheList.remove(index);
    443         defaultNamespace = null;
    444         currentNamespaceCache = null;
    445
    446         return namespace;
    447     }
    448
    449     protected Map getNamespaceCache() {
    450         if (currentNamespaceCache == null) {
    451             int index = namespaceStack.size() - 1;
    452
    453             if (index < 0) {
    454                 currentNamespaceCache = rootNamespaceCache;
    455             } else {
    456                 currentNamespaceCache = (Map) namespaceCacheList.get(index);
    457
    458                 if (currentNamespaceCache == null) {
    459                     currentNamespaceCache = new HashMap();
    460                     namespaceCacheList.set(index, currentNamespaceCache);
    461                 }
    462             }
    463         }
    464
    465         return currentNamespaceCache;
    466     }
    467 }
    468
    469 /*
    470  * Redistribution and use of this software and associated documentation
    471  * ("Software"), with or without modification, are permitted provided that the
    472  * following conditions are met:
    473  *
    474  * 1. Redistributions of source code must retain copyright statements and
    475  * notices. Redistributions must also contain a copy of this document.
    476  *
    477  * 2. Redistributions in binary form must reproduce the above copyright notice,
    478  * this list of conditions and the following disclaimer in the documentation
    479  * and/or other materials provided with the distribution.
    480  *
    481  * 3. The name "DOM4J" must not be used to endorse or promote products derived
    482  * from this Software without prior written permission of MetaStuff, Ltd. For
    483  * written permission, please contact dom4j-info@metastuff.com.
    484  *
    485  * 4. Products derived from this Software may not be called "DOM4J" nor may
    486  * "DOM4J" appear in their names without prior written permission of MetaStuff,
    487  * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
    488  *
    489  * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
    490  *
    491  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
    492  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    493  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    494  * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
    495  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    496  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    497  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    498  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    499  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    500  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    501  * POSSIBILITY OF SUCH DAMAGE.
    502  *
    503  * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
    504  */