1
2
3
4
5
6
7
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
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
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
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378