1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jxpath.ri;
18
19 import java.lang.ref.SoftReference;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.Comparator;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.Map;
27 import java.util.Vector;
28 import java.util.Map.Entry;
29
30 import org.apache.commons.jxpath.CompiledExpression;
31 import org.apache.commons.jxpath.ExceptionHandler;
32 import org.apache.commons.jxpath.Function;
33 import org.apache.commons.jxpath.Functions;
34 import org.apache.commons.jxpath.JXPathContext;
35 import org.apache.commons.jxpath.JXPathException;
36 import org.apache.commons.jxpath.JXPathFunctionNotFoundException;
37 import org.apache.commons.jxpath.JXPathInvalidSyntaxException;
38 import org.apache.commons.jxpath.JXPathNotFoundException;
39 import org.apache.commons.jxpath.JXPathTypeConversionException;
40 import org.apache.commons.jxpath.Pointer;
41 import org.apache.commons.jxpath.ri.axes.InitialContext;
42 import org.apache.commons.jxpath.ri.axes.RootContext;
43 import org.apache.commons.jxpath.ri.compiler.Expression;
44 import org.apache.commons.jxpath.ri.compiler.LocationPath;
45 import org.apache.commons.jxpath.ri.compiler.Path;
46 import org.apache.commons.jxpath.ri.compiler.TreeCompiler;
47 import org.apache.commons.jxpath.ri.model.NodePointer;
48 import org.apache.commons.jxpath.ri.model.NodePointerFactory;
49 import org.apache.commons.jxpath.ri.model.VariablePointerFactory;
50 import org.apache.commons.jxpath.ri.model.beans.BeanPointerFactory;
51 import org.apache.commons.jxpath.ri.model.beans.CollectionPointerFactory;
52 import org.apache.commons.jxpath.ri.model.container.ContainerPointerFactory;
53 import org.apache.commons.jxpath.ri.model.dynamic.DynamicPointerFactory;
54 import org.apache.commons.jxpath.util.ReverseComparator;
55 import org.apache.commons.jxpath.util.ClassLoaderUtil;
56 import org.apache.commons.jxpath.util.TypeUtils;
57
58
59
60
61
62
63
64 public class JXPathContextReferenceImpl extends JXPathContext {
65
66
67
68
69
70 public static final boolean USE_SOFT_CACHE = true;
71
72 private static final Compiler COMPILER = new TreeCompiler();
73 private static Map compiled = new HashMap();
74 private static int cleanupCount = 0;
75
76 private static NodePointerFactory[] nodeFactoryArray = null;
77
78 private static final int CLEANUP_THRESHOLD = 500;
79 private static final Vector nodeFactories = new Vector();
80
81 static {
82 nodeFactories.add(new CollectionPointerFactory());
83 nodeFactories.add(new BeanPointerFactory());
84 nodeFactories.add(new DynamicPointerFactory());
85 nodeFactories.add(new VariablePointerFactory());
86
87
88 Object domFactory = allocateConditionally(
89 "org.apache.commons.jxpath.ri.model.dom.DOMPointerFactory",
90 "org.w3c.dom.Node");
91 if (domFactory != null) {
92 nodeFactories.add(domFactory);
93 }
94
95
96 Object jdomFactory = allocateConditionally(
97 "org.apache.commons.jxpath.ri.model.jdom.JDOMPointerFactory",
98 "org.jdom.Document");
99 if (jdomFactory != null) {
100 nodeFactories.add(jdomFactory);
101 }
102
103
104 Object dynaBeanFactory =
105 allocateConditionally(
106 "org.apache.commons.jxpath.ri.model.dynabeans."
107 + "DynaBeanPointerFactory",
108 "org.apache.commons.beanutils.DynaBean");
109 if (dynaBeanFactory != null) {
110 nodeFactories.add(dynaBeanFactory);
111 }
112
113 nodeFactories.add(new ContainerPointerFactory());
114 createNodeFactoryArray();
115 }
116
117
118
119
120 private static synchronized void createNodeFactoryArray() {
121 if (nodeFactoryArray == null) {
122 nodeFactoryArray =
123 (NodePointerFactory[]) nodeFactories.
124 toArray(new NodePointerFactory[nodeFactories.size()]);
125 Arrays.sort(nodeFactoryArray, new Comparator() {
126 public int compare(Object a, Object b) {
127 int orderA = ((NodePointerFactory) a).getOrder();
128 int orderB = ((NodePointerFactory) b).getOrder();
129 return orderA - orderB;
130 }
131 });
132 }
133 }
134
135
136
137
138
139
140
141 public static void addNodePointerFactory(NodePointerFactory factory) {
142 synchronized (nodeFactories) {
143 nodeFactories.add(factory);
144 nodeFactoryArray = null;
145 }
146 }
147
148
149
150
151
152 public static NodePointerFactory[] getNodePointerFactories() {
153 return nodeFactoryArray;
154 }
155
156
157 protected NamespaceResolver namespaceResolver;
158
159 private Pointer rootPointer;
160 private Pointer contextPointer;
161
162
163
164
165
166
167 protected JXPathContextReferenceImpl(JXPathContext parentContext,
168 Object contextBean) {
169 this(parentContext, contextBean, null);
170 }
171
172
173
174
175
176
177
178 public JXPathContextReferenceImpl(JXPathContext parentContext,
179 Object contextBean, Pointer contextPointer) {
180 super(parentContext, contextBean);
181
182 synchronized (nodeFactories) {
183 createNodeFactoryArray();
184 }
185
186 if (contextPointer != null) {
187 this.contextPointer = contextPointer;
188 this.rootPointer =
189 NodePointer.newNodePointer(
190 new QName(null, "root"),
191 contextPointer.getRootNode(),
192 getLocale());
193 }
194 else {
195 this.contextPointer =
196 NodePointer.newNodePointer(
197 new QName(null, "root"),
198 contextBean,
199 getLocale());
200 this.rootPointer = this.contextPointer;
201 }
202
203 NamespaceResolver parentNR = null;
204 if (parentContext instanceof JXPathContextReferenceImpl) {
205 parentNR = ((JXPathContextReferenceImpl) parentContext).getNamespaceResolver();
206 }
207 namespaceResolver = new NamespaceResolver(parentNR);
208 namespaceResolver
209 .setNamespaceContextPointer((NodePointer) this.contextPointer);
210 }
211
212
213
214
215
216
217
218 protected Compiler getCompiler() {
219 return COMPILER;
220 }
221
222 protected CompiledExpression compilePath(String xpath) {
223 return new JXPathCompiledExpression(xpath, compileExpression(xpath));
224 }
225
226
227
228
229
230
231 private Expression compileExpression(String xpath) {
232 Expression expr;
233
234 synchronized (compiled) {
235 if (USE_SOFT_CACHE) {
236 expr = null;
237 SoftReference ref = (SoftReference) compiled.get(xpath);
238 if (ref != null) {
239 expr = (Expression) ref.get();
240 }
241 }
242 else {
243 expr = (Expression) compiled.get(xpath);
244 }
245 }
246
247 if (expr != null) {
248 return expr;
249 }
250
251 expr = (Expression) Parser.parseExpression(xpath, getCompiler());
252
253 synchronized (compiled) {
254 if (USE_SOFT_CACHE) {
255 if (cleanupCount++ >= CLEANUP_THRESHOLD) {
256 Iterator it = compiled.entrySet().iterator();
257 while (it.hasNext()) {
258 Entry me = (Entry) it.next();
259 if (((SoftReference) me.getValue()).get() == null) {
260 it.remove();
261 }
262 }
263 cleanupCount = 0;
264 }
265 compiled.put(xpath, new SoftReference(expr));
266 }
267 else {
268 compiled.put(xpath, expr);
269 }
270 }
271
272 return expr;
273 }
274
275
276
277
278
279
280
281 public Object getValue(String xpath) {
282 Expression expression = compileExpression(xpath);
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315 return getValue(xpath, expression);
316 }
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354 public Object getValue(String xpath, Expression expr) {
355 Object result = expr.computeValue(getEvalContext());
356 if (result == null) {
357 if (expr instanceof Path && !isLenient()) {
358 throw new JXPathNotFoundException("No value for xpath: "
359 + xpath);
360 }
361 return null;
362 }
363 if (result instanceof EvalContext) {
364 EvalContext ctx = (EvalContext) result;
365 result = ctx.getSingleNodePointer();
366 if (!isLenient() && result == null) {
367 throw new JXPathNotFoundException("No value for xpath: "
368 + xpath);
369 }
370 }
371 if (result instanceof NodePointer) {
372 result = ((NodePointer) result).getValuePointer();
373 if (!isLenient()) {
374 NodePointer.verify((NodePointer) result);
375 }
376 result = ((NodePointer) result).getValue();
377 }
378 return result;
379 }
380
381
382
383
384
385
386
387
388 public Object getValue(String xpath, Class requiredType) {
389 Expression expr = compileExpression(xpath);
390 return getValue(xpath, expr, requiredType);
391 }
392
393
394
395
396
397
398
399
400 public Object getValue(String xpath, Expression expr, Class requiredType) {
401 Object value = getValue(xpath, expr);
402 if (value != null && requiredType != null) {
403 if (!TypeUtils.canConvert(value, requiredType)) {
404 throw new JXPathTypeConversionException(
405 "Invalid expression type. '"
406 + xpath
407 + "' returns "
408 + value.getClass().getName()
409 + ". It cannot be converted to "
410 + requiredType.getName());
411 }
412 value = TypeUtils.convert(value, requiredType);
413 }
414 return value;
415 }
416
417
418
419
420
421
422
423
424 public Iterator iterate(String xpath) {
425 return iterate(xpath, compileExpression(xpath));
426 }
427
428
429
430
431
432
433
434
435
436 public Iterator iterate(String xpath, Expression expr) {
437 return expr.iterate(getEvalContext());
438 }
439
440 public Pointer getPointer(String xpath) {
441 return getPointer(xpath, compileExpression(xpath));
442 }
443
444
445
446
447
448
449
450 public Pointer getPointer(String xpath, Expression expr) {
451 Object result = expr.computeValue(getEvalContext());
452 if (result instanceof EvalContext) {
453 result = ((EvalContext) result).getSingleNodePointer();
454 }
455 if (result instanceof Pointer) {
456 if (!isLenient() && !((NodePointer) result).isActual()) {
457 throw new JXPathNotFoundException("No pointer for xpath: "
458 + xpath);
459 }
460 return (Pointer) result;
461 }
462 return NodePointer.newNodePointer(null, result, getLocale());
463 }
464
465 public void setValue(String xpath, Object value) {
466 setValue(xpath, compileExpression(xpath), value);
467 }
468
469
470
471
472
473
474
475 public void setValue(String xpath, Expression expr, Object value) {
476 try {
477 setValue(xpath, expr, value, false);
478 }
479 catch (Throwable ex) {
480 throw new JXPathException(
481 "Exception trying to set value with xpath " + xpath, ex);
482 }
483 }
484
485 public Pointer createPath(String xpath) {
486 return createPath(xpath, compileExpression(xpath));
487 }
488
489
490
491
492
493
494
495 public Pointer createPath(String xpath, Expression expr) {
496 try {
497 Object result = expr.computeValue(getEvalContext());
498 Pointer pointer = null;
499
500 if (result instanceof Pointer) {
501 pointer = (Pointer) result;
502 }
503 else if (result instanceof EvalContext) {
504 EvalContext ctx = (EvalContext) result;
505 pointer = ctx.getSingleNodePointer();
506 }
507 else {
508 checkSimplePath(expr);
509
510 throw new JXPathException("Cannot create path:" + xpath);
511 }
512 return ((NodePointer) pointer).createPath(this);
513 }
514 catch (Throwable ex) {
515 throw new JXPathException(
516 "Exception trying to create xpath " + xpath,
517 ex);
518 }
519 }
520
521 public Pointer createPathAndSetValue(String xpath, Object value) {
522 return createPathAndSetValue(xpath, compileExpression(xpath), value);
523 }
524
525
526
527
528
529
530
531
532 public Pointer createPathAndSetValue(String xpath, Expression expr,
533 Object value) {
534 try {
535 return setValue(xpath, expr, value, true);
536 }
537 catch (Throwable ex) {
538 throw new JXPathException(
539 "Exception trying to create xpath " + xpath,
540 ex);
541 }
542 }
543
544
545
546
547
548
549
550
551
552 private Pointer setValue(String xpath, Expression expr, Object value,
553 boolean create) {
554 Object result = expr.computeValue(getEvalContext());
555 Pointer pointer = null;
556
557 if (result instanceof Pointer) {
558 pointer = (Pointer) result;
559 }
560 else if (result instanceof EvalContext) {
561 EvalContext ctx = (EvalContext) result;
562 pointer = ctx.getSingleNodePointer();
563 }
564 else {
565 if (create) {
566 checkSimplePath(expr);
567 }
568
569
570 throw new JXPathException("Cannot set value for xpath: " + xpath);
571 }
572 if (create) {
573 pointer = ((NodePointer) pointer).createPath(this, value);
574 }
575 else {
576 pointer.setValue(value);
577 }
578 return pointer;
579 }
580
581
582
583
584
585
586 private void checkSimplePath(Expression expr) {
587 if (!(expr instanceof LocationPath)
588 || !((LocationPath) expr).isSimplePath()) {
589 throw new JXPathInvalidSyntaxException(
590 "JXPath can only create a path if it uses exclusively "
591 + "the child:: and attribute:: axes and has "
592 + "no context-dependent predicates");
593 }
594 }
595
596
597
598
599
600
601
602
603
604 public Iterator iteratePointers(String xpath) {
605 return iteratePointers(xpath, compileExpression(xpath));
606 }
607
608
609
610
611
612
613
614
615
616
617 public Iterator iteratePointers(String xpath, Expression expr) {
618 return expr.iteratePointers(getEvalContext());
619 }
620
621 public void removePath(String xpath) {
622 removePath(xpath, compileExpression(xpath));
623 }
624
625
626
627
628
629
630 public void removePath(String xpath, Expression expr) {
631 try {
632 NodePointer pointer = (NodePointer) getPointer(xpath, expr);
633 if (pointer != null) {
634 pointer.remove();
635 }
636 }
637 catch (Throwable ex) {
638 throw new JXPathException(
639 "Exception trying to remove xpath " + xpath,
640 ex);
641 }
642 }
643
644 public void removeAll(String xpath) {
645 removeAll(xpath, compileExpression(xpath));
646 }
647
648
649
650
651
652
653 public void removeAll(String xpath, Expression expr) {
654 try {
655 ArrayList list = new ArrayList();
656 Iterator it = expr.iteratePointers(getEvalContext());
657 while (it.hasNext()) {
658 list.add(it.next());
659 }
660 Collections.sort(list, ReverseComparator.INSTANCE);
661 it = list.iterator();
662 if (it.hasNext()) {
663 NodePointer pointer = (NodePointer) it.next();
664 pointer.remove();
665 while (it.hasNext()) {
666 removePath(((NodePointer) it.next()).asPath());
667 }
668 }
669 }
670 catch (Throwable ex) {
671 throw new JXPathException(
672 "Exception trying to remove all for xpath " + xpath,
673 ex);
674 }
675 }
676
677 public JXPathContext getRelativeContext(Pointer pointer) {
678 Object contextBean = pointer.getNode();
679 if (contextBean == null) {
680 throw new JXPathException(
681 "Cannot create a relative context for a non-existent node: "
682 + pointer);
683 }
684 return new JXPathContextReferenceImpl(this, contextBean, pointer);
685 }
686
687 public Pointer getContextPointer() {
688 return contextPointer;
689 }
690
691
692
693
694
695 private NodePointer getAbsoluteRootPointer() {
696 return (NodePointer) rootPointer;
697 }
698
699
700
701
702
703 private EvalContext getEvalContext() {
704 return new InitialContext(new RootContext(this,
705 (NodePointer) getContextPointer()));
706 }
707
708
709
710
711
712 public EvalContext getAbsoluteRootContext() {
713 return new InitialContext(new RootContext(this,
714 getAbsoluteRootPointer()));
715 }
716
717
718
719
720
721
722 public NodePointer getVariablePointer(QName name) {
723 return NodePointer.newNodePointer(name, VariablePointerFactory
724 .contextWrapper(this), getLocale());
725 }
726
727
728
729
730
731
732
733 public Function getFunction(QName functionName, Object[] parameters) {
734 String namespace = functionName.getPrefix();
735 String name = functionName.getName();
736 JXPathContext funcCtx = this;
737 Function func = null;
738 Functions funcs;
739 while (funcCtx != null) {
740 funcs = funcCtx.getFunctions();
741 if (funcs != null) {
742 func = funcs.getFunction(namespace, name, parameters);
743 if (func != null) {
744 return func;
745 }
746 }
747 funcCtx = funcCtx.getParentContext();
748 }
749 throw new JXPathFunctionNotFoundException(
750 "Undefined function: " + functionName.toString());
751 }
752
753 public void registerNamespace(String prefix, String namespaceURI) {
754 if (namespaceResolver.isSealed()) {
755 namespaceResolver = (NamespaceResolver) namespaceResolver.clone();
756 }
757 namespaceResolver.registerNamespace(prefix, namespaceURI);
758 }
759
760 public String getNamespaceURI(String prefix) {
761 return namespaceResolver.getNamespaceURI(prefix);
762 }
763
764
765
766
767
768 public String getPrefix(String namespaceURI) {
769 return namespaceResolver.getPrefix(namespaceURI);
770 }
771
772 public void setNamespaceContextPointer(Pointer pointer) {
773 if (namespaceResolver.isSealed()) {
774 namespaceResolver = (NamespaceResolver) namespaceResolver.clone();
775 }
776 namespaceResolver.setNamespaceContextPointer((NodePointer) pointer);
777 }
778
779 public Pointer getNamespaceContextPointer() {
780 return namespaceResolver.getNamespaceContextPointer();
781 }
782
783
784
785
786
787 public NamespaceResolver getNamespaceResolver() {
788 namespaceResolver.seal();
789 return namespaceResolver;
790 }
791
792
793
794
795 public void setExceptionHandler(ExceptionHandler exceptionHandler) {
796 if (rootPointer instanceof NodePointer) {
797 ((NodePointer) rootPointer).setExceptionHandler(exceptionHandler);
798 }
799 }
800
801
802
803
804
805
806
807
808 public static Object allocateConditionally(String className,
809 String existenceCheckClassName) {
810 try {
811 try {
812 ClassLoaderUtil.getClass(existenceCheckClassName, true);
813 }
814 catch (ClassNotFoundException ex) {
815 return null;
816 }
817 Class cls = ClassLoaderUtil.getClass(className, true);
818 return cls.newInstance();
819 }
820 catch (Exception ex) {
821 throw new JXPathException("Cannot allocate " + className, ex);
822 }
823 }
824 }