001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.jxpath.ri;
018
019import java.lang.ref.SoftReference;
020import java.util.ArrayList;
021import java.util.Arrays;
022import java.util.Collections;
023import java.util.Comparator;
024import java.util.HashMap;
025import java.util.Iterator;
026import java.util.Map;
027import java.util.Vector;
028import java.util.Map.Entry;
029
030import org.apache.commons.jxpath.CompiledExpression;
031import org.apache.commons.jxpath.Function;
032import org.apache.commons.jxpath.Functions;
033import org.apache.commons.jxpath.JXPathContext;
034import org.apache.commons.jxpath.JXPathException;
035import org.apache.commons.jxpath.JXPathFunctionNotFoundException;
036import org.apache.commons.jxpath.JXPathInvalidSyntaxException;
037import org.apache.commons.jxpath.JXPathNotFoundException;
038import org.apache.commons.jxpath.JXPathTypeConversionException;
039import org.apache.commons.jxpath.Pointer;
040import org.apache.commons.jxpath.ri.axes.InitialContext;
041import org.apache.commons.jxpath.ri.axes.RootContext;
042import org.apache.commons.jxpath.ri.compiler.Expression;
043import org.apache.commons.jxpath.ri.compiler.LocationPath;
044import org.apache.commons.jxpath.ri.compiler.Path;
045import org.apache.commons.jxpath.ri.compiler.TreeCompiler;
046import org.apache.commons.jxpath.ri.model.NodePointer;
047import org.apache.commons.jxpath.ri.model.NodePointerFactory;
048import org.apache.commons.jxpath.ri.model.VariablePointerFactory;
049import org.apache.commons.jxpath.ri.model.beans.BeanPointerFactory;
050import org.apache.commons.jxpath.ri.model.beans.CollectionPointerFactory;
051import org.apache.commons.jxpath.ri.model.container.ContainerPointerFactory;
052import org.apache.commons.jxpath.ri.model.dynamic.DynamicPointerFactory;
053import org.apache.commons.jxpath.util.ReverseComparator;
054import org.apache.commons.jxpath.util.TypeUtils;
055
056/**
057 * The reference implementation of JXPathContext.
058 *
059 * @author Dmitri Plotnikov
060 * @version $Revision: 670727 $ $Date: 2008-06-23 15:10:38 -0500 (Mon, 23 Jun 2008) $
061 */
062public class JXPathContextReferenceImpl extends JXPathContext {
063
064    /**
065     * Change this to <code>false</code> to disable soft caching of
066     * CompiledExpressions.
067     */
068    public static final boolean USE_SOFT_CACHE = true;
069
070    private static final Compiler COMPILER = new TreeCompiler();
071    private static Map compiled = new HashMap();
072    private static int cleanupCount = 0;
073
074    private static NodePointerFactory[] nodeFactoryArray = null;
075    // The frequency of the cache cleanup
076    private static final int CLEANUP_THRESHOLD = 500;
077    private static final Vector nodeFactories = new Vector();
078
079    static {
080        nodeFactories.add(new CollectionPointerFactory());
081        nodeFactories.add(new BeanPointerFactory());
082        nodeFactories.add(new DynamicPointerFactory());
083        nodeFactories.add(new VariablePointerFactory());
084
085        // DOM  factory is only registered if DOM support is on the classpath
086        Object domFactory = allocateConditionally(
087                "org.apache.commons.jxpath.ri.model.dom.DOMPointerFactory",
088                "org.w3c.dom.Node");
089        if (domFactory != null) {
090            nodeFactories.add(domFactory);
091        }
092
093        // JDOM  factory is only registered if JDOM is on the classpath
094        Object jdomFactory = allocateConditionally(
095                "org.apache.commons.jxpath.ri.model.jdom.JDOMPointerFactory",
096                "org.jdom.Document");
097        if (jdomFactory != null) {
098            nodeFactories.add(jdomFactory);
099        }
100
101        // DynaBean factory is only registered if BeanUtils are on the classpath
102        Object dynaBeanFactory =
103            allocateConditionally(
104                "org.apache.commons.jxpath.ri.model.dynabeans."
105                    + "DynaBeanPointerFactory",
106                "org.apache.commons.beanutils.DynaBean");
107        if (dynaBeanFactory != null) {
108            nodeFactories.add(dynaBeanFactory);
109        }
110
111        nodeFactories.add(new ContainerPointerFactory());
112        createNodeFactoryArray();
113    }
114
115    /**
116     * Create the default node factory array.
117     */
118    private static synchronized void createNodeFactoryArray() {
119        if (nodeFactoryArray == null) {
120            nodeFactoryArray =
121                (NodePointerFactory[]) nodeFactories.
122                    toArray(new NodePointerFactory[nodeFactories.size()]);
123            Arrays.sort(nodeFactoryArray, new Comparator() {
124                public int compare(Object a, Object b) {
125                    int orderA = ((NodePointerFactory) a).getOrder();
126                    int orderB = ((NodePointerFactory) b).getOrder();
127                    return orderA - orderB;
128                }
129            });
130        }
131    }
132
133    /**
134     * Call this with a custom NodePointerFactory to add support for
135     * additional types of objects.  Make sure the factory returns
136     * a name that puts it in the right position on the list of factories.
137     * @param factory NodePointerFactory to add
138     */
139    public static void addNodePointerFactory(NodePointerFactory factory) {
140        synchronized (nodeFactories) {
141            nodeFactories.add(factory);
142            nodeFactoryArray = null;
143        }
144    }
145
146    /**
147     * Get the registered NodePointerFactories.
148     * @return NodePointerFactory[]
149     */
150    public static NodePointerFactory[] getNodePointerFactories() {
151        return nodeFactoryArray;
152    }
153
154    /** Namespace resolver */
155    protected NamespaceResolver namespaceResolver;
156
157    private Pointer rootPointer;
158    private Pointer contextPointer;
159
160    /**
161     * Create a new JXPathContextReferenceImpl.
162     * @param parentContext parent context
163     * @param contextBean Object
164     */
165    protected JXPathContextReferenceImpl(JXPathContext parentContext,
166            Object contextBean) {
167        this(parentContext, contextBean, null);
168    }
169
170    /**
171     * Create a new JXPathContextReferenceImpl.
172     * @param parentContext parent context
173     * @param contextBean Object
174     * @param contextPointer context pointer
175     */
176    public JXPathContextReferenceImpl(JXPathContext parentContext,
177            Object contextBean, Pointer contextPointer) {
178        super(parentContext, contextBean);
179
180        synchronized (nodeFactories) {
181            createNodeFactoryArray();
182        }
183
184        if (contextPointer != null) {
185            this.contextPointer = contextPointer;
186            this.rootPointer =
187                NodePointer.newNodePointer(
188                    new QName(null, "root"),
189                    contextPointer.getRootNode(),
190                    getLocale());
191        }
192        else {
193            this.contextPointer =
194                NodePointer.newNodePointer(
195                    new QName(null, "root"),
196                    contextBean,
197                    getLocale());
198            this.rootPointer = this.contextPointer;
199        }
200
201        NamespaceResolver parentNR = null;
202        if (parentContext instanceof JXPathContextReferenceImpl) {
203            parentNR = ((JXPathContextReferenceImpl) parentContext).getNamespaceResolver();
204        }
205        namespaceResolver = new NamespaceResolver(parentNR);
206        namespaceResolver
207                .setNamespaceContextPointer((NodePointer) this.contextPointer);
208    }
209
210    /**
211     * Returns a static instance of TreeCompiler.
212     *
213     * Override this to return an alternate compiler.
214     * @return Compiler
215     */
216    protected Compiler getCompiler() {
217        return COMPILER;
218    }
219
220    protected CompiledExpression compilePath(String xpath) {
221        return new JXPathCompiledExpression(xpath, compileExpression(xpath));
222    }
223
224    /**
225     * Compile the given expression.
226     * @param xpath to compile
227     * @return Expression
228     */
229    private Expression compileExpression(String xpath) {
230        Expression expr;
231
232        synchronized (compiled) {
233            if (USE_SOFT_CACHE) {
234                expr = null;
235                SoftReference ref = (SoftReference) compiled.get(xpath);
236                if (ref != null) {
237                    expr = (Expression) ref.get();
238                }
239            }
240            else {
241                expr = (Expression) compiled.get(xpath);
242            }
243        }
244
245        if (expr != null) {
246            return expr;
247        }
248
249        expr = (Expression) Parser.parseExpression(xpath, getCompiler());
250
251        synchronized (compiled) {
252            if (USE_SOFT_CACHE) {
253                if (cleanupCount++ >= CLEANUP_THRESHOLD) {
254                    Iterator it = compiled.entrySet().iterator();
255                    while (it.hasNext()) {
256                        Entry me = (Entry) it.next();
257                        if (((SoftReference) me.getValue()).get() == null) {
258                            it.remove();
259                        }
260                    }
261                    cleanupCount = 0;
262                }
263                compiled.put(xpath, new SoftReference(expr));
264            }
265            else {
266                compiled.put(xpath, expr);
267            }
268        }
269
270        return expr;
271    }
272
273    /**
274     * Traverses the xpath and returns the resulting object. Primitive
275     * types are wrapped into objects.
276     * @param xpath expression
277     * @return Object found
278     */
279    public Object getValue(String xpath) {
280        Expression expression = compileExpression(xpath);
281// TODO: (work in progress) - trying to integrate with Xalan
282//        Object ctxNode = getNativeContextNode(expression);
283//        if (ctxNode != null) {
284//            System.err.println("WILL USE XALAN: " + xpath);
285//            CachedXPathAPI api = new CachedXPathAPI();
286//            try {
287//                if (expression instanceof Path) {
288//                    Node node = api.selectSingleNode((Node)ctxNode, xpath);
289//                    System.err.println("NODE: " + node);
290//                    if (node == null) {
291//                        return null;
292//                    }
293//                    return new DOMNodePointer(node, null).getValue();
294//                }
295//                else {
296//                    XObject object = api.eval((Node)ctxNode, xpath);
297//                    switch (object.getType()) {
298//                    case XObject.CLASS_STRING: return object.str();
299//                    case XObject.CLASS_NUMBER: return new Double(object.num());
300//                    case XObject.CLASS_BOOLEAN: return new Boolean(object.bool());
301//                    default:
302//                        System.err.println("OTHER TYPE: " + object.getTypeString());
303//                    }
304//                }
305//            }
306//            catch (TransformerException e) {
307//                // TODO Auto-generated catch block
308//                e.printStackTrace();
309//            }
310//            return
311//        }
312
313        return getValue(xpath, expression);
314    }
315
316//    private Object getNativeContextNode(Expression expression) {
317//        Object node = getNativeContextNode(getContextBean());
318//        if (node == null) {
319//            return null;
320//        }
321//
322//        List vars = expression.getUsedVariables();
323//        if (vars != null) {
324//            return null;
325//        }
326//
327//        return node;
328//    }
329
330//    private Object getNativeContextNode(Object bean) {
331//        if (bean instanceof Number || bean instanceof String || bean instanceof Boolean) {
332//            return bean;
333//        }
334//        if (bean instanceof Node) {
335//            return (Node)bean;
336//        }
337//
338//        if (bean instanceof Container) {
339//            bean = ((Container)bean).getValue();
340//            return getNativeContextNode(bean);
341//        }
342//
343//        return null;
344//    }
345
346    /**
347     * Get the value indicated.
348     * @param xpath String
349     * @param expr Expression
350     * @return Object
351     */
352    public Object getValue(String xpath, Expression expr) {
353        Object result = expr.computeValue(getEvalContext());
354        if (result == null) {
355            if (expr instanceof Path && !isLenient()) {
356                throw new JXPathNotFoundException("No value for xpath: "
357                        + xpath);
358            }
359            return null;
360        }
361        if (result instanceof EvalContext) {
362            EvalContext ctx = (EvalContext) result;
363            result = ctx.getSingleNodePointer();
364            if (!isLenient() && result == null) {
365                throw new JXPathNotFoundException("No value for xpath: "
366                        + xpath);
367            }
368        }
369        if (result instanceof NodePointer) {
370            result = ((NodePointer) result).getValuePointer();
371            if (!isLenient() && !((NodePointer) result).isActual()) {
372                // We need to differentiate between pointers representing
373                // a non-existing property and ones representing a property
374                // whose value is null.  In the latter case, the pointer
375                // is going to have isActual == false, but its parent,
376                // which is a non-node pointer identifying the bean property,
377                // will return isActual() == true.
378                NodePointer parent =
379                    ((NodePointer) result).getImmediateParentPointer();
380                if (parent == null
381                    || !parent.isContainer()
382                    || !parent.isActual()) {
383                    throw new JXPathNotFoundException("No value for xpath: "
384                            + xpath);
385                }
386            }
387            result = ((NodePointer) result).getValue();
388        }
389        return result;
390    }
391
392    /**
393     * Calls getValue(xpath), converts the result to the required type
394     * and returns the result of the conversion.
395     * @param xpath expression
396     * @param requiredType Class
397     * @return Object
398     */
399    public Object getValue(String xpath, Class requiredType) {
400        Expression expr = compileExpression(xpath);
401        return getValue(xpath, expr, requiredType);
402    }
403
404    /**
405     * Get the value indicated.
406     * @param xpath expression
407     * @param expr compiled Expression
408     * @param requiredType Class
409     * @return Object
410     */
411    public Object getValue(String xpath, Expression expr, Class requiredType) {
412        Object value = getValue(xpath, expr);
413        if (value != null && requiredType != null) {
414            if (!TypeUtils.canConvert(value, requiredType)) {
415                throw new JXPathTypeConversionException(
416                    "Invalid expression type. '"
417                        + xpath
418                        + "' returns "
419                        + value.getClass().getName()
420                        + ". It cannot be converted to "
421                        + requiredType.getName());
422            }
423            value = TypeUtils.convert(value, requiredType);
424        }
425        return value;
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     * @return Iterator
434     */
435    public Iterator iterate(String xpath) {
436        return iterate(xpath, compileExpression(xpath));
437    }
438
439    /**
440     * Traverses the xpath and returns a Iterator of all results found
441     * for the path. If the xpath matches no properties
442     * in the graph, the Iterator will not be null.
443     * @param xpath expression
444     * @param expr compiled Expression
445     * @return Iterator
446     */
447    public Iterator iterate(String xpath, Expression expr) {
448        return expr.iterate(getEvalContext());
449    }
450
451    public Pointer getPointer(String xpath) {
452        return getPointer(xpath, compileExpression(xpath));
453    }
454
455    /**
456     * Get a pointer to the specified path/expression.
457     * @param xpath String
458     * @param expr compiled Expression
459     * @return Pointer
460     */
461    public Pointer getPointer(String xpath, Expression expr) {
462        Object result = expr.computeValue(getEvalContext());
463        if (result instanceof EvalContext) {
464            result = ((EvalContext) result).getSingleNodePointer();
465        }
466        if (result instanceof Pointer) {
467            if (!isLenient() && !((NodePointer) result).isActual()) {
468                throw new JXPathNotFoundException("No pointer for xpath: "
469                        + xpath);
470            }
471            return (Pointer) result;
472        }
473        return NodePointer.newNodePointer(null, result, getLocale());
474    }
475
476    public void setValue(String xpath, Object value) {
477        setValue(xpath, compileExpression(xpath), value);
478    }
479
480    /**
481     * Set the value of xpath to value.
482     * @param xpath path
483     * @param expr compiled Expression
484     * @param value Object
485     */
486    public void setValue(String xpath, Expression expr, Object value) {
487        try {
488            setValue(xpath, expr, value, false);
489        }
490        catch (Throwable ex) {
491            throw new JXPathException(
492                "Exception trying to set value with xpath " + xpath, ex);
493        }
494    }
495
496    public Pointer createPath(String xpath) {
497        return createPath(xpath, compileExpression(xpath));
498    }
499
500    /**
501     * Create the given path.
502     * @param xpath String
503     * @param expr compiled Expression
504     * @return resulting Pointer
505     */
506    public Pointer createPath(String xpath, Expression expr) {
507        try {
508            Object result = expr.computeValue(getEvalContext());
509            Pointer pointer = null;
510
511            if (result instanceof Pointer) {
512                pointer = (Pointer) result;
513            }
514            else if (result instanceof EvalContext) {
515                EvalContext ctx = (EvalContext) result;
516                pointer = ctx.getSingleNodePointer();
517            }
518            else {
519                checkSimplePath(expr);
520                // This should never happen
521                throw new JXPathException("Cannot create path:" + xpath);
522            }
523            return ((NodePointer) pointer).createPath(this);
524        }
525        catch (Throwable ex) {
526            throw new JXPathException(
527                "Exception trying to create xpath " + xpath,
528                ex);
529        }
530    }
531
532    public Pointer createPathAndSetValue(String xpath, Object value) {
533        return createPathAndSetValue(xpath, compileExpression(xpath), value);
534    }
535
536    /**
537     * Create the given path setting its value to value.
538     * @param xpath String
539     * @param expr compiled Expression
540     * @param value Object
541     * @return resulting Pointer
542     */
543    public Pointer createPathAndSetValue(String xpath, Expression expr,
544            Object value) {
545        try {
546            return setValue(xpath, expr, value, true);
547        }
548        catch (Throwable ex) {
549            throw new JXPathException(
550                "Exception trying to create xpath " + xpath,
551                ex);
552        }
553    }
554
555    /**
556     * Set the specified value.
557     * @param xpath path
558     * @param expr compiled Expression
559     * @param value destination value
560     * @param create whether to create missing node(s)
561     * @return Pointer created
562     */
563    private Pointer setValue(String xpath, Expression expr, Object value,
564            boolean create) {
565        Object result = expr.computeValue(getEvalContext());
566        Pointer pointer = null;
567
568        if (result instanceof Pointer) {
569            pointer = (Pointer) result;
570        }
571        else if (result instanceof EvalContext) {
572            EvalContext ctx = (EvalContext) result;
573            pointer = ctx.getSingleNodePointer();
574        }
575        else {
576            if (create) {
577                checkSimplePath(expr);
578            }
579
580            // This should never happen
581            throw new JXPathException("Cannot set value for xpath: " + xpath);
582        }
583        if (create) {
584            pointer = ((NodePointer) pointer).createPath(this, value);
585        }
586        else {
587            pointer.setValue(value);
588        }
589        return pointer;
590    }
591
592    /**
593     * Checks if the path follows the JXPath restrictions on the type
594     * of path that can be passed to create... methods.
595     * @param expr Expression to check
596     */
597    private void checkSimplePath(Expression expr) {
598        if (!(expr instanceof LocationPath)
599            || !((LocationPath) expr).isSimplePath()) {
600            throw new JXPathInvalidSyntaxException(
601                "JXPath can only create a path if it uses exclusively "
602                    + "the child:: and attribute:: axes and has "
603                    + "no context-dependent predicates");
604        }
605    }
606
607    /**
608     * Traverses the xpath and returns an Iterator of Pointers.
609     * A Pointer provides easy access to a property.
610     * If the xpath matches no properties
611     * in the graph, the Iterator be empty, but not null.
612     * @param xpath expression
613     * @return Iterator
614     */
615    public Iterator iteratePointers(String xpath) {
616        return iteratePointers(xpath, compileExpression(xpath));
617    }
618
619    /**
620     * Traverses the xpath and returns an Iterator of Pointers.
621     * A Pointer provides easy access to a property.
622     * If the xpath matches no properties
623     * in the graph, the Iterator be empty, but not null.
624     * @param xpath expression
625     * @param expr compiled Expression
626     * @return Iterator
627     */
628    public Iterator iteratePointers(String xpath, Expression expr) {
629        return expr.iteratePointers(getEvalContext());
630    }
631
632    public void removePath(String xpath) {
633        removePath(xpath, compileExpression(xpath));
634    }
635
636    /**
637     * Remove the specified path.
638     * @param xpath expression
639     * @param expr compiled Expression
640     */
641    public void removePath(String xpath, Expression expr) {
642        try {
643            NodePointer pointer = (NodePointer) getPointer(xpath, expr);
644            if (pointer != null) {
645                ((NodePointer) pointer).remove();
646            }
647        }
648        catch (Throwable ex) {
649            throw new JXPathException(
650                "Exception trying to remove xpath " + xpath,
651                ex);
652        }
653    }
654
655    public void removeAll(String xpath) {
656        removeAll(xpath, compileExpression(xpath));
657    }
658
659    /**
660     * Remove all matching nodes.
661     * @param xpath expression
662     * @param expr compiled Expression
663     */
664    public void removeAll(String xpath, Expression expr) {
665        try {
666            ArrayList list = new ArrayList();
667            Iterator it = expr.iteratePointers(getEvalContext());
668            while (it.hasNext()) {
669                list.add(it.next());
670            }
671            Collections.sort(list, ReverseComparator.INSTANCE);
672            it = list.iterator();
673            if (it.hasNext()) {
674                NodePointer pointer = (NodePointer) it.next();
675                pointer.remove();
676                while (it.hasNext()) {
677                    removePath(((NodePointer) it.next()).asPath());
678                }
679            }
680        }
681        catch (Throwable ex) {
682            throw new JXPathException(
683                "Exception trying to remove all for xpath " + xpath,
684                ex);
685        }
686    }
687
688    public JXPathContext getRelativeContext(Pointer pointer) {
689        Object contextBean = pointer.getNode();
690        if (contextBean == null) {
691            throw new JXPathException(
692                "Cannot create a relative context for a non-existent node: "
693                    + pointer);
694        }
695        return new JXPathContextReferenceImpl(this, contextBean, pointer);
696    }
697
698    public Pointer getContextPointer() {
699        return contextPointer;
700    }
701
702    /**
703     * Get absolute root pointer.
704     * @return NodePointer
705     */
706    private NodePointer getAbsoluteRootPointer() {
707        return (NodePointer) rootPointer;
708    }
709
710    /**
711     * Get the evaluation context.
712     * @return EvalContext
713     */
714    private EvalContext getEvalContext() {
715        return new InitialContext(new RootContext(this,
716                (NodePointer) getContextPointer()));
717    }
718
719    /**
720     * Get the absolute root context.
721     * @return EvalContext
722     */
723    public EvalContext getAbsoluteRootContext() {
724        return new InitialContext(new RootContext(this,
725                getAbsoluteRootPointer()));
726    }
727
728    /**
729     * Get a VariablePointer for the given variable name.
730     * @param name variable name
731     * @return NodePointer
732     */
733    public NodePointer getVariablePointer(QName name) {
734        return NodePointer.newNodePointer(name, VariablePointerFactory
735                .contextWrapper(this), getLocale());
736    }
737
738    /**
739     * Get the named Function.
740     * @param functionName name
741     * @param parameters function args
742     * @return Function
743     */
744    public Function getFunction(QName functionName, Object[] parameters) {
745        String namespace = functionName.getPrefix();
746        String name = functionName.getName();
747        JXPathContext funcCtx = this;
748        Function func = null;
749        Functions funcs;
750        while (funcCtx != null) {
751            funcs = funcCtx.getFunctions();
752            if (funcs != null) {
753                func = funcs.getFunction(namespace, name, parameters);
754                if (func != null) {
755                    return func;
756                }
757            }
758            funcCtx = funcCtx.getParentContext();
759        }
760        throw new JXPathFunctionNotFoundException(
761            "Undefined function: " + functionName.toString());
762    }
763
764    public void registerNamespace(String prefix, String namespaceURI) {
765        if (namespaceResolver.isSealed()) {
766            namespaceResolver = (NamespaceResolver) namespaceResolver.clone();
767        }
768        namespaceResolver.registerNamespace(prefix, namespaceURI);
769    }
770
771    public String getNamespaceURI(String prefix) {
772        return namespaceResolver.getNamespaceURI(prefix);
773    }
774
775    /**
776     * {@inheritDoc}
777     * @see org.apache.commons.jxpath.JXPathContext#getPrefix(java.lang.String)
778     */
779    public String getPrefix(String namespaceURI) {
780        return namespaceResolver.getPrefix(namespaceURI);
781    }
782
783    public void setNamespaceContextPointer(Pointer pointer) {
784        if (namespaceResolver.isSealed()) {
785            namespaceResolver = (NamespaceResolver) namespaceResolver.clone();
786        }
787        namespaceResolver.setNamespaceContextPointer((NodePointer) pointer);
788    }
789
790    public Pointer getNamespaceContextPointer() {
791        return namespaceResolver.getNamespaceContextPointer();
792    }
793
794    /**
795     * Get the namespace resolver.
796     * @return NamespaceResolver
797     */
798    public NamespaceResolver getNamespaceResolver() {
799        namespaceResolver.seal();
800        return namespaceResolver;
801    }
802
803    /**
804     * Checks if existenceCheckClass exists on the class path. If so, allocates
805     * an instance of the specified class, otherwise returns null.
806     * @param className to instantiate
807     * @param existenceCheckClassName guard class
808     * @return className instance
809     */
810    public static Object allocateConditionally(String className,
811            String existenceCheckClassName) {
812        try {
813            try {
814                Class.forName(existenceCheckClassName);
815            }
816            catch (ClassNotFoundException ex) {
817                return null;
818            }
819            Class cls = Class.forName(className);
820            return cls.newInstance();
821        }
822        catch (Exception ex) {
823            throw new JXPathException("Cannot allocate " + className, ex);
824        }
825    }
826}