    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    }