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 java.util.HashMap;
69
70 import org.jaxen.pattern.Pattern;
71 import org.w3c.dom.DOMException;
72 import org.w3c.dom.Document;
73 import org.w3c.dom.NamedNodeMap;
74 import org.w3c.dom.Node;
75 import org.w3c.dom.NodeList;
76 import org.w3c.dom.UserDataHandler;
77
78
79 /***
80 * Extension DOM2/DOm3 node type for a namespace node.
81 *
82 * <p>This class implements the DOM2 and DOM3 {@link Node} interface
83 * to allow namespace nodes to be included in the result
84 * set of an XPath selectNodes operation, even though DOM does
85 * not model namespaces in scope as separate nodes.</p>
86 *
87 * <p>
88 * While all of the DOM2 methods are implemented with reasonable
89 * defaults, there will be some unexpected surprises, so users are
90 * advised to test for NamespaceNodes and filter them out from the
91 * result sets as early as possible.
92 * </p>
93 *
94 * <ol>
95 *
96 * <li>The {@link #getNodeType} method returns {@link #NAMESPACE_NODE},
97 * which is not one of the usual DOM2 node types. Generic code may
98 * fall unexpectedly out of switch statements, for example.</li>
99 *
100 * <li>The {@link #getOwnerDocument} method returns the owner document
101 * of the parent node, but that owner document will know nothing about
102 * the namespace node.</p>
103 *
104 * <li>The {@link #isSupported} method always returns false.</li>
105 *
106 * <li> The DOM3 methods sometimes throw UnsupportedOperationException.
107 * They're here only to allow this class to be compiled with Java 1.5.
108 * Do not call or rely on them.</li>
109 * </ol>
110 *
111 * <p>All attempts to modify a <code>NamespaceNode</code> will fail with a {@link
112 * DOMException} ({@link
113 * DOMException#NO_MODIFICATION_ALLOWED_ERR}).</p>
114 *
115 * @author David Megginson
116 * @author Elliotte Rusty Harold
117 * @see DocumentNavigator
118 */
119 public class NamespaceNode implements Node
120 {
121
122
123
124
125
126
127 /***
128 * Constant: this is a NamespaceNode.
129 *
130 * @see #getNodeType
131 */
132 public final static short NAMESPACE_NODE = Pattern.NAMESPACE_NODE;
133
134
135
136
137
138
139
140
141
142 /***
143 * Create a new NamespaceNode.
144 *
145 * @param parent the DOM node to which the namespace is attached
146 * @param name the namespace prefix
147 * @param value the namespace URI
148 */
149 public NamespaceNode (Node parent, String name, String value)
150 {
151 this.parent = parent;
152 this.name = name;
153 this.value = value;
154 }
155
156
157 /***
158 * Constructor.
159 *
160 * @param parent the DOM node to which the namespace is attached
161 * @param attribute the DOM attribute object containing the
162 * namespace declaration
163 */
164 NamespaceNode (Node parent, Node attribute)
165 {
166 String attributeName = attribute.getNodeName();
167
168 if (attributeName.equals("xmlns")) {
169 this.name = "";
170 }
171 else if (attributeName.startsWith("xmlns:")) {
172 this.name = attributeName.substring(6);
173 }
174 else {
175 this.name = attributeName;
176 }
177 this.parent = parent;
178 this.value = attribute.getNodeValue();
179 }
180
181
182
183
184
185
186
187
188 /***
189 * Get the namespace prefix.
190 *
191 * @return the namespace prefix, or "" for the default namespace
192 */
193 public String getNodeName ()
194 {
195 return name;
196 }
197
198
199 /***
200 * Get the namespace URI.
201 *
202 * @return the namespace URI
203 */
204 public String getNodeValue ()
205 {
206 return value;
207 }
208
209
210 /***
211 * Change the namespace URI (always fails).
212 *
213 * @param value the new URI
214 * @throws DOMException always
215 */
216 public void setNodeValue (String value) throws DOMException
217 {
218 disallowModification();
219 }
220
221
222 /***
223 * Get the node type.
224 *
225 * @return always {@link #NAMESPACE_NODE}.
226 */
227 public short getNodeType ()
228 {
229 return NAMESPACE_NODE;
230 }
231
232
233 /***
234 * Get the parent node.
235 *
236 * <p>This method returns the element that was queried for Namespaces
237 * in effect, <em>not</em> necessarily the actual element containing
238 * the Namespace declaration.</p>
239 *
240 * @return the parent node (not null)
241 */
242 public Node getParentNode ()
243 {
244 return parent;
245 }
246
247
248 /***
249 * Get the list of child nodes.
250 *
251 * @return an empty node list
252 */
253 public NodeList getChildNodes ()
254 {
255 return new EmptyNodeList();
256 }
257
258
259 /***
260 * Get the first child node.
261 *
262 * @return null
263 */
264 public Node getFirstChild ()
265 {
266 return null;
267 }
268
269
270 /***
271 * Get the last child node.
272 *
273 * @return null
274 */
275 public Node getLastChild ()
276 {
277 return null;
278 }
279
280
281 /***
282 * Get the previous sibling node.
283 *
284 * @return null
285 */
286 public Node getPreviousSibling ()
287 {
288 return null;
289 }
290
291
292 /***
293 * Get the next sibling node.
294 *
295 * @return null
296 */
297 public Node getNextSibling ()
298 {
299 return null;
300 }
301
302
303 /***
304 * Get the attribute nodes.
305 *
306 * @return null
307 */
308 public NamedNodeMap getAttributes ()
309 {
310 return null;
311 }
312
313
314 /***
315 * Get the owner document.
316 *
317 * @return the owner document <em>of the parent node</em>
318 */
319 public Document getOwnerDocument ()
320 {
321 if (parent == null) return null;
322 return parent.getOwnerDocument();
323 }
324
325
326 /***
327 * Insert a new child node (always fails).
328 *
329 * @param newChild the node to add
330 * @param refChild ignored
331 * @return never
332 * @throws DOMException always
333 * @see Node#insertBefore
334 */
335 public Node insertBefore (Node newChild, Node refChild)
336 throws DOMException
337 {
338 disallowModification();
339 return null;
340 }
341
342
343 /***
344 * Replace a child node (always fails).
345 *
346 * @param newChild the node to add
347 * @param oldChild the child node to replace
348 * @return never
349 * @throws DOMException always
350 * @see Node#replaceChild
351 */
352 public Node replaceChild (Node newChild, Node oldChild)
353 throws DOMException
354 {
355 disallowModification();
356 return null;
357 }
358
359
360 /***
361 * Remove a child node (always fails).
362 *
363 * @param oldChild the child node to remove
364 * @return never
365 * @throws DOMException always
366 * @see Node#removeChild
367 */
368 public Node removeChild (Node oldChild)
369 throws DOMException
370 {
371 disallowModification();
372 return null;
373 }
374
375
376 /***
377 * Append a new child node (always fails).
378 *
379 * @param newChild the node to add
380 * @return never
381 * @throws DOMException always
382 * @see Node#appendChild
383 */
384 public Node appendChild (Node newChild)
385 throws DOMException
386 {
387 disallowModification();
388 return null;
389 }
390
391
392 /***
393 * Test for child nodes.
394 *
395 * @return false
396 */
397 public boolean hasChildNodes ()
398 {
399 return false;
400 }
401
402
403 /***
404 * Create a copy of this node.
405 *
406 * @param deep make a deep copy (no effect, since namespace nodes
407 * don't have children).
408 * @return a new copy of this namespace node
409 */
410 public Node cloneNode (boolean deep)
411 {
412 return new NamespaceNode(parent, name, value);
413 }
414
415
416 /***
417 * Normalize the text descendants of this node.
418 *
419 * <p>This method has no effect, since namespace nodes have no
420 * descendants.</p>
421 */
422 public void normalize ()
423 {
424
425 }
426
427
428 /***
429 * Test if a DOM2 feature is supported.
430 *
431 * @param feature the feature name
432 * @param version the feature version
433 * @return false
434 */
435 public boolean isSupported (String feature, String version)
436 {
437 return false;
438 }
439
440
441 /***
442 * Get the namespace URI of this node.
443 *
444 * <p>Namespace declarations are not themselves
445 * Namespace-qualified.</p>
446 *
447 * @return null
448 */
449 public String getNamespaceURI ()
450 {
451 return null;
452 }
453
454
455 /***
456 * Get the namespace prefix of this node.
457 *
458 * <p>Namespace declarations are not themselves
459 * namespace-qualified.</p>
460 *
461 * @return null
462 * @see #getLocalName
463 */
464 public String getPrefix ()
465 {
466 return null;
467 }
468
469
470 /***
471 * Change the namespace prefix of this node (always fails).
472 *
473 * @param prefix the new prefix
474 * @throws DOMException always thrown
475 */
476 public void setPrefix (String prefix)
477 throws DOMException
478 {
479 disallowModification();
480 }
481
482
483 /***
484 * Get the XPath name of the namespace node;; i.e. the
485 * namespace prefix.
486 *
487 * @return the namespace prefix
488 */
489 public String getLocalName ()
490 {
491 return name;
492 }
493
494
495 /***
496 * Test if this node has attributes.
497 *
498 * @return false
499 */
500 public boolean hasAttributes ()
501 {
502 return false;
503 }
504
505
506 /***
507 * Throw a NO_MODIFICATION_ALLOWED_ERR DOMException.
508 *
509 * @throws DOMException always thrown
510 */
511 private void disallowModification () throws DOMException
512 {
513 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
514 "Namespace node may not be modified");
515 }
516
517
518
519
520
521
522
523
524 /***
525 * Generate a hash code for a namespace node.
526 *
527 * @return a hash code for this node
528 */
529 public int hashCode ()
530 {
531 return hashCode(parent) + hashCode(name) + hashCode(value);
532 }
533
534
535 /***
536 * Test for equivalence with another object.
537 *
538 * <p>Two Namespace nodes are considered equivalent if their parents,
539 * names, and values are equal.</p>
540 *
541 * @param o the object to test for equality
542 * @return true if the object is equivalent to this node, false
543 * otherwise
544 */
545 public boolean equals (Object o)
546 {
547 if (o == this) return true;
548 else if (o == null) return false;
549 else if (o instanceof NamespaceNode) {
550 NamespaceNode ns = (NamespaceNode)o;
551 return (equals(parent, ns.getParentNode()) &&
552 equals(name, ns.getNodeName()) &&
553 equals(value, ns.getNodeValue()));
554 } else {
555 return false;
556 }
557 }
558
559
560 /***
561 * Helper method for generating a hash code.
562 *
563 * @param o the object for generating a hash code (possibly null)
564 * @return the object's hash code, or 0 if the object is null
565 * @see java.lang.Object#hashCode
566 */
567 private int hashCode (Object o)
568 {
569 return (o == null ? 0 : o.hashCode());
570 }
571
572
573 /***
574 * Helper method for comparing two objects.
575 *
576 * @param a the first object to compare (possibly null)
577 * @param b the second object to compare (possibly null)
578 * @return true if the objects are equivalent or are both null
579 * @see java.lang.Object#equals
580 */
581 private boolean equals (Object a, Object b)
582 {
583 return ((a == null && b == null) ||
584 (a != null && a.equals(b)));
585 }
586
587
588
589
590
591
592 private Node parent;
593 private String name;
594 private String value;
595
596
597
598
599
600
601
602
603 /***
604 * A node list with no members.
605 *
606 * <p>This class is necessary for the {@link Node#getChildNodes}
607 * method, which must return an empty node list rather than
608 * null when there are no children.</p>
609 */
610 private static class EmptyNodeList implements NodeList
611 {
612
613 /***
614 * @see NodeList#getLength
615 */
616 public int getLength ()
617 {
618 return 0;
619 }
620
621
622 /***
623 * @see NodeList#item
624 */
625 public Node item(int index)
626 {
627 return null;
628 }
629
630 }
631
632
633
634
635
636 /***
637 * Return the base URI of the document containing this node (always fails).
638 *
639 * @return null
640 */
641 public String getBaseURI() {
642
643 return null;
644 }
645
646
647 /***
648 * Compare relative position of this node to another nbode. (Always fails).
649 *
650 * @return never
651 * @throws DOMException NOT_SUPPORTED_ERR
652 */
653 public short compareDocumentPosition(Node other) throws DOMException {
654 DOMException ex = new DOMException(
655 DOMException.NOT_SUPPORTED_ERR,
656 "DOM level 3 interfaces are not fully implemented in Jaxen's NamespaceNode class"
657 );
658 throw ex;
659 }
660
661
662 /***
663 * Return the namespace URI.
664 *
665 * @return the namespace URI
666 * @see #getNodeValue
667 */
668 public String getTextContent() {
669 return value;
670 }
671
672
673 /***
674 * Change the value of this node (always fails).
675 *
676 * @param textContent the new content
677 * @throws DOMException always
678 */
679 public void setTextContent(String textContent) throws DOMException {
680 disallowModification();
681 }
682
683
684 /***
685 * Returns true if and only if this object represents the same XPath namespace node
686 * as the argument; that is, they have the same parent, the same prefix, and the
687 * same URI.
688 *
689 * @param other the node to compare to
690 * @return true if this object represents the same XPath namespace node
691 * as other; false otherwise
692 */
693 public boolean isSameNode(Node other) {
694 return this.isEqualNode(other)
695
696
697 && this.getParentNode() == other.getParentNode();
698 }
699
700
701 /***
702 * Return the prefix bound to this namespace URI within the scope
703 * of this node (always fails).
704 *
705 * @return never
706 * @throws UnsupportedOperationException always
707 */
708 public String lookupPrefix(String namespaceURI) {
709
710
711
712 throw new UnsupportedOperationException("Changing interfaces in a JDK blows chunks!");
713 }
714
715
716 /***
717 * Return true if the specified URI is the default namespace in
718 * scope (always fails).
719 *
720 * @return never
721 * @throws UnsupportedOperationException always
722 */
723 public boolean isDefaultNamespace(String namespaceURI) {
724 return namespaceURI.equals(this.lookupNamespaceURI(null));
725 }
726
727
728 /***
729 * Return the namespace URI mapped to the specified
730 * prefix within the scope of this namespace node (always fails).
731 *
732 * @return never
733 * @throws UnsupportedOperationException always
734 */
735 public String lookupNamespaceURI(String prefix) {
736
737
738
739 throw new UnsupportedOperationException("Changing interfaces in a JDK blows chunks!");
740 }
741
742
743 /***
744 * Returns true if this object binds the same prefix to the same URI.
745 * That is, this object has the same prefix and URI as the argument.
746 *
747 * @param arg the node to compare to
748 * @return true if this object has the same prefix and URI as the argument; false otherwise
749 */
750 public boolean isEqualNode(Node arg) {
751 if (arg.getNodeType() == this.getNodeType()) {
752 NamespaceNode other = (NamespaceNode) arg;
753 if (other.name == null && this.name != null) return false;
754 else if (other.name != null && this.name == null) return false;
755 else if (other.value == null && this.value != null) return false;
756 else if (other.value != null && this.value == null) return false;
757 else if (other.name == null && this.name == null) {
758 return other.value.equals(this.value);
759 }
760
761 return other.name.equals(this.name) && other.value.equals(this.value);
762 }
763 return false;
764 }
765
766
767 /***
768 * Returns the value of the requested feature. Always returns null.
769 *
770 * @return null
771 */
772 public Object getFeature(String feature, String version) {
773 return null;
774 }
775
776
777
778 private HashMap userData = new HashMap();
779
780 /***
781 * Associates an object with a key.
782 *
783 * @param key the key by which the data will be retrieved
784 * @param data the object to store with the key
785 * @param handler ignored since nnamespace ndoes cannot be imported, cloned, or renamed
786 *
787 * @return the value previously associated with this key; or null
788 * if there isn't any such previous value
789 */
790 public Object setUserData(String key, Object data, UserDataHandler handler) {
791 Object oldValue = getUserData(key);
792 userData.put(key, data);
793 return oldValue;
794 }
795
796
797 /***
798 * Returns the user data associated with the given key.
799 *
800 * @return the object associated with the key; or null if no such object is available
801 */
802 public Object getUserData(String key) {
803 return userData.get(key);
804 }
805
806 }
807
808