1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jxpath.ri.model.dom;
18
19 import java.util.HashMap;
20 import java.util.Locale;
21 import java.util.Map;
22
23 import org.apache.commons.jxpath.JXPathAbstractFactoryException;
24 import org.apache.commons.jxpath.JXPathContext;
25 import org.apache.commons.jxpath.JXPathException;
26 import org.apache.commons.jxpath.Pointer;
27 import org.apache.commons.jxpath.ri.Compiler;
28 import org.apache.commons.jxpath.ri.NamespaceResolver;
29 import org.apache.commons.jxpath.ri.QName;
30 import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
31 import org.apache.commons.jxpath.ri.compiler.NodeTest;
32 import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
33 import org.apache.commons.jxpath.ri.compiler.ProcessingInstructionTest;
34 import org.apache.commons.jxpath.ri.model.NodeIterator;
35 import org.apache.commons.jxpath.ri.model.NodePointer;
36 import org.apache.commons.jxpath.ri.model.beans.NullPointer;
37 import org.apache.commons.jxpath.util.TypeUtils;
38 import org.w3c.dom.Attr;
39 import org.w3c.dom.Comment;
40 import org.w3c.dom.Document;
41 import org.w3c.dom.Element;
42 import org.w3c.dom.NamedNodeMap;
43 import org.w3c.dom.Node;
44 import org.w3c.dom.NodeList;
45 import org.w3c.dom.ProcessingInstruction;
46
47
48
49
50
51
52
53
54 public class DOMNodePointer extends NodePointer {
55
56 private static final long serialVersionUID = -8751046933894857319L;
57
58 private Node node;
59 private Map namespaces;
60 private String defaultNamespace;
61 private String id;
62 private NamespaceResolver localNamespaceResolver;
63
64
65 public static final String XML_NAMESPACE_URI =
66 "http://www.w3.org/XML/1998/namespace";
67
68
69 public static final String XMLNS_NAMESPACE_URI =
70 "http://www.w3.org/2000/xmlns/";
71
72
73
74
75
76
77 public DOMNodePointer(Node node, Locale locale) {
78 super(null, locale);
79 this.node = node;
80 }
81
82
83
84
85
86
87
88 public DOMNodePointer(Node node, Locale locale, String id) {
89 super(null, locale);
90 this.node = node;
91 this.id = id;
92 }
93
94
95
96
97
98
99 public DOMNodePointer(NodePointer parent, Node node) {
100 super(parent);
101 this.node = node;
102 }
103
104 public boolean testNode(NodeTest test) {
105 return testNode(node, test);
106 }
107
108
109
110
111
112
113
114 public static boolean testNode(Node node, NodeTest test) {
115 if (test == null) {
116 return true;
117 }
118 if (test instanceof NodeNameTest) {
119 if (node.getNodeType() != Node.ELEMENT_NODE) {
120 return false;
121 }
122
123 NodeNameTest nodeNameTest = (NodeNameTest) test;
124 QName testName = nodeNameTest.getNodeName();
125 String namespaceURI = nodeNameTest.getNamespaceURI();
126 boolean wildcard = nodeNameTest.isWildcard();
127 String testPrefix = testName.getPrefix();
128 if (wildcard && testPrefix == null) {
129 return true;
130 }
131 if (wildcard
132 || testName.getName()
133 .equals(DOMNodePointer.getLocalName(node))) {
134 String nodeNS = DOMNodePointer.getNamespaceURI(node);
135 return equalStrings(namespaceURI, nodeNS) || nodeNS == null
136 && equalStrings(testPrefix, getPrefix(node));
137 }
138 return false;
139 }
140 if (test instanceof NodeTypeTest) {
141 int nodeType = node.getNodeType();
142 switch (((NodeTypeTest) test).getNodeType()) {
143 case Compiler.NODE_TYPE_NODE :
144 return true;
145 case Compiler.NODE_TYPE_TEXT :
146 return nodeType == Node.CDATA_SECTION_NODE
147 || nodeType == Node.TEXT_NODE;
148 case Compiler.NODE_TYPE_COMMENT :
149 return nodeType == Node.COMMENT_NODE;
150 case Compiler.NODE_TYPE_PI :
151 return nodeType == Node.PROCESSING_INSTRUCTION_NODE;
152 default:
153 return false;
154 }
155 }
156 if (test instanceof ProcessingInstructionTest
157 && node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
158 String testPI = ((ProcessingInstructionTest) test).getTarget();
159 String nodePI = ((ProcessingInstruction) node).getTarget();
160 return testPI.equals(nodePI);
161 }
162 return false;
163 }
164
165
166
167
168
169
170
171 private static boolean equalStrings(String s1, String s2) {
172 if (s1 == s2) {
173 return true;
174 }
175 s1 = s1 == null ? "" : s1.trim();
176 s2 = s2 == null ? "" : s2.trim();
177 return s1.equals(s2);
178 }
179
180 public QName getName() {
181 String ln = null;
182 String ns = null;
183 int type = node.getNodeType();
184 if (type == Node.ELEMENT_NODE) {
185 ns = DOMNodePointer.getPrefix(node);
186 ln = DOMNodePointer.getLocalName(node);
187 }
188 else if (type == Node.PROCESSING_INSTRUCTION_NODE) {
189 ln = ((ProcessingInstruction) node).getTarget();
190 }
191 return new QName(ns, ln);
192 }
193
194 public String getNamespaceURI() {
195 return getNamespaceURI(node);
196 }
197
198 public NodeIterator childIterator(NodeTest test, boolean reverse,
199 NodePointer startWith) {
200 return new DOMNodeIterator(this, test, reverse, startWith);
201 }
202
203 public NodeIterator attributeIterator(QName name) {
204 return new DOMAttributeIterator(this, name);
205 }
206
207 public NodePointer namespacePointer(String prefix) {
208 return new NamespacePointer(this, prefix);
209 }
210
211 public NodeIterator namespaceIterator() {
212 return new DOMNamespaceIterator(this);
213 }
214
215 public synchronized NamespaceResolver getNamespaceResolver() {
216 if (localNamespaceResolver == null) {
217 localNamespaceResolver = new NamespaceResolver(super.getNamespaceResolver());
218 localNamespaceResolver.setNamespaceContextPointer(this);
219 }
220 return localNamespaceResolver;
221 }
222
223 public String getNamespaceURI(String prefix) {
224 if (prefix == null || prefix.equals("")) {
225 return getDefaultNamespaceURI();
226 }
227
228 if (prefix.equals("xml")) {
229 return XML_NAMESPACE_URI;
230 }
231
232 if (prefix.equals("xmlns")) {
233 return XMLNS_NAMESPACE_URI;
234 }
235
236 String namespace = null;
237 if (namespaces == null) {
238 namespaces = new HashMap();
239 }
240 else {
241 namespace = (String) namespaces.get(prefix);
242 }
243
244 if (namespace == null) {
245 String qname = "xmlns:" + prefix;
246 Node aNode = node;
247 if (aNode instanceof Document) {
248 aNode = ((Document) aNode).getDocumentElement();
249 }
250 while (aNode != null) {
251 if (aNode.getNodeType() == Node.ELEMENT_NODE) {
252 Attr attr = ((Element) aNode).getAttributeNode(qname);
253 if (attr != null) {
254 namespace = attr.getValue();
255 break;
256 }
257 }
258 aNode = aNode.getParentNode();
259 }
260 if (namespace == null || namespace.equals("")) {
261 namespace = NodePointer.UNKNOWN_NAMESPACE;
262 }
263 }
264
265 namespaces.put(prefix, namespace);
266 if (namespace == UNKNOWN_NAMESPACE) {
267 return null;
268 }
269
270
271 return namespace;
272 }
273
274 public String getDefaultNamespaceURI() {
275 if (defaultNamespace == null) {
276 Node aNode = node;
277 if (aNode instanceof Document) {
278 aNode = ((Document) aNode).getDocumentElement();
279 }
280 while (aNode != null) {
281 if (aNode.getNodeType() == Node.ELEMENT_NODE) {
282 Attr attr = ((Element) aNode).getAttributeNode("xmlns");
283 if (attr != null) {
284 defaultNamespace = attr.getValue();
285 break;
286 }
287 }
288 aNode = aNode.getParentNode();
289 }
290 }
291 if (defaultNamespace == null) {
292 defaultNamespace = "";
293 }
294
295 return defaultNamespace.equals("") ? null : defaultNamespace;
296 }
297
298 public Object getBaseValue() {
299 return node;
300 }
301
302 public Object getImmediateNode() {
303 return node;
304 }
305
306 public boolean isActual() {
307 return true;
308 }
309
310 public boolean isCollection() {
311 return false;
312 }
313
314 public int getLength() {
315 return 1;
316 }
317
318 public boolean isLeaf() {
319 return !node.hasChildNodes();
320 }
321
322
323
324
325
326
327
328
329 public boolean isLanguage(String lang) {
330 String current = getLanguage();
331 return current == null ? super.isLanguage(lang)
332 : current.toUpperCase(Locale.ENGLISH).startsWith(lang.toUpperCase(Locale.ENGLISH));
333 }
334
335
336
337
338
339
340
341
342 protected static String findEnclosingAttribute(Node n, String attrName) {
343 while (n != null) {
344 if (n.getNodeType() == Node.ELEMENT_NODE) {
345 Element e = (Element) n;
346 String attr = e.getAttribute(attrName);
347 if (attr != null && !attr.equals("")) {
348 return attr;
349 }
350 }
351 n = n.getParentNode();
352 }
353 return null;
354 }
355
356
357
358
359
360 protected String getLanguage() {
361 return findEnclosingAttribute(node, "xml:lang");
362 }
363
364
365
366
367
368
369
370
371 public void setValue(Object value) {
372 if (node.getNodeType() == Node.TEXT_NODE
373 || node.getNodeType() == Node.CDATA_SECTION_NODE) {
374 String string = (String) TypeUtils.convert(value, String.class);
375 if (string != null && !string.equals("")) {
376 node.setNodeValue(string);
377 }
378 else {
379 node.getParentNode().removeChild(node);
380 }
381 }
382 else {
383 NodeList children = node.getChildNodes();
384 int count = children.getLength();
385 for (int i = count; --i >= 0;) {
386 Node child = children.item(i);
387 node.removeChild(child);
388 }
389
390 if (value instanceof Node) {
391 Node valueNode = (Node) value;
392 if (valueNode instanceof Element
393 || valueNode instanceof Document) {
394 children = valueNode.getChildNodes();
395 for (int i = 0; i < children.getLength(); i++) {
396 Node child = children.item(i);
397 node.appendChild(child.cloneNode(true));
398 }
399 }
400 else {
401 node.appendChild(valueNode.cloneNode(true));
402 }
403 }
404 else {
405 String string = (String) TypeUtils.convert(value, String.class);
406 if (string != null && !string.equals("")) {
407 Node textNode =
408 node.getOwnerDocument().createTextNode(string);
409 node.appendChild(textNode);
410 }
411 }
412 }
413 }
414
415 public NodePointer createChild(JXPathContext context, QName name, int index) {
416 if (index == WHOLE_COLLECTION) {
417 index = 0;
418 }
419 boolean success =
420 getAbstractFactory(context).createObject(
421 context,
422 this,
423 node,
424 name.toString(),
425 index);
426 if (success) {
427 NodeTest nodeTest;
428 String prefix = name.getPrefix();
429 String namespaceURI = prefix == null ? null : context
430 .getNamespaceURI(prefix);
431 nodeTest = new NodeNameTest(name, namespaceURI);
432
433 NodeIterator it = childIterator(nodeTest, false, null);
434 if (it != null && it.setPosition(index + 1)) {
435 return it.getNodePointer();
436 }
437 }
438 throw new JXPathAbstractFactoryException(
439 "Factory could not create a child node for path: " + asPath()
440 + "/" + name + "[" + (index + 1) + "]");
441 }
442
443 public NodePointer createChild(JXPathContext context, QName name,
444 int index, Object value) {
445 NodePointer ptr = createChild(context, name, index);
446 ptr.setValue(value);
447 return ptr;
448 }
449
450 public NodePointer createAttribute(JXPathContext context, QName name) {
451 if (!(node instanceof Element)) {
452 return super.createAttribute(context, name);
453 }
454 Element element = (Element) node;
455 String prefix = name.getPrefix();
456 if (prefix != null) {
457 String ns = null;
458 NamespaceResolver nsr = getNamespaceResolver();
459 if (nsr != null) {
460 ns = nsr.getNamespaceURI(prefix);
461 }
462 if (ns == null) {
463 throw new JXPathException(
464 "Unknown namespace prefix: " + prefix);
465 }
466 element.setAttributeNS(ns, name.toString(), "");
467 }
468 else {
469 if (!element.hasAttribute(name.getName())) {
470 element.setAttribute(name.getName(), "");
471 }
472 }
473 NodeIterator it = attributeIterator(name);
474 it.setPosition(1);
475 return it.getNodePointer();
476 }
477
478 public void remove() {
479 Node parent = node.getParentNode();
480 if (parent == null) {
481 throw new JXPathException("Cannot remove root DOM node");
482 }
483 parent.removeChild(node);
484 }
485
486 public String asPath() {
487 if (id != null) {
488 return "id('" + escape(id) + "')";
489 }
490
491 StringBuffer buffer = new StringBuffer();
492 if (parent != null) {
493 buffer.append(parent.asPath());
494 }
495 switch (node.getNodeType()) {
496 case Node.ELEMENT_NODE :
497
498
499
500 if (parent instanceof DOMNodePointer) {
501 if (buffer.length() == 0
502 || buffer.charAt(buffer.length() - 1) != '/') {
503 buffer.append('/');
504 }
505 String ln = DOMNodePointer.getLocalName(node);
506 String nsURI = getNamespaceURI();
507 if (nsURI == null) {
508 buffer.append(ln);
509 buffer.append('[');
510 buffer.append(getRelativePositionByQName()).append(']');
511 }
512 else {
513 String prefix = getNamespaceResolver().getPrefix(nsURI);
514 if (prefix != null) {
515 buffer.append(prefix);
516 buffer.append(':');
517 buffer.append(ln);
518 buffer.append('[');
519 buffer.append(getRelativePositionByQName());
520 buffer.append(']');
521 }
522 else {
523 buffer.append("node()");
524 buffer.append('[');
525 buffer.append(getRelativePositionOfElement());
526 buffer.append(']');
527 }
528 }
529 }
530 break;
531 case Node.TEXT_NODE :
532 case Node.CDATA_SECTION_NODE :
533 buffer.append("/text()");
534 buffer.append('[');
535 buffer.append(getRelativePositionOfTextNode()).append(']');
536 break;
537 case Node.PROCESSING_INSTRUCTION_NODE :
538 buffer.append("/processing-instruction(\'");
539 buffer.append(((ProcessingInstruction) node).getTarget()).append("')");
540 buffer.append('[');
541 buffer.append(getRelativePositionOfPI()).append(']');
542 break;
543 case Node.DOCUMENT_NODE :
544
545 break;
546 default:
547 break;
548 }
549 return buffer.toString();
550 }
551
552
553
554
555
556 private int getRelativePositionByQName() {
557 int count = 1;
558 Node n = node.getPreviousSibling();
559 while (n != null) {
560 if (n.getNodeType() == Node.ELEMENT_NODE && matchesQName(n)) {
561 count++;
562 }
563 n = n.getPreviousSibling();
564 }
565 return count;
566 }
567
568 private boolean matchesQName(Node n) {
569 if (getNamespaceURI() != null) {
570 return equalStrings(getNamespaceURI(n), getNamespaceURI())
571 && equalStrings(node.getLocalName(), n.getLocalName());
572 }
573 return equalStrings(node.getNodeName(), n.getNodeName());
574 }
575
576
577
578
579
580 private int getRelativePositionOfElement() {
581 int count = 1;
582 Node n = node.getPreviousSibling();
583 while (n != null) {
584 if (n.getNodeType() == Node.ELEMENT_NODE) {
585 count++;
586 }
587 n = n.getPreviousSibling();
588 }
589 return count;
590 }
591
592
593
594
595
596 private int getRelativePositionOfTextNode() {
597 int count = 1;
598 Node n = node.getPreviousSibling();
599 while (n != null) {
600 if (n.getNodeType() == Node.TEXT_NODE
601 || n.getNodeType() == Node.CDATA_SECTION_NODE) {
602 count++;
603 }
604 n = n.getPreviousSibling();
605 }
606 return count;
607 }
608
609
610
611
612
613 private int getRelativePositionOfPI() {
614 int count = 1;
615 String target = ((ProcessingInstruction) node).getTarget();
616 Node n = node.getPreviousSibling();
617 while (n != null) {
618 if (n.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE
619 && ((ProcessingInstruction) n).getTarget().equals(target)) {
620 count++;
621 }
622 n = n.getPreviousSibling();
623 }
624 return count;
625 }
626
627 public int hashCode() {
628 return node.hashCode();
629 }
630
631 public boolean equals(Object object) {
632 return object == this || object instanceof DOMNodePointer && node == ((DOMNodePointer) object).node;
633 }
634
635
636
637
638
639
640 public static String getPrefix(Node node) {
641 String prefix = node.getPrefix();
642 if (prefix != null) {
643 return prefix;
644 }
645
646 String name = node.getNodeName();
647 int index = name.lastIndexOf(':');
648 return index < 0 ? null : name.substring(0, index);
649 }
650
651
652
653
654
655
656 public static String getLocalName(Node node) {
657 String localName = node.getLocalName();
658 if (localName != null) {
659 return localName;
660 }
661
662 String name = node.getNodeName();
663 int index = name.lastIndexOf(':');
664 return index < 0 ? name : name.substring(index + 1);
665 }
666
667
668
669
670
671
672 public static String getNamespaceURI(Node node) {
673 if (node instanceof Document) {
674 node = ((Document) node).getDocumentElement();
675 }
676
677 Element element = (Element) node;
678
679 String uri = element.getNamespaceURI();
680 if (uri == null) {
681 String prefix = getPrefix(node);
682 String qname = prefix == null ? "xmlns" : "xmlns:" + prefix;
683
684 Node aNode = node;
685 while (aNode != null) {
686 if (aNode.getNodeType() == Node.ELEMENT_NODE) {
687 Attr attr = ((Element) aNode).getAttributeNode(qname);
688 if (attr != null) {
689 uri = attr.getValue();
690 break;
691 }
692 }
693 aNode = aNode.getParentNode();
694 }
695 }
696 return "".equals(uri) ? null : uri;
697 }
698
699 public Object getValue() {
700 if (node.getNodeType() == Node.COMMENT_NODE) {
701 String text = ((Comment) node).getData();
702 return text == null ? "" : text.trim();
703 }
704 return stringValue(node);
705 }
706
707
708
709
710
711
712 private String stringValue(Node node) {
713 int nodeType = node.getNodeType();
714 if (nodeType == Node.COMMENT_NODE) {
715 return "";
716 }
717 boolean trim = !"preserve".equals(findEnclosingAttribute(node, "xml:space"));
718 if (nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE) {
719 String text = node.getNodeValue();
720 return text == null ? "" : trim ? text.trim() : text;
721 }
722 if (nodeType == Node.PROCESSING_INSTRUCTION_NODE) {
723 String text = ((ProcessingInstruction) node).getData();
724 return text == null ? "" : trim ? text.trim() : text;
725 }
726 NodeList list = node.getChildNodes();
727 StringBuffer buf = new StringBuffer();
728 for (int i = 0; i < list.getLength(); i++) {
729 Node child = list.item(i);
730 buf.append(stringValue(child));
731 }
732 return buf.toString();
733 }
734
735
736
737
738
739
740
741 public Pointer getPointerByID(JXPathContext context, String id) {
742 Document document = node.getNodeType() == Node.DOCUMENT_NODE ? (Document) node
743 : node.getOwnerDocument();
744 Element element = document.getElementById(id);
745 return element == null ? (Pointer) new NullPointer(getLocale(), id)
746 : new DOMNodePointer(element, getLocale(), id);
747 }
748
749 public int compareChildNodePointers(NodePointer pointer1,
750 NodePointer pointer2) {
751 Node node1 = (Node) pointer1.getBaseValue();
752 Node node2 = (Node) pointer2.getBaseValue();
753 if (node1 == node2) {
754 return 0;
755 }
756
757 int t1 = node1.getNodeType();
758 int t2 = node2.getNodeType();
759 if (t1 == Node.ATTRIBUTE_NODE && t2 != Node.ATTRIBUTE_NODE) {
760 return -1;
761 }
762 if (t1 != Node.ATTRIBUTE_NODE && t2 == Node.ATTRIBUTE_NODE) {
763 return 1;
764 }
765 if (t1 == Node.ATTRIBUTE_NODE && t2 == Node.ATTRIBUTE_NODE) {
766 NamedNodeMap map = ((Node) getNode()).getAttributes();
767 int length = map.getLength();
768 for (int i = 0; i < length; i++) {
769 Node n = map.item(i);
770 if (n == node1) {
771 return -1;
772 }
773 if (n == node2) {
774 return 1;
775 }
776 }
777 return 0;
778 }
779
780 Node current = node.getFirstChild();
781 while (current != null) {
782 if (current == node1) {
783 return -1;
784 }
785 if (current == node2) {
786 return 1;
787 }
788 current = current.getNextSibling();
789 }
790 return 0;
791 }
792 }