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