View Javadoc

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