KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > jxpath > ri > JXPathContextReferenceImpl


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.commons.jxpath.ri;
17
18
19 import java.lang.ref.SoftReference JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Arrays JavaDoc;
22 import java.util.Collections JavaDoc;
23 import java.util.Comparator JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.Vector JavaDoc;
28 import java.util.Map.Entry;
29
30 import org.apache.commons.jxpath.CompiledExpression;
31 import org.apache.commons.jxpath.Function;
32 import org.apache.commons.jxpath.Functions;
33 import org.apache.commons.jxpath.JXPathContext;
34 import org.apache.commons.jxpath.JXPathException;
35 import org.apache.commons.jxpath.Pointer;
36 import org.apache.commons.jxpath.Variables;
37 import org.apache.commons.jxpath.ri.axes.InitialContext;
38 import org.apache.commons.jxpath.ri.axes.RootContext;
39 import org.apache.commons.jxpath.ri.compiler.Expression;
40 import org.apache.commons.jxpath.ri.compiler.LocationPath;
41 import org.apache.commons.jxpath.ri.compiler.Path;
42 import org.apache.commons.jxpath.ri.compiler.TreeCompiler;
43 import org.apache.commons.jxpath.ri.model.NodePointer;
44 import org.apache.commons.jxpath.ri.model.NodePointerFactory;
45 import org.apache.commons.jxpath.ri.model.VariablePointer;
46 import org.apache.commons.jxpath.ri.model.beans.BeanPointerFactory;
47 import org.apache.commons.jxpath.ri.model.beans.CollectionPointerFactory;
48 import org.apache.commons.jxpath.ri.model.container.ContainerPointerFactory;
49 import org.apache.commons.jxpath.ri.model.dynamic.DynamicPointerFactory;
50 import org.apache.commons.jxpath.util.TypeUtils;
51
52 /**
53  * The reference implementation of JXPathContext.
54  *
55  * @author Dmitri Plotnikov
56  * @version $Revision: 1.43 $ $Date: 2004/04/04 23:16:23 $
57  */

