1   
2   
3   
4   
5   
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             
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     
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 
471 
472 
473 
474 
475 
476 
477 
478 
479 
480 
481 
482 
483 
484 
485 
486 
487 
488 
489 
490 
491 
492 
493 
494 
495 
496 
497 
498 
499 
500 
501 
502 
503 
504