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