58 public class JXPathContextReferenceImpl extends JXPathContext {
59     
60     /**
61      * Change this to <code>false</code> to disable soft caching of
62      * CompiledExpressions.
63      */

64     public static final boolean USE_SOFT_CACHE = true;
65     
66     private static final Compiler JavaDoc COMPILER = new TreeCompiler();
67     private static Map JavaDoc compiled = new HashMap JavaDoc();
68     private static int cleanupCount = 0;
69     
70     private static Vector JavaDoc nodeFactories = new Vector JavaDoc();
71     private static NodePointerFactory nodeFactoryArray[] = null;
72     static {
73         nodeFactories.add(new CollectionPointerFactory());
74         nodeFactories.add(new BeanPointerFactory());
75         nodeFactories.add(new DynamicPointerFactory());
76
77         // DOM factory is only registered if DOM support is on the classpath
78
Object JavaDoc domFactory = allocateConditionally(
79                 "org.apache.commons.jxpath.ri.model.dom.DOMPointerFactory",
80                 "org.w3c.dom.Node");
81         if (domFactory != null) {
82             nodeFactories.add(domFactory);
83         }
84
85         // JDOM factory is only registered if JDOM is on the classpath
86
Object JavaDoc jdomFactory = allocateConditionally(
87                 "org.apache.commons.jxpath.ri.model.jdom.JDOMPointerFactory",
88                 "org.jdom.Document");
89         if (jdomFactory != null) {
90             nodeFactories.add(jdomFactory);
91         }
92
93         // DynaBean factory is only registered if BeanUtils are on the classpath
94
Object JavaDoc dynaBeanFactory =
95             allocateConditionally(
96                 "org.apache.commons.jxpath.ri.model.dynabeans."
97                     + "DynaBeanPointerFactory",
98                 "org.apache.commons.beanutils.DynaBean");
99         if (dynaBeanFactory != null) {
100             nodeFactories.add(dynaBeanFactory);
101         }
102
103         nodeFactories.add(new ContainerPointerFactory());
104         createNodeFactoryArray();
105     }
106
107     private Pointer rootPointer;
108     private Pointer contextPointer;
109     
110     protected NamespaceResolver namespaceResolver;
111
112     // The frequency of the cache cleanup
113
private static final int CLEANUP_THRESHOLD = 500;
114
115     protected JXPathContextReferenceImpl(JXPathContext parentContext,
116                                          Object JavaDoc contextBean)
117     {
118         this(parentContext, contextBean, null);
119     }
120
121     public JXPathContextReferenceImpl(
122         JXPathContext parentContext,
123         Object JavaDoc contextBean,
124         Pointer contextPointer)
125     {
126         super(parentContext, contextBean);
127
128         synchronized (nodeFactories) {
129             createNodeFactoryArray();
130         }
131                 
132         if (contextPointer != null) {
133             this.contextPointer = contextPointer;
134             this.rootPointer =
135                 NodePointer.newNodePointer(
136                     new QName(null, "root"),
137                     contextPointer.getRootNode(),
138                     getLocale());
139         }
140         else {
141             this.contextPointer =
142                 NodePointer.newNodePointer(
143                     new QName(null, "root"),
144                     contextBean,
145                     getLocale());
146             this.rootPointer = this.contextPointer;
147         }
148         
149         namespaceResolver = new NamespaceResolver();
150         namespaceResolver
151                 .setNamespaceContextPointer((NodePointer) this.contextPointer);
152     }
153
154     private static void createNodeFactoryArray() {
155         if (nodeFactoryArray == null) {
156             nodeFactoryArray =
157                 (NodePointerFactory[]) nodeFactories.
158                     toArray(new NodePointerFactory[0]);
159             Arrays.sort(nodeFactoryArray, new Comparator JavaDoc() {
160                 public int compare(Object JavaDoc a, Object JavaDoc b) {
161                     int orderA = ((NodePointerFactory) a).getOrder();
162                     int orderB = ((NodePointerFactory) b).getOrder();
163                     return orderA - orderB;
164                 }
165             });
166         }
167     }
168     
169     /**
170      * Call this with a custom NodePointerFactory to add support for
171      * additional types of objects. Make sure the factory returns
172      * a name that puts it in the right position on the list of factories.
173      */

174     public static void addNodePointerFactory(NodePointerFactory factory) {
175         synchronized (nodeFactories) {
176             nodeFactories.add(factory);
177             nodeFactoryArray = null;
178         }
179     }
180
181     public static NodePointerFactory[] getNodePointerFactories() {
182         return nodeFactoryArray;
183     }
184
185     /**
186      * Returns a static instance of TreeCompiler.
187      *
188      * Override this to return an aternate compiler.
189      */

190     protected Compiler JavaDoc getCompiler() {
191         return COMPILER;
192     }
193     
194     protected CompiledExpression compilePath(String JavaDoc xpath) {
195         return new JXPathCompiledExpression(xpath, compileExpression(xpath));
196     }
197
198     private Expression compileExpression(String JavaDoc xpath) {
199         Expression expr;
200
201         synchronized (compiled) {
202             if (USE_SOFT_CACHE) {
203                 expr = null;
204                 SoftReference JavaDoc ref = (SoftReference JavaDoc) compiled.get(xpath);
205                 if (ref != null) {
206                     expr = (Expression) ref.get();
207                 }
208             }
209             else {
210                 expr = (Expression) compiled.get(xpath);
211             }
212         }
213
214         if (expr != null) {
215             return expr;
216         }
217
218         expr = (Expression) Parser.parseExpression(xpath, getCompiler());
219
220         synchronized (compiled) {
221             if (USE_SOFT_CACHE) {
222                 if (cleanupCount++ >= CLEANUP_THRESHOLD) {
223                     Iterator JavaDoc it = compiled.entrySet().iterator();
224                     while (it.hasNext()) {
225                         Entry me = (Entry) it.next();
226                         if (((SoftReference JavaDoc) me.getValue()).get() == null) {
227                             it.remove();
228                         }
229                     }
230                     cleanupCount = 0;
231                 }
232                 compiled.put(xpath, new SoftReference JavaDoc(expr));
233             }
234             else {
235                 compiled.put(xpath, expr);
236             }
237         }
238
239         return expr;
240     }
241
242     /**
243      * Traverses the xpath and returns the resulting object. Primitive
244      * types are wrapped into objects.
245      */

246     public Object JavaDoc getValue(String JavaDoc xpath) {
247         Expression expression = compileExpression(xpath);
248 // TODO: (work in progress) - trying to integrate with Xalan
249
// Object ctxNode = getNativeContextNode(expression);
250
// if (ctxNode != null) {
251
// System.err.println("WILL USE XALAN: " + xpath);
252
// CachedXPathAPI api = new CachedXPathAPI();
253
// try {
254
// if (expression instanceof Path) {
255
// Node node = api.selectSingleNode((Node)ctxNode, xpath);
256
// System.err.println("NODE: " + node);
257
// if (node == null) {
258
// return null;
259
// }
260
// return new DOMNodePointer(node, null).getValue();
261
// }
262
// else {
263
// XObject object = api.eval((Node)ctxNode, xpath);
264
// switch (object.getType()) {
265
// case XObject.CLASS_STRING: return object.str();
266
// case XObject.CLASS_NUMBER: return new Double(object.num());
267
// case XObject.CLASS_BOOLEAN: return new Boolean(object.bool());
268
// default:
269
// System.err.println("OTHER TYPE: " + object.getTypeString());
270
// }
271
// }
272
// }
273
// catch (TransformerException e) {
274
// // TODO Auto-generated catch block
275
// e.printStackTrace();
276
// }
277
// return
278
// }
279

280         return getValue(xpath, expression);
281     }
282
283 // private Object getNativeContextNode(Expression expression) {
284
// Object node = getNativeContextNode(getContextBean());
285
// if (node == null) {
286
// return null;
287
// }
288
//
289
// List vars = expression.getUsedVariables();
290
// if (vars != null) {
291
// return null;
292
// }
293
//
294
// return node;
295
// }
296

297 // private Object getNativeContextNode(Object bean) {
298
// if (bean instanceof Number || bean instanceof String || bean instanceof Boolean) {
299
// return bean;
300
// }
301
// if (bean instanceof Node) {
302
// return (Node)bean;
303
// }
304
//
305
// if (bean instanceof Container) {
306
// bean = ((Container)bean).getValue();
307
// return getNativeContextNode(bean);
308
// }
309
//
310
// return null;
311
// }
312

313     public Object JavaDoc getValue(String JavaDoc xpath, Expression expr) {
314         Object JavaDoc result = expr.computeValue(getEvalContext());
315         if (result == null) {
316             if (expr instanceof Path) {
317                 if (!isLenient()) {
318                     throw new JXPathException("No value for xpath: " + xpath);
319                 }
320             }
321             return null;
322         }
323         if (result instanceof EvalContext) {
324             EvalContext ctx = (EvalContext) result;
325             result = ctx.getSingleNodePointer();
326             if (!isLenient() && result == null) {
327                 throw new JXPathException("No value for xpath: " + xpath);
328             }
329         }
330         if (result instanceof NodePointer) {
331             result = ((NodePointer) result).getValuePointer();
332             if (!isLenient() && !((NodePointer) result).isActual()) {
333                 // We need to differentiate between pointers representing
334
// a non-existing property and ones representing a property
335
// whose value is null. In the latter case, the pointer
336
// is going to have isActual == false, but its parent,
337
// which is a non-node pointer identifying the bean property,
338
// will return isActual() == true.
339
NodePointer parent =
340                     ((NodePointer) result).getImmediateParentPointer();
341                 if (parent == null
342                     || !parent.isContainer()
343                     || !parent.isActual()) {
344                     throw new JXPathException("No value for xpath: " + xpath);
345                 }
346             }
347             result = ((NodePointer) result).getValue();
348         }
349         return result;
350     }
351
352     /**
353      * Calls getValue(xpath), converts the result to the required type
354      * and returns the result of the conversion.
355      */

356     public Object JavaDoc getValue(String JavaDoc xpath, Class JavaDoc requiredType) {
357         Expression expr = compileExpression(xpath);
358         return getValue(xpath, expr, requiredType);
359     }
360
361     public Object JavaDoc getValue(String JavaDoc xpath, Expression expr, Class JavaDoc requiredType) {
362         Object JavaDoc value = getValue(xpath, expr);
363         if (value != null && requiredType != null) {
364             if (!TypeUtils.canConvert(value, requiredType)) {
365                 throw new JXPathException(
366                     "Invalid expression type. '"
367                         + xpath
368                         + "' returns "
369                         + value.getClass().getName()
370                         + ". It cannot be converted to "
371                         + requiredType.getName());
372             }
373             value = TypeUtils.convert(value, requiredType);
374         }
375         return value;
376     }
377
378     /**
379      * Traverses the xpath and returns a Iterator of all results found
380      * for the path. If the xpath matches no properties
381      * in the graph, the Iterator will not be null.
382      */

383     public Iterator JavaDoc iterate(String JavaDoc xpath) {
384         return iterate(xpath, compileExpression(xpath));
385     }
386
387     public Iterator JavaDoc iterate(String JavaDoc xpath, Expression expr) {
388         return expr.iterate(getEvalContext());
389     }
390
391     public Pointer getPointer(String JavaDoc xpath) {
392         return getPointer(xpath, compileExpression(xpath));
393     }
394
395     public Pointer getPointer(String JavaDoc xpath, Expression expr) {
396         Object JavaDoc result = expr.computeValue(getEvalContext());
397         if (result instanceof EvalContext) {
398             result = ((EvalContext) result).getSingleNodePointer();
399         }
400         if (result instanceof Pointer) {
401             if (!isLenient() && !((NodePointer) result).isActual()) {
402                 throw new JXPathException("No pointer for xpath: " + xpath);
403             }
404             return (Pointer) result;
405         }
406         else {
407             return NodePointer.newNodePointer(null, result, getLocale());
408         }
409     }
410
411     public void setValue(String JavaDoc xpath, Object JavaDoc value) {
412         setValue(xpath, compileExpression(xpath), value);
413     }
414
415
416     public void setValue(String JavaDoc xpath, Expression expr, Object JavaDoc value) {
417         try {
418             setValue(xpath, expr, value, false);
419         }
420         catch (Throwable JavaDoc ex) {
421             throw new JXPathException(
422                 "Exception trying to set value with xpath " + xpath, ex);
423         }
424     }
425
426     public Pointer createPath(String JavaDoc xpath) {
427         return createPath(xpath, compileExpression(xpath));
428     }
429
430     public Pointer createPath(String JavaDoc xpath, Expression expr) {
431         try {
432             Object JavaDoc result = expr.computeValue(getEvalContext());
433             Pointer pointer = null;
434
435             if (result instanceof Pointer) {
436                 pointer = (Pointer) result;
437             }
438             else if (result instanceof EvalContext) {
439                 EvalContext ctx = (EvalContext) result;
440                 pointer = ctx.getSingleNodePointer();
441             }
442             else {
443                 checkSimplePath(expr);
444                 // This should never happen
445
throw new JXPathException("Cannot create path:" + xpath);
446             }
447             return ((NodePointer) pointer).createPath(this);
448         }
449         catch (Throwable JavaDoc ex) {
450             throw new JXPathException(
451                 "Exception trying to create xpath " + xpath,
452                 ex);
453         }
454     }
455
456     public Pointer createPathAndSetValue(String JavaDoc xpath, Object JavaDoc value) {
457         return createPathAndSetValue(xpath, compileExpression(xpath), value);
458     }
459
460     public Pointer createPathAndSetValue(
461         String JavaDoc xpath,
462         Expression expr,
463         Object JavaDoc value)
464     {
465         try {
466             return setValue(xpath, expr, value, true);
467         }
468         catch (Throwable JavaDoc ex) {
469             throw new JXPathException(
470                 "Exception trying to create xpath " + xpath,
471                 ex);
472         }
473     }
474
475     private Pointer setValue(
476         String JavaDoc xpath,
477         Expression expr,
478         Object JavaDoc value,
479         boolean create)
480     {
481         Object JavaDoc result = expr.computeValue(getEvalContext());
482         Pointer pointer = null;
483
484         if (result instanceof Pointer) {
485             pointer = (Pointer) result;
486         }
487         else if (result instanceof EvalContext) {
488             EvalContext ctx = (EvalContext) result;
489             pointer = ctx.getSingleNodePointer();
490         }
491         else {
492             if (create) {
493                 checkSimplePath(expr);
494             }
495             
496             // This should never happen
497
throw new JXPathException("Cannot set value for xpath: " + xpath);
498         }
499         if (create) {
500             pointer = ((NodePointer) pointer).createPath(this, value);
501         }
502         else {
503             pointer.setValue(value);
504         }
505         return pointer;
506     }
507
508     /**
509      * Checks if the path follows the JXPath restrictions on the type
510      * of path that can be passed to create... methods.
511      */

512     private void checkSimplePath(Expression expr) {
513         if (!(expr instanceof LocationPath)
514             || !((LocationPath) expr).isSimplePath()) {
515             throw new JXPathException(
516                 "JXPath can only create a path if it uses exclusively "
517                     + "the child:: and attribute:: axes and has "
518                     + "no context-dependent predicates");
519         }
520     }
521
522     /**
523      * Traverses the xpath and returns an Iterator of Pointers.
524      * A Pointer provides easy access to a property.
525      * If the xpath matches no properties
526      * in the graph, the Iterator be empty, but not null.
527      */

528     public Iterator JavaDoc iteratePointers(String JavaDoc xpath) {
529         return iteratePointers(xpath, compileExpression(xpath));
530     }
531
532     public Iterator JavaDoc iteratePointers(String JavaDoc xpath, Expression expr) {
533         return expr.iteratePointers(getEvalContext());
534     }
535
536     public void removePath(String JavaDoc xpath) {
537         removePath(xpath, compileExpression(xpath));
538     }
539
540     public void removePath(String JavaDoc xpath, Expression expr) {
541         try {
542             NodePointer pointer = (NodePointer) getPointer(xpath, expr);
543             if (pointer != null) {
544                 ((NodePointer) pointer).remove();
545             }
546         }
547         catch (Throwable JavaDoc ex) {
548             throw new JXPathException(
549                 "Exception trying to remove xpath " + xpath,
550                 ex);
551         }
552     }
553
554     public void removeAll(String JavaDoc xpath) {
555         removeAll(xpath, compileExpression(xpath));
556     }
557
558     public void removeAll(String JavaDoc xpath, Expression expr) {
559         try {
560             ArrayList JavaDoc list = new ArrayList JavaDoc();
561             Iterator JavaDoc it = expr.iteratePointers(getEvalContext());
562             while (it.hasNext()) {
563                 list.add(it.next());
564             }
565             Collections.sort(list);
566             for (int i = list.size() - 1; i >= 0; i--) {
567                 NodePointer pointer = (NodePointer) list.get(i);
568                 pointer.remove();
569             }
570         }
571         catch (Throwable JavaDoc ex) {
572             throw new JXPathException(
573                 "Exception trying to remove all for xpath " + xpath,
574                 ex);
575         }
576     }
577
578     public JXPathContext getRelativeContext(Pointer pointer) {
579         Object JavaDoc contextBean = pointer.getNode();
580         if (contextBean == null) {
581             throw new JXPathException(
582                 "Cannot create a relative context for a non-existent node: "
583                     + pointer);
584         }
585         return new JXPathContextReferenceImpl(this, contextBean, pointer);
586     }
587     
588     public Pointer getContextPointer() {
589         return contextPointer;
590     }
591
592     private NodePointer getAbsoluteRootPointer() {
593         return (NodePointer) rootPointer;
594     }
595
596     private EvalContext getEvalContext() {
597         return new InitialContext(new RootContext(this,
598                 (NodePointer) getContextPointer()));
599     }
600
601     public EvalContext getAbsoluteRootContext() {
602         return new InitialContext(new RootContext(this,
603                 getAbsoluteRootPointer()));
604     }
605
606     public NodePointer getVariablePointer(QName name) {
607         String JavaDoc varName = name.toString();
608         JXPathContext varCtx = this;
609         Variables vars = null;
610         while (varCtx != null) {
611             vars = varCtx.getVariables();
612             if (vars.isDeclaredVariable(varName)) {
613                 break;
614             }
615             varCtx = varCtx.getParentContext();
616             vars = null;
617         }
618         if (vars != null) {
619             return new VariablePointer(vars, name);
620         }
621         else {
622             // The variable is not declared, but we will create
623
// a pointer anyway in case the user want to set, rather
624
// than get, the value of the variable.
625
return new VariablePointer(name);
626         }
627     }
628
629     public Function getFunction(QName functionName, Object JavaDoc[] parameters) {
630         String JavaDoc namespace = functionName.getPrefix();
631         String JavaDoc name = functionName.getName();
632         JXPathContext funcCtx = this;
633         Function func = null;
634         Functions funcs;
635         while (funcCtx != null) {
636             funcs = funcCtx.getFunctions();
637             if (funcs != null) {
638                 func = funcs.getFunction(namespace, name, parameters);
639                 if (func != null) {
640                     return func;
641                 }
642             }
643             funcCtx = funcCtx.getParentContext();
644         }
645         throw new JXPathException(
646             "Undefined function: " + functionName.toString());
647     }
648     
649     public void registerNamespace(String JavaDoc prefix, String JavaDoc namespaceURI) {
650         if (namespaceResolver.isSealed()) {
651             namespaceResolver = (NamespaceResolver) namespaceResolver.clone();
652         }
653         namespaceResolver.registerNamespace(prefix, namespaceURI);
654     }
655     
656     public String JavaDoc getNamespaceURI(String JavaDoc prefix) {
657         return namespaceResolver.getNamespaceURI(prefix);
658     }
659     
660     public void setNamespaceContextPointer(Pointer pointer) {
661         if (namespaceResolver.isSealed()) {
662             namespaceResolver = (NamespaceResolver) namespaceResolver.clone();
663         }
664         namespaceResolver.setNamespaceContextPointer((NodePointer) pointer);
665     }
666     
667     public Pointer getNamespaceContextPointer() {
668         return namespaceResolver.getNamespaceContextPointer();
669     }
670
671     public NamespaceResolver getNamespaceResolver() {
672         namespaceResolver.seal();
673         return namespaceResolver;
674     }
675     
676     /**
677      * Checks if existenceCheckClass exists on the class path. If so, allocates
678      * an instance of the specified class, otherwise returns null.
679      */

680     public static Object JavaDoc allocateConditionally(
681             String JavaDoc className,
682             String JavaDoc existenceCheckClassName)
683     {
684         try {
685             try {
686                 Class.forName(existenceCheckClassName);
687             }
688             catch (ClassNotFoundException JavaDoc ex) {
689                 return null;
690             }
691
692             Class JavaDoc cls = Class.forName(className);
693             return cls.newInstance();
694         }
695         catch (Exception JavaDoc ex) {
696             throw new JXPathException("Cannot allocate " + className, ex);
697         }
698     }
699 }
Popular Tags