1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 package org.jaxen.dom;
67
68 import org.jaxen.pattern.Pattern;
69 import org.w3c.dom.DOMException;
70 import org.w3c.dom.Document;
71 import org.w3c.dom.NamedNodeMap;
72 import org.w3c.dom.Node;
73 import org.w3c.dom.NodeList;
74
75
76 /***
77 * Extension DOM2 node type for a namespace declaration.
78 *
79 * <p>This class implements the DOM2 {@link Node} interface to
80 * allow Namespace declarations to be included in the result
81 * set of an XPath selectNodes operation, even though DOM2 does
82 * not model Namespace declarations as separate nodes.</p>
83 *
84 * <p>While all of the methods are implemented with reasonable
85 * defaults, there will be some unexpected surprises, so users are
86 * advised to test for NamespaceNodes and filter them out from the
87 * result sets as early as possible:</p>
88 *
89 * <ol>
90 *
91 * <li>The {@link #getNodeType} method returns {@link #NAMESPACE_NODE},
92 * which is not one of the usual DOM2 node types. Generic code may
93 * fall unexpectedly out of switch statements, for example.</li>
94 *
95 * <li>The {@link #getOwnerDocument} method returns the owner document
96 * of the parent node, but that owner document will know nothing about
97 * the Namespace node.</p>
98 *
99 * <li>The {@link #isSupported} method always returns false.</li>
100 *
101 * </ol>
102 *
103 * <p>All attempts to modify a NamespaceNode will fail with a {@link
104 * DOMException} ({@link
105 * DOMException#NO_MODIFICATION_ALLOWED_ERR}).</p>
106 *
107 * <p>This class has only protected constructors, so that it can be
108 * instantiated only by {@link DocumentNavigator}.</p>
109 *
110 * @author David Megginson
111 * @see DocumentNavigator
112 */
113 public class NamespaceNode implements Node
114 {
115
116
117
118
119
120
121 /***
122 * Constant: this is a NamespaceNode.
123 *
124 * @see #getNodeType
125 */
126 public final static short NAMESPACE_NODE = Pattern.NAMESPACE_NODE;
127
128
129
130
131
132
133
134
135 /***
136 * Constructor.
137 *
138 * @param parent the DOM node to which the namespace is attached
139 * @param name the namespace prefix
140 * @param value the namespace URI
141 */
142 public NamespaceNode (Node parent, String name, String value)
143 {
144 this.parent = parent;
145 this.name = name;
146 this.value = value;
147 }
148
149
150 /***
151 * Constructor.
152 *
153 * @param parent the DOM node to which the namespace is attached
154 * @param attribute the DOM attribute object containing the
155 * namespace declaration
156 */
157 NamespaceNode (Node parent, Node attribute)
158 {
159 String name = attribute.getNodeName();
160
161 if (name.equals("xmlns")) {
162 this.name = "";
163 }
164 else {
165 this.name = name.substring(6);
166 }
167 this.parent = parent;
168 this.value = attribute.getNodeValue();
169 }
170
171
172
173
174
175
176
177
178 /***
179 * Get the namespace prefix.
180 *
181 * @return the namespace prefix, or "" for the default namespace
182 */
183 public String getNodeName ()
184 {
185 return name;
186 }
187
188
189 /***
190 * Get the namespace URI.
191 *
192 * @return the Namespace URI
193 */
194 public String getNodeValue ()
195 {
196 return value;
197 }
198
199
200 /***
201 * Change the Namespace URI (always fails).
202 *
203 * @param value the new URI
204 * @throws DOMException always thrown
205 */
206 public void setNodeValue (String value) throws DOMException
207 {
208 disallowModification();
209 }
210
211
212 /***
213 * Get the node type.
214 *
215 * @return Always {@link #NAMESPACE_NODE}.
216 */
217 public short getNodeType ()
218 {
219 return NAMESPACE_NODE;
220 }
221
222
223 /***
224 * Get the parent node.
225 *
226 * <p>This method returns the element that was queried for Namespaces
227 * in effect, <em>not</em> necessarily the actual element containing
228 * the Namespace declaration.</p>
229 *
230 * @return the parent node (not null)
231 */
232 public Node getParentNode ()
233 {
234 return parent;
235 }
236
237
238 /***
239 * Get the list of child nodes.
240 *
241 * @return an empty node list
242 */
243 public NodeList getChildNodes ()
244 {
245 return new EmptyNodeList();
246 }
247
248
249 /***
250 * Get the first child node.
251 *
252 * @return null
253 */
254 public Node getFirstChild ()
255 {
256 return null;
257 }
258
259
260 /***
261 * Get the last child node.
262 *
263 * @return null
264 */
265 public Node getLastChild ()
266 {
267 return null;
268 }
269
270
271 /***
272 * Get the previous sibling node.
273 *
274 * @return null
275 */
276 public Node getPreviousSibling ()
277 {
278 return null;
279 }
280
281
282 /***
283 * Get the next sibling node.
284 *
285 * @return null
286 */
287 public Node getNextSibling ()
288 {
289 return null;
290 }
291
292
293 /***
294 * Get the attribute nodes.
295 *
296 * @return null
297 */
298 public NamedNodeMap getAttributes ()
299 {
300 return null;
301 }
302
303
304 /***
305 * Get the owner document.
306 *
307 * @return the owner document <em>of the parent node</em>
308 */
309 public Document getOwnerDocument ()
310 {
311
312 return (parent == null ? null : parent.getOwnerDocument());
313 }
314
315
316 /***
317 * Insert a new child node (always fails).
318 *
319 * @throws DOMException always thrown
320 * @see Node#insertBefore
321 */
322 public Node insertBefore (Node newChild, Node refChild)
323 throws DOMException
324 {
325 disallowModification();
326 return null;
327 }
328
329
330 /***
331 * Replace a child node (always fails).
332 *
333 * @throws DOMException always thrown
334 * @see Node#replaceChild
335 */
336 public Node replaceChild (Node newChild, Node oldChild)
337 throws DOMException
338 {
339 disallowModification();
340 return null;
341 }
342
343
344 /***
345 * Remove a child node (always fails).
346 *
347 * @throws DOMException always thrown
348 * @see Node#removeChild
349 */
350 public Node removeChild (Node oldChild)
351 throws DOMException
352 {
353 disallowModification();
354 return null;
355 }
356
357
358 /***
359 * Append a new child node (always fails).
360 *
361 * @throws DOMException always thrown
362 * @see Node#appendChild
363 */
364 public Node appendChild (Node newChild)
365 throws DOMException
366 {
367 disallowModification();
368 return null;
369 }
370
371
372 /***
373 * Test for child nodes.
374 *
375 * @return false
376 */
377 public boolean hasChildNodes ()
378 {
379 return false;
380 }
381
382
383 /***
384 * Create a copy of this node.
385 *
386 * @param deep Make a deep copy (no effect, since Namespace nodes
387 * don't have children).
388 * @return a new copy of this Namespace node
389 */
390 public Node cloneNode (boolean deep)
391 {
392 return new NamespaceNode(parent, name, value);
393 }
394
395
396 /***
397 * Normalize the text descendants of this node.
398 *
399 * <p>This method has no effect, since Namespace nodes have no
400 * descendants.</p>
401 */
402 public void normalize ()
403 {
404
405 }
406
407
408 /***
409 * Test if a DOM2 feature is supported.
410 *
411 * @param feature the feature name
412 * @param version the feature version
413 * @return false
414 */
415 public boolean isSupported (String feature, String version)
416 {
417 return false;
418 }
419
420
421 /***
422 * Get the Namespace URI for this node.
423 *
424 * <p>Namespace declarations are not themselves
425 * Namespace-qualified.</p>
426 *
427 * @return null
428 */
429 public String getNamespaceURI ()
430 {
431 return null;
432 }
433
434
435 /***
436 * Get the namespace prefix for this node.
437 *
438 * <p>Namespace declarations are not themselves
439 * Namespace-qualified.</p>
440 *
441 * @return null
442 */
443 public String getPrefix ()
444 {
445 return null;
446 }
447
448
449 /***
450 * Change the Namespace prefix for this node (always fails).
451 *
452 * @param prefix the new prefix
453 * @throws DOMException always thrown
454 */
455 public void setPrefix (String prefix)
456 throws DOMException
457 {
458 disallowModification();
459 }
460
461
462 /***
463 * Get the local name for this node.
464 *
465 * @return null
466 */
467 public String getLocalName ()
468 {
469 return name;
470 }
471
472
473 /***
474 * Test if this node has attributes.
475 *
476 * @return false
477 */
478 public boolean hasAttributes ()
479 {
480 return false;
481 }
482
483
484 /***
485 * Throw a NO_MODIFICATION_ALLOWED_ERR DOMException.
486 *
487 * @throws DOMException always thrown
488 */
489 private void disallowModification () throws DOMException
490 {
491 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
492 "Namespace node may not be modified");
493 }
494
495
496
497
498
499
500
501
502 /***
503 * Generate a hash code for a Namespace node.
504 *
505 * <p>The hash code is the sum of the hash codes of the parent node,
506 * name, and value.</p>
507 *
508 * @return a hash code for this node
509 */
510 public int hashCode ()
511 {
512 return hashCode(parent) + hashCode(name) + hashCode(value);
513 }
514
515
516 /***
517 * Test for equivalence with another object.
518 *
519 * <p>Two Namespace nodes are considered equivalent if their parents,
520 * names, and values are equal.</p>
521 *
522 * @param o The object to test for equality.
523 * @return true if the object is equivalent to this node, false
524 * otherwise.
525 */
526 public boolean equals (Object o)
527 {
528 if (o == this) return true;
529 else if (o == null) return false;
530 else if (o instanceof NamespaceNode) {
531 NamespaceNode ns = (NamespaceNode)o;
532 return (equals(parent, ns.getParentNode()) &&
533 equals(name, ns.getNodeName()) &&
534 equals(value, ns.getNodeValue()));
535 } else {
536 return false;
537 }
538 }
539
540
541 /***
542 * Helper method for generating a hash code.
543 *
544 * @param o the object for generating a hash code (possibly null)
545 * @return the object's hash code, or 0 if the object is null
546 * @see java.lang.Object#hashCode
547 */
548 private int hashCode (Object o)
549 {
550 return (o == null ? 0 : o.hashCode());
551 }
552
553
554 /***
555 * Helper method for comparing two objects.
556 *
557 * @param a the first object to compare (possibly null)
558 * @param b the second object to compare (possibly null)
559 * @return true if the objects are equivalent or are both null
560 * @see java.lang.Object#equals
561 */
562 private boolean equals (Object a, Object b)
563 {
564 return ((a == null && b == null) ||
565 (a != null && a.equals(b)));
566 }
567
568
569
570
571
572
573 private Node parent;
574 private String name;
575 private String value;
576
577
578
579
580
581
582
583
584 /***
585 * A node list with no members.
586 *
587 * <p>This class is necessary for the {@link Node#getChildNodes}
588 * method, which must return an empty node list rather than
589 * null when there are no children.</p>
590 */
591 class EmptyNodeList implements NodeList
592 {
593
594 /***
595 * @see NodeList#getLength
596 */
597 public int getLength ()
598 {
599 return 0;
600 }
601
602
603 /***
604 * @see NodeList#item
605 */
606 public Node item(int index)
607 {
608 return null;
609 }
610
611 }
612 }
613
614