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     */
017    package org.apache.commons.jxpath.ri;
018    
019    import java.lang.ref.SoftReference;
020    import java.util.ArrayList;
021    import java.util.Arrays;
022    import java.util.Collections;
023    import java.util.Comparator;
024    import java.util.HashMap;
025    import java.util.Iterator;
026    import java.util.Map;
027    import java.util.Vector;
028    import java.util.Map.Entry;
029    
030    import org.apache.commons.jxpath.CompiledExpression;
031    import org.apache.commons.jxpath.Function;
032    import org.apache.commons.jxpath.Functions;
033    import org.apache.commons.jxpath.JXPathContext;
034    import org.apache.commons.jxpath.JXPathException;
035    import org.apache.commons.jxpath.JXPathFunctionNotFoundException;
036    import org.apache.commons.jxpath.JXPathInvalidSyntaxException;
037    import org.apache.commons.jxpath.JXPathNotFoundException;
038    import org.apache.commons.jxpath.JXPathTypeConversionException;
039    import org.apache.commons.jxpath.Pointer;
040    import org.apache.commons.jxpath.ri.axes.InitialContext;
041    import org.apache.commons.jxpath.ri.axes.RootContext;
042    import org.apache.commons.jxpath.ri.compiler.Expression;
043    import org.apache.commons.jxpath.ri.compiler.LocationPath;
044    import org.apache.commons.jxpath.ri.compiler.Path;
045    import org.apache.commons.jxpath.ri.compiler.TreeCompiler;
046    import org.apache.commons.jxpath.ri.model.NodePointer;
047    import org.apache.commons.jxpath.ri.model.NodePointerFactory;
048    import org.apache.commons.jxpath.ri.model.VariablePointerFactory;
049    import org.apache.commons.jxpath.ri.model.beans.BeanPointerFactory;
050    import org.apache.commons.jxpath.ri.model.beans.CollectionPointerFactory;
051    import org.apache.commons.jxpath.ri.model.container.ContainerPointerFactory;
052    import org.apache.commons.jxpath.ri.model.dynamic.DynamicPointerFactory;
053    import org.apache.commons.jxpath.util.ReverseComparator;
054    import 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     */
062    public 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    }