1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jxpath.ri.model;
18
19 import java.util.HashSet;
20 import java.util.Locale;
21
22 import org.apache.commons.jxpath.AbstractFactory;
23 import org.apache.commons.jxpath.ExceptionHandler;
24 import org.apache.commons.jxpath.JXPathContext;
25 import org.apache.commons.jxpath.JXPathException;
26 import org.apache.commons.jxpath.JXPathNotFoundException;
27 import org.apache.commons.jxpath.NodeSet;
28 import org.apache.commons.jxpath.Pointer;
29 import org.apache.commons.jxpath.ri.Compiler;
30 import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl;
31 import org.apache.commons.jxpath.ri.NamespaceResolver;
32 import org.apache.commons.jxpath.ri.QName;
33 import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
34 import org.apache.commons.jxpath.ri.compiler.NodeTest;
35 import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
36 import org.apache.commons.jxpath.ri.model.beans.NullPointer;
37
38
39
40
41
42
43
44
45
46
47 public abstract class NodePointer implements Pointer {
48
49
50 private static final long serialVersionUID = 8117201322861007777L;
51
52
53 public static final int WHOLE_COLLECTION = Integer.MIN_VALUE;
54
55
56 public static final String UNKNOWN_NAMESPACE = "<<unknown namespace>>";
57
58
59 protected int index = WHOLE_COLLECTION;
60
61 private boolean attribute = false;
62 private NamespaceResolver namespaceResolver;
63 private ExceptionHandler exceptionHandler;
64 private transient Object rootNode;
65
66
67
68
69
70
71
72
73
74 public static NodePointer newNodePointer(
75 QName name,
76 Object bean,
77 Locale locale) {
78 NodePointer pointer = null;
79 if (bean == null) {
80 pointer = new NullPointer(name, locale);
81 return pointer;
82 }
83
84 NodePointerFactory[] factories =
85 JXPathContextReferenceImpl.getNodePointerFactories();
86 for (int i = 0; i < factories.length; i++) {
87 pointer = factories[i].createNodePointer(name, bean, locale);
88 if (pointer != null) {
89 return pointer;
90 }
91 }
92 throw new JXPathException(
93 "Could not allocate a NodePointer for object of "
94 + bean.getClass());
95 }
96
97
98
99
100
101
102
103
104
105 public static NodePointer newChildNodePointer(
106 NodePointer parent,
107 QName name,
108 Object bean) {
109 NodePointerFactory[] factories =
110 JXPathContextReferenceImpl.getNodePointerFactories();
111 for (int i = 0; i < factories.length; i++) {
112 NodePointer pointer =
113 factories[i].createNodePointer(parent, name, bean);
114 if (pointer != null) {
115 return pointer;
116 }
117 }
118 throw new JXPathException(
119 "Could not allocate a NodePointer for object of "
120 + bean.getClass());
121 }
122
123
124 protected NodePointer parent;
125
126
127 protected Locale locale;
128
129
130
131
132
133 protected NodePointer(NodePointer parent) {
134 this.parent = parent;
135 }
136
137
138
139
140
141
142 protected NodePointer(NodePointer parent, Locale locale) {
143 this.parent = parent;
144 this.locale = locale;
145 }
146
147
148
149
150
151 public NamespaceResolver getNamespaceResolver() {
152 if (namespaceResolver == null && parent != null) {
153 namespaceResolver = parent.getNamespaceResolver();
154 }
155 return namespaceResolver;
156 }
157
158
159
160
161
162 public void setNamespaceResolver(NamespaceResolver namespaceResolver) {
163 this.namespaceResolver = namespaceResolver;
164 }
165
166
167
168
169
170 public NodePointer getParent() {
171 NodePointer pointer = parent;
172 while (pointer != null && pointer.isContainer()) {
173 pointer = pointer.getImmediateParentPointer();
174 }
175 return pointer;
176 }
177
178
179
180
181
182 public NodePointer getImmediateParentPointer() {
183 return parent;
184 }
185
186
187
188
189
190 public void setAttribute(boolean attribute) {
191 this.attribute = attribute;
192 }
193
194
195
196
197
198 public boolean isAttribute() {
199 return attribute;
200 }
201
202
203
204
205
206 public boolean isRoot() {
207 return parent == null;
208 }
209
210
211
212
213
214 public abstract boolean isLeaf();
215
216
217
218
219
220
221 public boolean isNode() {
222 return !isContainer();
223 }
224
225
226
227
228
229
230 public boolean isContainer() {
231 return false;
232 }
233
234
235
236
237
238
239
240
241
242 public int getIndex() {
243 return index;
244 }
245
246
247
248
249
250 public void setIndex(int index) {
251 this.index = index;
252 }
253
254
255
256
257
258
259 public abstract boolean isCollection();
260
261
262
263
264
265
266
267 public abstract int getLength();
268
269
270
271
272
273
274
275 public Object getValue() {
276 NodePointer valuePointer = getValuePointer();
277 if (valuePointer != this) {
278 return valuePointer.getValue();
279 }
280
281 return getNode();
282 }
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302 public NodePointer getValuePointer() {
303 NodePointer ivp = getImmediateValuePointer();
304 return ivp == this ? this : ivp.getValuePointer();
305 }
306
307
308
309
310
311
312
313 public NodePointer getImmediateValuePointer() {
314 return this;
315 }
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331 public boolean isActual() {
332 return index == WHOLE_COLLECTION || index >= 0 && index < getLength();
333 }
334
335
336
337
338
339 public abstract QName getName();
340
341
342
343
344
345
346
347 public abstract Object getBaseValue();
348
349
350
351
352
353
354
355 public Object getNodeValue() {
356 return getNode();
357 }
358
359
360
361
362
363
364
365 public Object getNode() {
366 return getValuePointer().getImmediateNode();
367 }
368
369
370
371
372
373 public synchronized Object getRootNode() {
374 if (rootNode == null) {
375 rootNode = parent == null ? getImmediateNode() : parent.getRootNode();
376 }
377 return rootNode;
378 }
379
380
381
382
383
384
385 public abstract Object getImmediateNode();
386
387
388
389
390
391
392 public abstract void setValue(Object value);
393
394
395
396
397
398
399
400
401 public abstract int compareChildNodePointers(
402 NodePointer pointer1, NodePointer pointer2);
403
404
405
406
407
408
409 public boolean testNode(NodeTest test) {
410 if (test == null) {
411 return true;
412 }
413 if (test instanceof NodeNameTest) {
414 if (isContainer()) {
415 return false;
416 }
417 NodeNameTest nodeNameTest = (NodeNameTest) test;
418 QName testName = nodeNameTest.getNodeName();
419 QName nodeName = getName();
420 if (nodeName == null) {
421 return false;
422 }
423
424 String testPrefix = testName.getPrefix();
425 String nodePrefix = nodeName.getPrefix();
426 if (!safeEquals(testPrefix, nodePrefix)) {
427 String testNS = getNamespaceURI(testPrefix);
428 String nodeNS = getNamespaceURI(nodePrefix);
429 if (!safeEquals(testNS, nodeNS)) {
430 return false;
431 }
432 }
433 if (nodeNameTest.isWildcard()) {
434 return true;
435 }
436 return testName.getName().equals(nodeName.getName());
437 }
438 return test instanceof NodeTypeTest
439 && ((NodeTypeTest) test).getNodeType() == Compiler.NODE_TYPE_NODE && isNode();
440 }
441
442
443
444
445
446
447
448
449 public NodePointer createPath(JXPathContext context, Object value) {
450 setValue(value);
451 return this;
452 }
453
454
455
456
457 public void remove() {
458
459
460
461
462 }
463
464
465
466
467
468
469
470
471 public NodePointer createPath(JXPathContext context) {
472 return this;
473 }
474
475
476
477
478
479
480
481
482
483
484
485
486 public NodePointer createChild(
487 JXPathContext context,
488 QName name,
489 int index,
490 Object value) {
491 throw new JXPathException("Cannot create an object for path "
492 + asPath() + "/" + name + "[" + (index + 1) + "]"
493 + ", operation is not allowed for this type of node");
494 }
495
496
497
498
499
500
501
502
503
504
505
506 public NodePointer createChild(JXPathContext context, QName name, int index) {
507 throw new JXPathException("Cannot create an object for path "
508 + asPath() + "/" + name + "[" + (index + 1) + "]"
509 + ", operation is not allowed for this type of node");
510 }
511
512
513
514
515
516
517
518 public NodePointer createAttribute(JXPathContext context, QName name) {
519 throw new JXPathException("Cannot create an attribute for path "
520 + asPath() + "/@" + name
521 + ", operation is not allowed for this type of node");
522 }
523
524
525
526
527
528
529 public Locale getLocale() {
530 if (locale == null && parent != null) {
531 locale = parent.getLocale();
532 }
533 return locale;
534 }
535
536
537
538
539
540
541
542 public boolean isLanguage(String lang) {
543 Locale loc = getLocale();
544 String name = loc.toString().replace('_', '-');
545 return name.toUpperCase(Locale.ENGLISH).startsWith(lang.toUpperCase(Locale.ENGLISH));
546 }
547
548
549
550
551
552
553
554
555
556 public NodeIterator childIterator(
557 NodeTest test,
558 boolean reverse,
559 NodePointer startWith) {
560 NodePointer valuePointer = getValuePointer();
561 return valuePointer == null || valuePointer == this ? null
562 : valuePointer.childIterator(test, reverse, startWith);
563 }
564
565
566
567
568
569
570
571
572 public NodeIterator attributeIterator(QName qname) {
573 NodePointer valuePointer = getValuePointer();
574 return valuePointer == null || valuePointer == this ? null
575 : valuePointer.attributeIterator(qname);
576 }
577
578
579
580
581
582
583
584 public NodeIterator namespaceIterator() {
585 return null;
586 }
587
588
589
590
591
592
593
594
595 public NodePointer namespacePointer(String namespace) {
596 return null;
597 }
598
599
600
601
602
603
604 public String getNamespaceURI(String prefix) {
605 return null;
606 }
607
608
609
610
611
612 public String getNamespaceURI() {
613 return null;
614 }
615
616
617
618
619
620
621
622 protected boolean isDefaultNamespace(String prefix) {
623 if (prefix == null) {
624 return true;
625 }
626
627 String namespace = getNamespaceURI(prefix);
628 return namespace != null && namespace.equals(getDefaultNamespaceURI());
629 }
630
631
632
633
634
635 protected String getDefaultNamespaceURI() {
636 return null;
637 }
638
639
640
641
642
643
644
645 public Pointer getPointerByID(JXPathContext context, String id) {
646 return context.getPointerByID(id);
647 }
648
649
650
651
652
653
654
655
656 public Pointer getPointerByKey(
657 JXPathContext context,
658 String key,
659 String value) {
660 return context.getPointerByKey(key, value);
661 }
662
663
664
665
666
667
668
669
670 public NodeSet getNodeSetByKey(JXPathContext context, String key, Object value) {
671 return context.getNodeSetByKey(key, value);
672 }
673
674
675
676
677
678 public String asPath() {
679
680
681 if (parent != null && parent.isContainer()) {
682 return parent.asPath();
683 }
684
685 StringBuffer buffer = new StringBuffer();
686 if (parent != null) {
687 buffer.append(parent.asPath());
688 }
689
690 if (buffer.length() == 0
691 || buffer.charAt(buffer.length() - 1) != '/') {
692 buffer.append('/');
693 }
694 if (attribute) {
695 buffer.append('@');
696 }
697 buffer.append(getName());
698
699 if (index != WHOLE_COLLECTION && isCollection()) {
700 buffer.append('[').append(index + 1).append(']');
701 }
702 return buffer.toString();
703 }
704
705
706
707
708
709 public Object clone() {
710 try {
711 NodePointer ptr = (NodePointer) super.clone();
712 if (parent != null) {
713 ptr.parent = (NodePointer) parent.clone();
714 }
715 return ptr;
716 }
717 catch (CloneNotSupportedException ex) {
718
719 ex.printStackTrace();
720 }
721 return null;
722 }
723
724 public String toString() {
725 return asPath();
726 }
727
728 public int compareTo(Object object) {
729 if (object == this) {
730 return 0;
731 }
732
733 NodePointer pointer = (NodePointer) object;
734 if (safeEquals(parent, pointer.parent)) {
735 return parent == null ? 0 : parent.compareChildNodePointers(this, pointer);
736 }
737
738
739 int depth1 = 0;
740 NodePointer p1 = this;
741 HashSet parents1 = new HashSet();
742 while (p1 != null) {
743 depth1++;
744 p1 = p1.parent;
745 if (p1 != null) {
746 parents1.add(p1);
747 }
748 }
749 boolean commonParentFound = false;
750 int depth2 = 0;
751 NodePointer p2 = pointer;
752 while (p2 != null) {
753 depth2++;
754 p2 = p2.parent;
755 if (parents1.contains(p2)) {
756 commonParentFound = true;
757 }
758 }
759
760 return commonParentFound ? compareNodePointers(this, depth1, pointer, depth2) : 0;
761 }
762
763
764
765
766
767
768
769
770
771 private int compareNodePointers(
772 NodePointer p1,
773 int depth1,
774 NodePointer p2,
775 int depth2) {
776 if (depth1 < depth2) {
777 int r = compareNodePointers(p1, depth1, p2.parent, depth2 - 1);
778 return r == 0 ? -1 : r;
779 }
780 if (depth1 > depth2) {
781 int r = compareNodePointers(p1.parent, depth1 - 1, p2, depth2);
782 return r == 0 ? 1 : r;
783 }
784
785 if (safeEquals(p1, p2)) {
786 return 0;
787 }
788 if (depth1 == 1) {
789 throw new JXPathException(
790 "Cannot compare pointers that do not belong to the same tree: '"
791 + p1 + "' and '" + p2 + "'");
792 }
793 int r = compareNodePointers(p1.parent, depth1 - 1, p2.parent, depth2 - 1);
794 return r == 0 ? p1.parent.compareChildNodePointers(p1, p2) : r;
795 }
796
797
798
799
800 public void printPointerChain() {
801 printDeep(this, "");
802 }
803
804
805
806
807
808 public void setExceptionHandler(ExceptionHandler exceptionHandler) {
809 this.exceptionHandler = exceptionHandler;
810 }
811
812
813
814
815
816
817
818 public void handle(Throwable t, NodePointer originator) {
819 if (exceptionHandler != null) {
820 exceptionHandler.handle(t, originator);
821 return;
822 }
823 if (parent != null) {
824 parent.handle(t, originator);
825 }
826 }
827
828
829
830
831
832
833 public void handle(Throwable t) {
834 handle(t, this);
835 }
836
837
838
839
840
841
842 protected String escape(String string) {
843 final char[] c = new char[] { '\'', '"' };
844 final String[] esc = new String[] { "'", """ };
845 StringBuffer sb = null;
846 for (int i = 0; sb == null && i < c.length; i++) {
847 if (string.indexOf(c[i]) >= 0) {
848 sb = new StringBuffer(string);
849 }
850 }
851 if (sb == null) {
852 return string;
853 }
854 for (int i = 0; i < c.length; i++) {
855 if (string.indexOf(c[i]) < 0) {
856 continue;
857 }
858 int pos = 0;
859 while (pos < sb.length()) {
860 if (sb.charAt(pos) == c[i]) {
861 sb.replace(pos, pos + 1, esc[i]);
862 pos += esc[i].length();
863 }
864 else {
865 pos++;
866 }
867 }
868 }
869 return sb.toString();
870 }
871
872
873
874
875
876
877 protected AbstractFactory getAbstractFactory(JXPathContext context) {
878 AbstractFactory factory = context.getFactory();
879 if (factory == null) {
880 throw new JXPathException(
881 "Factory is not set on the JXPathContext - cannot create path: "
882 + asPath());
883 }
884 return factory;
885 }
886
887
888
889
890
891
892 private static void printDeep(NodePointer pointer, String indent) {
893 if (indent.length() == 0) {
894 System.err.println(
895 "POINTER: "
896 + pointer
897 + "("
898 + pointer.getClass().getName()
899 + ")");
900 }
901 else {
902 System.err.println(
903 indent
904 + " of "
905 + pointer
906 + "("
907 + pointer.getClass().getName()
908 + ")");
909 }
910 if (pointer.getImmediateParentPointer() != null) {
911 printDeep(pointer.getImmediateParentPointer(), indent + " ");
912 }
913 }
914
915 private static boolean safeEquals(Object o1, Object o2) {
916 return o1 == o2 || o1 != null && o1.equals(o2);
917 }
918
919
920
921
922
923
924
925 public static NodePointer verify(NodePointer nodePointer) {
926 if (!nodePointer.isActual()) {
927
928
929
930
931
932
933 NodePointer parent = nodePointer.getImmediateParentPointer();
934 if (parent == null
935 || !parent.isContainer()
936 || !parent.isActual()) {
937 throw new JXPathNotFoundException("No value for xpath: " + nodePointer);
938 }
939 }
940 return nodePointer;
941 }
942 }