View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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   * The reference implementation of JXPathContext.
60   *
61   * @author Dmitri Plotnikov
62   * @version $Revision: 1523199 $ $Date: 2013-09-14 11:45:47 +0200 (Sa, 14 Sep 2013) $
63   */
64  public class JXPathContextReferenceImpl extends JXPathContext {
65  
66      /**
67       * Change this to <code>false</code> to disable soft caching of
68       * CompiledExpressions.
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      // The frequency of the cache cleanup
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          // DOM  factory is only registered if DOM support is on the classpath
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          // JDOM  factory is only registered if JDOM is on the classpath
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         // DynaBean factory is only registered if BeanUtils are on the classpath
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      * Create the default node factory array.
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      * Call this with a custom NodePointerFactory to add support for
137      * additional types of objects.  Make sure the factory returns
138      * a name that puts it in the right position on the list of factories.
139      * @param factory NodePointerFactory to add
140      */
141     public static void addNodePointerFactory(NodePointerFactory factory) {
142         synchronized (nodeFactories) {
143             nodeFactories.add(factory);
144             nodeFactoryArray = null;
145         }
146     }
147 
148     /**
149      * Get the registered NodePointerFactories.
150      * @return NodePointerFactory[]
151      */
152     public static NodePointerFactory[] getNodePointerFactories() {
153         return nodeFactoryArray;
154     }
155 
156     /** Namespace resolver */
157     protected NamespaceResolver namespaceResolver;
158 
159     private Pointer rootPointer;
160     private Pointer contextPointer;
161 
162     /**
163      * Create a new JXPathContextReferenceImpl.
164      * @param parentContext parent context
165      * @param contextBean Object
166      */
167     protected JXPathContextReferenceImpl(JXPathContext parentContext,
168             Object contextBean) {
169         this(parentContext, contextBean, null);
170     }
171 
172     /**
173      * Create a new JXPathContextReferenceImpl.
174      * @param parentContext parent context
175      * @param contextBean Object
176      * @param contextPointer context pointer
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      * Returns a static instance of TreeCompiler.
214      *
215      * Override this to return an alternate compiler.
216      * @return Compiler
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      * Compile the given expression.
228      * @param xpath to compile
229      * @return Expression
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      * Traverses the xpath and returns the resulting object. Primitive
277      * types are wrapped into objects.
278      * @param xpath expression
279      * @return Object found
280      */
281     public Object getValue(String xpath) {
282         Expression expression = compileExpression(xpath);
283 // TODO: (work in progress) - trying to integrate with Xalan
284 //        Object ctxNode = getNativeContextNode(expression);
285 //        if (ctxNode != null) {
286 //            System.err.println("WILL USE XALAN: " + xpath);
287 //            CachedXPathAPI api = new CachedXPathAPI();
288 //            try {
289 //                if (expression instanceof Path) {
290 //                    Node node = api.selectSingleNode((Node)ctxNode, xpath);
291 //                    System.err.println("NODE: " + node);
292 //                    if (node == null) {
293 //                        return null;
294 //                    }
295 //                    return new DOMNodePointer(node, null).getValue();
296 //                }
297 //                else {
298 //                    XObject object = api.eval((Node)ctxNode, xpath);
299 //                    switch (object.getType()) {
300 //                    case XObject.CLASS_STRING: return object.str();
301 //                    case XObject.CLASS_NUMBER: return new Double(object.num());
302 //                    case XObject.CLASS_BOOLEAN: return new Boolean(object.bool());
303 //                    default:
304 //                        System.err.println("OTHER TYPE: " + object.getTypeString());
305 //                    }
306 //                }
307 //            }
308 //            catch (TransformerException e) {
309 //                // TODO Auto-generated catch block
310 //                e.printStackTrace();
311 //            }
312 //            return
313 //        }
314 
315         return getValue(xpath, expression);
316     }
317 
318 //    private Object getNativeContextNode(Expression expression) {
319 //        Object node = getNativeContextNode(getContextBean());
320 //        if (node == null) {
321 //            return null;
322 //        }
323 //
324 //        List vars = expression.getUsedVariables();
325 //        if (vars != null) {
326 //            return null;
327 //        }
328 //
329 //        return node;
330 //    }
331 
332 //    private Object getNativeContextNode(Object bean) {
333 //        if (bean instanceof Number || bean instanceof String || bean instanceof Boolean) {
334 //            return bean;
335 //        }
336 //        if (bean instanceof Node) {
337 //            return (Node)bean;
338 //        }
339 //
340 //        if (bean instanceof Container) {
341 //            bean = ((Container)bean).getValue();
342 //            return getNativeContextNode(bean);
343 //        }
344 //
345 //        return null;
346 //    }
347 
348     /**
349      * Get the value indicated.
350      * @param xpath String
351      * @param expr Expression
352      * @return Object
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      * Calls getValue(xpath), converts the result to the required type
383      * and returns the result of the conversion.
384      * @param xpath expression
385      * @param requiredType Class
386      * @return Object
387      */
388     public Object getValue(String xpath, Class requiredType) {
389         Expression expr = compileExpression(xpath);
390         return getValue(xpath, expr, requiredType);
391     }
392 
393     /**
394      * Get the value indicated.
395      * @param xpath expression
396      * @param expr compiled Expression
397      * @param requiredType Class
398      * @return Object
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      * Traverses the xpath and returns a Iterator of all results found
419      * for the path. If the xpath matches no properties
420      * in the graph, the Iterator will not be null.
421      * @param xpath expression
422      * @return Iterator
423      */
424     public Iterator iterate(String xpath) {
425         return iterate(xpath, compileExpression(xpath));
426     }
427 
428     /**
429      * Traverses the xpath and returns a Iterator of all results found
430      * for the path. If the xpath matches no properties
431      * in the graph, the Iterator will not be null.
432      * @param xpath expression
433      * @param expr compiled Expression
434      * @return Iterator
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      * Get a pointer to the specified path/expression.
446      * @param xpath String
447      * @param expr compiled Expression
448      * @return Pointer
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      * Set the value of xpath to value.
471      * @param xpath path
472      * @param expr compiled Expression
473      * @param value Object
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      * Create the given path.
491      * @param xpath String
492      * @param expr compiled Expression
493      * @return resulting Pointer
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                 // This should never happen
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      * Create the given path setting its value to value.
527      * @param xpath String
528      * @param expr compiled Expression
529      * @param value Object
530      * @return resulting Pointer
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      * Set the specified value.
546      * @param xpath path
547      * @param expr compiled Expression
548      * @param value destination value
549      * @param create whether to create missing node(s)
550      * @return Pointer created
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             // This should never happen
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      * Checks if the path follows the JXPath restrictions on the type
583      * of path that can be passed to create... methods.
584      * @param expr Expression to check
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      * Traverses the xpath and returns an Iterator of Pointers.
598      * A Pointer provides easy access to a property.
599      * If the xpath matches no properties
600      * in the graph, the Iterator be empty, but not null.
601      * @param xpath expression
602      * @return Iterator
603      */
604     public Iterator iteratePointers(String xpath) {
605         return iteratePointers(xpath, compileExpression(xpath));
606     }
607 
608     /**
609      * Traverses the xpath and returns an Iterator of Pointers.
610      * A Pointer provides easy access to a property.
611      * If the xpath matches no properties
612      * in the graph, the Iterator be empty, but not null.
613      * @param xpath expression
614      * @param expr compiled Expression
615      * @return Iterator
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      * Remove the specified path.
627      * @param xpath expression
628      * @param expr compiled Expression
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      * Remove all matching nodes.
650      * @param xpath expression
651      * @param expr compiled Expression
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      * Get absolute root pointer.
693      * @return NodePointer
694      */
695     private NodePointer getAbsoluteRootPointer() {
696         return (NodePointer) rootPointer;
697     }
698 
699     /**
700      * Get the evaluation context.
701      * @return EvalContext
702      */
703     private EvalContext getEvalContext() {
704         return new InitialContext(new RootContext(this,
705                 (NodePointer) getContextPointer()));
706     }
707 
708     /**
709      * Get the absolute root context.
710      * @return EvalContext
711      */
712     public EvalContext getAbsoluteRootContext() {
713         return new InitialContext(new RootContext(this,
714                 getAbsoluteRootPointer()));
715     }
716 
717     /**
718      * Get a VariablePointer for the given variable name.
719      * @param name variable name
720      * @return NodePointer
721      */
722     public NodePointer getVariablePointer(QName name) {
723         return NodePointer.newNodePointer(name, VariablePointerFactory
724                 .contextWrapper(this), getLocale());
725     }
726 
727     /**
728      * Get the named Function.
729      * @param functionName name
730      * @param parameters function args
731      * @return Function
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      * {@inheritDoc}
766      * @see org.apache.commons.jxpath.JXPathContext#getPrefix(java.lang.String)
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      * Get the namespace resolver.
785      * @return NamespaceResolver
786      */
787     public NamespaceResolver getNamespaceResolver() {
788         namespaceResolver.seal();
789         return namespaceResolver;
790     }
791 
792     /**
793      * {@inheritDoc}
794      */
795     public void setExceptionHandler(ExceptionHandler exceptionHandler) {
796         if (rootPointer instanceof NodePointer) {
797             ((NodePointer) rootPointer).setExceptionHandler(exceptionHandler);
798         }
799     }
800 
801     /**
802      * Checks if existenceCheckClass exists on the class path. If so, allocates
803      * an instance of the specified class, otherwise returns null.
804      * @param className to instantiate
805      * @param existenceCheckClassName guard class
806      * @return className instance
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 }