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