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