    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;
    018
    019    import java.text.DecimalFormatSymbols;
    020    import java.util.ArrayList;
    021    import java.util.HashMap;
    022    import java.util.Iterator;
    023    import java.util.List;
    024    import java.util.Locale;
    025
    026    import org.apache.commons.jxpath.util.KeyManagerUtils;
    027
    028    /**
    029     * JXPathContext  provides APIs for the traversal of graphs of JavaBeans using
    030     * the XPath syntax. Using JXPathContext, you can read and write properties of
    031     * JavaBeans, arrays, collections and maps. JXPathContext uses JavaBeans
    032     * introspection to enumerate and access JavaBeans properties.
    033     * <p>
    034     * JXPathContext  allows alternative implementations. This is why instead of
    035     * allocating JXPathContext directly, you should call a static
    036     * <code>newContext</code> method.  This method will utilize the
    037     * {@link JXPathContextFactory} API to locate a suitable implementation of
    038     * JXPath. Bundled with JXPath comes a default implementation called Reference
    039     * Implementation.
    040     * </p>
    041     *
    042     * <h2>JXPath Interprets XPath Syntax on Java Object Graphs</h2>
    043     *
    044     * JXPath uses an intuitive interpretation of the xpath syntax in the context
    045     * of Java object graphs. Here are some examples:
    046     *
    047     * <h3>Example 1: JavaBean Property Access</h3>
    048     *
    049     * JXPath can be used to access properties of a JavaBean.
    050     *
    051     * <pre><blockquote>
    052     * public class Employee {
    053     *    public String getFirstName(){
    054     *       ...
    055     *    }
    056     * }
    057     *
    058     * Employee emp = new Employee();
    059     * ...
    060     *
    061     * JXPathContext context = JXPathContext.newContext(emp);
    062     * String fName = (String)context.getValue("firstName");
    063     * </blockquote></pre>
    064     *
    065     * In  this example, we are using JXPath to access a property of the
    066     * <code>emp</code> bean. In this simple case the invocation of JXPath is
    067     * equivalent to invocation of getFirstName() on the bean.
    068     *
    069     * <h3>Example 2: Nested Bean Property Access</h3>
    070     * JXPath can traverse object graphs:
    071     *
    072     * <pre><blockquote>
    073     * public class Employee {
    074     *    public Address getHomeAddress(){
    075     *       ...
    076     *    }
    077     * }
    078     * public class Address {
    079     *    public String getStreetNumber(){
    080     *       ...
    081     *    }
    082     * }
    083     *
    084     * Employee emp = new Employee();
    085     * ...
    086     *
    087     * JXPathContext context = JXPathContext.newContext(emp);
    088     * String sNumber = (String)context.getValue("homeAddress/streetNumber");
    089     * </blockquote></pre>
    090     *
    091     * In this case XPath is used to access a property of a nested bean.
    092     * <p>
    093     * A property identified by the xpath does not have to be a "leaf" property.
    094     * For instance, we can extract the whole Address object in above example:
    095     *
    096     * <pre><blockquote>
    097     *    Address addr = (Address)context.getValue("homeAddress");
    098     * </blockquote></pre>
    099     * </p>
    100     *
    101     * <h3>Example 3: Collection Subscripts</h3>
    102     * JXPath can extract elements from arrays and collections.
    103     *
    104     * <pre><blockquote>
    105     * public class Integers {
    106     *    public int[] getNumbers(){
    107     *       ...
    108     *    }
    109     * }
    110     *
    111     * Integers ints = new Integers();
    112     * ...
    113     *
    114     * JXPathContext context = JXPathContext.newContext(ints);
    115     * Integer thirdInt = (Integer)context.getValue("numbers[3]");
    116     * </blockquote></pre>
    117     * A  collection can be an arbitrary array or an instance of java.util.
    118     * Collection.
    119     * <p>
    120     * Note: in XPath the first element of a collection has index 1, not 0.<br>
    121     *
    122     * <h3>Example 4: Map Element Access</h3>
    123     *
    124     * JXPath supports maps. To get a value use its key.
    125     *
    126     * <pre><blockquote>
    127     * public class Employee {
    128     *    public Map getAddresses(){
    129     *       return addressMap;
    130     *    }
    131     *
    132     *    public void addAddress(String key, Address address){
    133     *       addressMap.put(key, address);
    134     *    }
    135     *    ...
    136     * }
    137     *
    138     * Employee emp = new Employee();
    139     * emp.addAddress("home", new Address(...));
    140     * emp.addAddress("office", new Address(...));
    141     * ...
    142     *
    143     * JXPathContext context = JXPathContext.newContext(emp);
    144     * String homeZipCode = (String)context.getValue("addresses/home/zipCode");
    145     * </blockquote></pre>
    146     *
    147     * Often you will need to use the alternative syntax for accessing Map
    148     * elements:
    149     *
    150     * <pre><blockquote>
    151     * String homeZipCode =
    152     *     (String) context.getValue("addresses[@name='home']/zipCode");
    153     * </blockquote></pre>
    154     *
    155     * In this case, the key can be an expression, e.g. a variable.<br>
    156     *
    157     * Note: At this point JXPath only supports Maps that use strings for keys.<br>
    158     * Note: JXPath supports the extended notion of Map: any object with
    159     *       dynamic properties can be handled by JXPath provided that its
    160     *       class is registered with the {@link JXPathIntrospector}.
    161     *
    162     * <h3>Example 5: Retrieving Multiple Results</h3>
    163     *
    164     * JXPath can retrieve multiple objects from a graph. Note that the method
    165     * called in this case is not <code>getValue</code>, but <code>iterate</code>.
    166     *
    167     * <pre><blockquote>
    168     * public class Author {
    169     *    public Book[] getBooks(){
    170     *       ...
    171     *    }
    172     * }
    173     *
    174     * Author auth = new Author();
    175     * ...
    176     *
    177     * JXPathContext context = JXPathContext.newContext(auth);
    178     * Iterator threeBooks = context.iterate("books[position() &lt; 4]");
    179     * </blockquote></pre>
    180     *
    181     * This returns a list of at most three books from the array of all books
    182     * written by the author.
    183     *
    184     * <h3>Example 6: Setting Properties</h3>
    185     * JXPath can be used to modify property values.
    186     *
    187     * <pre><blockquote>
    188     * public class Employee {
    189     *    public Address getAddress() {
    190     *       ...
    191     *    }
    192     *
    193     *    public void setAddress(Address address) {
    194     *       ...
    195     *    }
    196     * }
    197     *
    198     * Employee emp = new Employee();
    199     * Address addr = new Address();
    200     * ...
    201     *
    202     * JXPathContext context = JXPathContext.newContext(emp);
    203     * context.setValue("address", addr);
    204     * context.setValue("address/zipCode", "90190");
    205     *
    206     * </blockquote></pre>
    207     *
    208     * <h3>Example 7: Creating objects</h3>
    209     * JXPath  can be used to create new objects. First, create a subclass of {@link
    210     * AbstractFactory AbstractFactory} and install it on the JXPathContext. Then
    211     * call {@link JXPathContext#createPath createPathAndSetValue()} instead of
    212     * "setValue". JXPathContext will invoke your AbstractFactory when it discovers
    213     * that an intermediate node of the path is <b>null</b>.  It will not override
    214     * existing nodes.
    215     *
    216     * <pre><blockquote>
    217     * public class AddressFactory extends AbstractFactory {
    218     *    public boolean createObject(JXPathContext context,
    219     *               Pointer pointer, Object parent, String name, int index){
    220     *     if ((parent instanceof Employee) &amp;&amp; name.equals("address"){
    221     *       ((Employee)parent).setAddress(new Address());
    222     *       return true;
    223     *     }
    224     *     return false;
    225     *   }
    226     * }
    227     *
    228     * JXPathContext context = JXPathContext.newContext(emp);
    229     * context.setFactory(new AddressFactory());
    230     * context.createPathAndSetValue("address/zipCode", "90190");
    231     * </blockquote></pre>
    232     *
    233     * <h3>Example 8: Using Variables</h3>
    234     * JXPath supports the notion of variables. The XPath syntax for accessing
    235     * variables is <i>"$varName"</i>.
    236     *
    237     * <pre><blockquote>
    238     * public class Author {
    239     *    public Book[] getBooks(){
    240     *       ...
    241     *    }
    242     * }
    243     *
    244     * Author auth = new Author();
    245     * ...
    246     *
    247     * JXPathContext context = JXPathContext.newContext(auth);
    248     * context.getVariables().declareVariable("index", new Integer(2));
    249     *
    250     * Book secondBook = (Book)context.getValue("books[$index]");
    251     * </blockquote></pre>
    252     *
    253     * You can also set variables using JXPath:
    254     *
    255     * <pre><blockquote>
    256     * context.setValue("$index", new Integer(3));
    257     * </blockquote></pre>
    258     *
    259     * Note: you can only <i>change</i> the value of an existing variable this
    260     * way, you cannot <i>define</i> a new variable.
    261     *
    262     * <p>
    263     * When a variable contains a JavaBean or a collection, you can
    264     * traverse the bean or collection as well:
    265     * <pre><blockquote>
    266     * ...
    267     * context.getVariables().declareVariable("book", myBook);
    268     * String title = (String)context.getValue("$book/title);
    269     *
    270     * Book array[] = new Book[]{...};
    271     *
    272     * context.getVariables().declareVariable("books", array);
    273     *
    274     * String title = (String)context.getValue("$books[2]/title);
    275     * </blockquote></pre>
    276     *
    277     * <h3>Example 9: Using Nested Contexts</h3>
    278     * If  you need to use the same set of variable while interpreting XPaths with
    279     * different beans, it makes sense to put the variables in a separate context
    280     * and specify that context as a parent context every time you allocate a new
    281     * JXPathContext for a JavaBean.
    282     *
    283     * <pre><blockquote>
    284     * JXPathContext varContext = JXPathContext.newContext(null);
    285     * varContext.getVariables().declareVariable("title", "Java");
    286     *
    287     * JXPathContext context = JXPathContext.newContext(varContext, auth);
    288     *
    289     * Iterator javaBooks = context.iterate("books[title = $title]");
    290     * </blockquote></pre>
    291     *
    292     * <h3>Using Custom Variable Pools</h3>
    293     * By default, JXPathContext creates a HashMap of variables. However,
    294     * you can substitute a custom implementation of the Variables
    295     * interface to make JXPath work with an alternative source of variables.
    296     * For example, you can define implementations of Variables that
    297     * cover a servlet context, HTTP request or any similar structure.
    298     *
    299     * <h3>Example 10: Using Standard Extension Functions</h3>
    300     * Using the standard extension functions, you can call methods on objects,
    301     * static methods on classes and create objects using any constructor.
    302     * The class names should be fully qualified.
    303     * <p>
    304     * Here's how you can create new objects:
    305     * <pre><blockquote>
    306     * Book book =
    307     *    (Book) context.getValue(
    308     *         "org.apache.commons.jxpath.example.Book.new ('John Updike')");
    309     * </blockquote></pre>
    310     *
    311     * Here's how you can call static methods:
    312     * <pre><blockquote>
    313     *   Book book =
    314     *    (Book) context.getValue(
    315     *       "org. apache.commons.jxpath.example.Book.getBestBook('John Updike')");
    316     * </blockquote></pre>
    317     *
    318     * Here's how you can call regular methods:
    319     * <pre><blockquote>
    320     * String firstName = (String)context.getValue("getAuthorsFirstName($book)");
    321     * </blockquote></pre>
    322     * As you can see, the target of the method is specified as the first parameter
    323     * of the function.
    324     *
    325     * <h3>Example 11: Using Custom Extension Functions</h3>
    326     * Collections of custom extension functions can be implemented
    327     * as {@link Functions Functions} objects or as Java classes, whose methods
    328     * become extenstion functions.
    329     * <p>
    330     * Let's say the following class implements various formatting operations:
    331     * <pre><blockquote>
    332     * public class Formats {
    333     *    public static String date(Date d, String pattern){
    334     *        return new SimpleDateFormat(pattern).format(d);
    335     *    }
    336     *    ...
    337     * }
    338     * </blockquote></pre>
    339     *
    340     * We can register this class with a JXPathContext:
    341     *
    342     * <pre><blockquote>
    343     * context.setFunctions(new ClassFunctions(Formats.class, "format"));
    344     * ...
    345     *
    346     * context.getVariables().declareVariable("today", new Date());
    347     * String today = (String)context.getValue("format:date($today, 'MM/dd/yyyy')");
    348     *
    349     * </blockquote></pre>
    350     * You can also register whole packages of Java classes using PackageFunctions.
    351     * <p>
    352     * Also, see {@link FunctionLibrary FunctionLibrary}, which is a class
    353     * that allows you to register multiple sets of extension functions with
    354     * the same JXPathContext.
    355     *
    356     * <h2>Configuring JXPath</h2>
    357     *
    358     * JXPath uses JavaBeans introspection to discover properties of JavaBeans.
    359     * You can provide alternative property lists by supplying
    360     * custom JXPathBeanInfo classes (see {@link JXPathBeanInfo JXPathBeanInfo}).
    361     *
    362     * <h2>Notes</h2>
    363     * <ul>
    364     * <li> JXPath does not support DOM attributes for non-DOM objects. Even though
    365     * XPaths like "para[@type='warning']" are legitimate, they will always produce
    366     * empty results. The only attribute supported for JavaBeans is "name".  The
    367     * XPath "foo/bar" is equivalent to "foo[@name='bar']".
    368     * </ul>
    369     *
    370     * See  <a href="http://www.w3schools.com/xpath">XPath Tutorial by
    371     * W3Schools</a><br>. Also see <a href="http://www.w3.org/TR/xpath">XML Path
    372     * Language (XPath) Version 1.0</a><br><br>
    373     *
    374     * You will also find more information and examples in
    375     * <a href="http://commons.apache.org/jxpath/users-guide.html">
    376     * JXPath User's Guide</a>
    377     *
    378     *
    379     * @author Dmitri Plotnikov
    380     * @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
    381     */
    382    public abstract class JXPathContext {
    383        private static JXPathContextFactory contextFactory;
    384        private static JXPathContext compilationContext;
    385
    386        private static final PackageFunctions GENERIC_FUNCTIONS =
    387            new PackageFunctions("", null);
    388
    389        /** parent context */
    390        protected JXPathContext parentContext;
    391        /** context bean */
    392        protected Object contextBean;
    393        /**  variables */
    394        protected Variables vars;
    395        /** functions */
    396        protected Functions functions;
    397        /** AbstractFactory */
    398        protected AbstractFactory factory;
    399        /** IdentityManager */
    400        protected IdentityManager idManager;
    401        /** KeyManager */
    402        protected KeyManager keyManager;
    403        /** decimal format map */
    404        protected HashMap decimalFormats;
    405
    406        private Locale locale;
    407        private boolean lenientSet = false;
    408        private boolean lenient = false;
    409
    410        /**
    411         * Creates a new JXPathContext with the specified object as the root node.
    412         * @param contextBean Object
    413         * @return JXPathContext
    414         */
    415        public static JXPathContext newContext(Object contextBean) {
    416            return getContextFactory().newContext(null, contextBean);
    417        }
    418
    419        /**
    420         * Creates a new JXPathContext with the specified bean as the root node and
    421         * the specified parent context. Variables defined in a parent context can
    422         * be referenced in XPaths passed to the child context.
    423         * @param parentContext parent context
    424         * @param contextBean Object
    425         * @return JXPathContext
    426         */
    427        public static JXPathContext newContext(
    428                JXPathContext parentContext,
    429                Object contextBean) {
    430            return getContextFactory().newContext(parentContext, contextBean);
    431        }
    432
    433        /**
    434         * Acquires a context factory and caches it.
    435         * @return JXPathContextFactory
    436         */
    437        private static JXPathContextFactory getContextFactory () {
    438            if (contextFactory == null) {
    439                contextFactory = JXPathContextFactory.newInstance();
    440            }
    441            return contextFactory;
    442        }
    443
    444        /**
    445         * This constructor should remain protected - it is to be overridden by
    446         * subclasses, but never explicitly invoked by clients.
    447         * @param parentContext parent context
    448         * @param contextBean Object
    449         */
    450        protected JXPathContext(JXPathContext parentContext, Object contextBean) {
    451            this.parentContext = parentContext;
    452            this.contextBean = contextBean;
    453        }
    454
    455        /**
    456         * Returns the parent context of this context or null.
    457         * @return JXPathContext
    458         */
    459        public JXPathContext getParentContext() {
    460            return parentContext;
    461        }
    462
    463        /**
    464         * Returns the JavaBean associated with this context.
    465         * @return Object
    466         */
    467        public Object getContextBean() {
    468            return contextBean;
    469        }
    470
    471        /**
    472         * Returns a Pointer for the context bean.
    473         * @return Pointer
    474         */
    475        public abstract Pointer getContextPointer();
    476
    477        /**
    478         * Returns a JXPathContext that is relative to the current JXPathContext.
    479         * The supplied pointer becomes the context pointer of the new context.
    480         * The relative context inherits variables, extension functions, locale etc
    481         * from the parent context.
    482         * @param pointer Pointer
    483         * @return JXPathContext
    484         */
    485        public abstract JXPathContext getRelativeContext(Pointer pointer);
    486
    487        /**
    488         * Installs a custom implementation of the Variables interface.
    489         * @param vars Variables
    490         */
    491        public void setVariables(Variables vars) {
    492            this.vars = vars;
    493        }
    494
    495        /**
    496         * Returns the variable pool associated with the context. If no such
    497         * pool was specified with the {@link #setVariables} method,
    498         * returns the default implementation of Variables,
    499         * {@link BasicVariables BasicVariables}.
    500         * @return Variables
    501         */
    502        public Variables getVariables() {
    503            if (vars == null) {
    504                vars = new BasicVariables();
    505            }
    506            return vars;
    507        }
    508
    509        /**
    510         * Install a library of extension functions.
    511         * @param functions Functions
    512         * @see FunctionLibrary
    513         */
    514        public void setFunctions(Functions functions) {
    515            this.functions = functions;
    516        }
    517
    518        /**
    519         * Returns the set of functions installed on the context.
    520         * @return Functions
    521         */
    522        public Functions getFunctions() {
    523            if (functions != null) {
    524                return functions;
    525            }
    526            if (parentContext == null) {
    527                return GENERIC_FUNCTIONS;
    528            }
    529            return null;
    530        }
    531
    532        /**
    533         * Install an abstract factory that should be used by the
    534         * <code>createPath()</code> and <code>createPathAndSetValue()</code>
    535         * methods.
    536         * @param factory AbstractFactory
    537         */
    538        public void setFactory(AbstractFactory factory) {
    539            this.factory = factory;
    540        }
    541
    542        /**
    543         * Returns the AbstractFactory installed on this context.
    544         * If none has been installed and this context has a parent context,
    545         * returns the parent's factory.  Otherwise returns null.
    546         * @return AbstractFactory
    547         */
    548        public AbstractFactory getFactory() {
    549            if (factory == null && parentContext != null) {
    550                return parentContext.getFactory();
    551            }
    552            return factory;
    553        }
    554
    555        /**
    556         * Set the locale for this context.  The value of the "lang"
    557         * attribute as well as the the lang() function will be
    558         * affected by the locale.  By default, JXPath uses
    559         * <code>Locale.getDefault()</code>
    560         * @param locale Locale
    561         */
    562        public synchronized void setLocale(Locale locale) {
    563            this.locale = locale;
    564        }
    565
    566        /**
    567         * Returns the locale set with setLocale. If none was set and
    568         * the context has a parent, returns the parent's locale.
    569         * Otherwise, returns Locale.getDefault().
    570         * @return Locale
    571         */
    572        public synchronized Locale getLocale() {
    573            if (locale == null) {
    574                if (parentContext != null) {
    575                    return parentContext.getLocale();
    576                }
    577                locale = Locale.getDefault();
    578            }
    579            return locale;
    580        }
    581
    582        /**
    583         * Sets {@link DecimalFormatSymbols} for a given name. The DecimalFormatSymbols
    584         * can be referenced as the third, optional argument in the invocation of
    585         * <code>format-number (number,format,decimal-format-name)</code> function.
    586         * By default, JXPath uses the symbols for the current locale.
    587         *
    588         * @param name the format name or null for default format.
    589         * @param symbols DecimalFormatSymbols
    590         */
    591        public synchronized void setDecimalFormatSymbols(String name,
    592                DecimalFormatSymbols symbols) {
    593            if (decimalFormats == null) {
    594                decimalFormats = new HashMap();
    595            }
    596            decimalFormats.put(name, symbols);
    597        }
    598
    599        /**
    600         * Get the named DecimalFormatSymbols.
    601         * @param name key
    602         * @return DecimalFormatSymbols
    603         * @see #setDecimalFormatSymbols(String, DecimalFormatSymbols)
    604         */
    605        public synchronized DecimalFormatSymbols getDecimalFormatSymbols(String name) {
    606            if (decimalFormats == null) {
    607                return parentContext == null ? null : parentContext.getDecimalFormatSymbols(name);
    608            }
    609            return (DecimalFormatSymbols) decimalFormats.get(name);
    610        }
    611
    612        /**
    613         * If the context is in the lenient mode, then getValue() returns null
    614         * for inexistent paths.  Otherwise, a path that does not map to
    615         * an existing property will throw an exception.  Note that if the
    616         * property exists, but its value is null, the exception is <i>not</i>
    617         * thrown.
    618         * <p>
    619         * By default, lenient = false
    620         * @param lenient flag
    621         */
    622        public synchronized void setLenient(boolean lenient) {
    623            this.lenient = lenient;
    624            lenientSet = true;
    625        }
    626
    627        /**
    628         * Learn whether this JXPathContext is lenient.
    629         * @return boolean
    630         * @see #setLenient(boolean)
    631         */
    632        public synchronized boolean isLenient() {
    633            if (!lenientSet && parentContext != null) {
    634                return parentContext.isLenient();
    635            }
    636            return lenient;
    637        }
    638
    639        /**
    640         * Compiles the supplied XPath and returns an internal representation
    641         * of the path that can then be evaluated.  Use CompiledExpressions
    642         * when you need to evaluate the same expression multiple times
    643         * and there is a convenient place to cache CompiledExpression
    644         * between invocations.
    645         * @param xpath to compile
    646         * @return CompiledExpression
    647         */
    648        public static CompiledExpression compile(String xpath) {
    649            if (compilationContext == null) {
    650                compilationContext = JXPathContext.newContext(null);
    651            }
    652            return compilationContext.compilePath(xpath);
    653        }
    654
    655        /**
    656         * Overridden by each concrete implementation of JXPathContext
    657         * to perform compilation. Is called by <code>compile()</code>.
    658         * @param xpath to compile
    659         * @return CompiledExpression
    660         */
    661        protected abstract CompiledExpression compilePath(String xpath);
    662
    663        /**
    664         * Finds the first object that matches the specified XPath. It is equivalent
    665         * to <code>getPointer(xpath).getNode()</code>. Note that this method
    666         * produces the same result as <code>getValue()</code> on object models
    667         * like JavaBeans, but a different result for DOM/JDOM etc., because it
    668         * returns the Node itself, rather than its textual contents.
    669         *
    670         * @param xpath the xpath to be evaluated
    671         * @return the found object
    672         */
    673        public Object selectSingleNode(String xpath) {
    674            Pointer pointer = getPointer(xpath);
    675            return pointer == null ? null : pointer.getNode();
    676        }
    677
    678        /**
    679         * Finds all nodes that match the specified XPath.
    680         *
    681         * @param xpath the xpath to be evaluated
    682         * @return a list of found objects
    683         */
    684        public List selectNodes(String xpath) {
    685            ArrayList list = new ArrayList();
    686            Iterator iterator = iteratePointers(xpath);
    687            while (iterator.hasNext()) {
    688                Pointer pointer = (Pointer) iterator.next();
    689                list.add(pointer.getNode());
    690            }
    691            return list;
    692        }
    693
    694        /**
    695         * Evaluates the xpath and returns the resulting object. Primitive
    696         * types are wrapped into objects.
    697         * @param xpath to evaluate
    698         * @return Object found
    699         */
    700        public abstract Object getValue(String xpath);
    701
    702        /**
    703         * Evaluates the xpath, converts the result to the specified class and
    704         * returns the resulting object.
    705         * @param xpath to evaluate
    706         * @param requiredType required type
    707         * @return Object found
    708         */
    709        public abstract Object getValue(String xpath, Class requiredType);
    710
    711        /**
    712         * Modifies the value of the property described by the supplied xpath.
    713         * Will throw an exception if one of the following conditions occurs:
    714         * <ul>
    715         * <li>The xpath does not in fact describe an existing property
    716         * <li>The property is not writable (no public, non-static set method)
    717         * </ul>
    718         * @param xpath indicating position
    719         * @param value to set
    720         */
    721        public abstract void setValue(String xpath, Object value);
    722
    723        /**
    724         * Creates missing elements of the path by invoking an {@link AbstractFactory},
    725         * which should first be installed on the context by calling {@link #setFactory}.
    726         * <p>
    727         * Will throw an exception if the AbstractFactory fails to create
    728         * an instance for a path element.
    729         * @param xpath indicating destination to create
    730         * @return pointer to new location
    731         */
    732        public abstract Pointer createPath(String xpath);
    733
    734        /**
    735         * The same as setValue, except it creates intermediate elements of
    736         * the path by invoking an {@link AbstractFactory}, which should first be
    737         * installed on the context by calling {@link #setFactory}.
    738         * <p>
    739         * Will throw an exception if one of the following conditions occurs:
    740         * <ul>
    741         * <li>Elements of the xpath aleady exist, but the path does not in
    742         *  fact describe an existing property
    743         * <li>The AbstractFactory fails to create an instance for an intermediate
    744         * element.
    745         * <li>The property is not writable (no public, non-static set method)
    746         * </ul>
    747         * @param xpath indicating position to create
    748         * @param value to set
    749         * @return pointer to new location
    750         */
    751        public abstract Pointer createPathAndSetValue(String xpath, Object value);
    752
    753        /**
    754         * Removes the element of the object graph described by the xpath.
    755         * @param xpath indicating position to remove
    756         */
    757        public abstract void removePath(String xpath);
    758
    759        /**
    760         * Removes all elements of the object graph described by the xpath.
    761         * @param xpath indicating positions to remove
    762         */
    763        public abstract void removeAll(String xpath);
    764
    765        /**
    766         * Traverses the xpath and returns an Iterator of all results found
    767         * for the path. If the xpath matches no properties
    768         * in the graph, the Iterator will be empty, but not null.
    769         * @param xpath to iterate
    770         * @return Iterator<Object>
    771         */
    772        public abstract Iterator iterate(String xpath);
    773
    774        /**
    775         * Traverses the xpath and returns a Pointer.
    776         * A Pointer provides easy access to a property.
    777         * If the xpath matches no properties
    778         * in the graph, the pointer will be null.
    779         * @param xpath desired
    780         * @return Pointer
    781         */
    782        public abstract Pointer getPointer(String xpath);
    783
    784        /**
    785         * Traverses the xpath and returns an Iterator of Pointers.
    786         * A Pointer provides easy access to a property.
    787         * If the xpath matches no properties
    788         * in the graph, the Iterator be empty, but not null.
    789         * @param xpath to iterate
    790         * @return Iterator<Pointer>
    791         */
    792        public abstract Iterator iteratePointers(String xpath);
    793
    794        /**
    795         * Install an identity manager that will be used by the context
    796         * to look up a node by its ID.
    797         * @param idManager IdentityManager to set
    798         */
    799        public void setIdentityManager(IdentityManager idManager) {
    800            this.idManager = idManager;
    801        }
    802
    803        /**
    804         * Returns this context's identity manager. If none has been installed,
    805         * returns the identity manager of the parent context.
    806         * @return IdentityManager
    807         */
    808        public IdentityManager getIdentityManager() {
    809            if (idManager == null && parentContext != null) {
    810                return parentContext.getIdentityManager();
    811            }
    812            return idManager;
    813        }
    814
    815        /**
    816         * Locates a Node by its ID.
    817         *
    818         * @param id is the ID of the sought node.
    819         * @return Pointer
    820         */
    821        public Pointer getPointerByID(String id) {
    822            IdentityManager manager = getIdentityManager();
    823            if (manager != null) {
    824                return manager.getPointerByID(this, id);
    825            }
    826            throw new JXPathException(
    827                "Cannot find an element by ID - "
    828                    + "no IdentityManager has been specified");
    829        }
    830
    831        /**
    832         * Install a key manager that will be used by the context
    833         * to look up a node by a key value.
    834         * @param keyManager KeyManager
    835         */
    836        public void setKeyManager(KeyManager keyManager) {
    837            this.keyManager = keyManager;
    838        }
    839
    840        /**
    841         * Returns this context's key manager. If none has been installed,
    842         * returns the key manager of the parent context.
    843         * @return KeyManager
    844         */
    845        public KeyManager getKeyManager() {
    846            if (keyManager == null && parentContext != null) {
    847                return parentContext.getKeyManager();
    848            }
    849            return keyManager;
    850        }
    851
    852        /**
    853         * Locates a Node by a key value.
    854         * @param key string
    855         * @param value string
    856         * @return Pointer found
    857         */
    858        public Pointer getPointerByKey(String key, String value) {
    859            KeyManager manager = getKeyManager();
    860            if (manager != null) {
    861                return manager.getPointerByKey(this, key, value);
    862            }
    863            throw new JXPathException(
    864                "Cannot find an element by key - "
    865                    + "no KeyManager has been specified");
    866        }
    867
    868        /**
    869         * Locates a NodeSet by key/value.
    870         * @param key string
    871         * @param value object
    872         * @return NodeSet found
    873         */
    874        public NodeSet getNodeSetByKey(String key, Object value) {
    875            KeyManager manager = getKeyManager();
    876            if (manager != null) {
    877                return KeyManagerUtils.getExtendedKeyManager(manager)
    878                        .getNodeSetByKey(this, key, value);
    879            }
    880            throw new JXPathException("Cannot find an element by key - "
    881                    + "no KeyManager has been specified");
    882        }
    883
    884        /**
    885         * Registers a namespace prefix.
    886         *
    887         * @param prefix A namespace prefix
    888         * @param namespaceURI A URI for that prefix
    889         */
    890        public void registerNamespace(String prefix, String namespaceURI) {
    891            throw new UnsupportedOperationException(
    892                    "Namespace registration is not implemented by " + getClass());
    893        }
    894
    895        /**
    896         * Given a prefix, returns a registered namespace URI. If the requested
    897         * prefix was not defined explicitly using the registerNamespace method,
    898         * JXPathContext will then check the context node to see if the prefix is
    899         * defined there. See
    900         * {@link #setNamespaceContextPointer(Pointer) setNamespaceContextPointer}.
    901         *
    902         * @param prefix The namespace prefix to look up
    903         * @return namespace URI or null if the prefix is undefined.
    904         */
    905        public String getNamespaceURI(String prefix) {
    906            throw new UnsupportedOperationException(
    907                    "Namespace registration is not implemented by " + getClass());
    908        }
    909
    910        /**
    911         * Get the prefix associated with the specifed namespace URI.
    912         * @param namespaceURI the ns URI to check.
    913         * @return String prefix
    914         * @since JXPath 1.3
    915         */
    916        public String getPrefix(String namespaceURI) {
    917            throw new UnsupportedOperationException(
    918                    "Namespace registration is not implemented by " + getClass());
    919        }
    920
    921        /**
    922         * Namespace prefixes can be defined implicitly by specifying a pointer to a
    923         * context where the namespaces are defined. By default,
    924         * NamespaceContextPointer is the same as the Context Pointer, see
    925         * {@link #getContextPointer() getContextPointer()}
    926         *
    927         * @param namespaceContextPointer The pointer to the context where prefixes used in
    928         *        XPath expressions should be resolved.
    929         */
    930        public void setNamespaceContextPointer(Pointer namespaceContextPointer) {
    931            throw new UnsupportedOperationException(
    932                    "Namespace registration is not implemented by " + getClass());
    933        }
    934
    935        /**
    936         * Returns the namespace context pointer set with
    937         * {@link #setNamespaceContextPointer(Pointer) setNamespaceContextPointer()}
    938         * or, if none has been specified, the context pointer otherwise.
    939         *
    940         * @return The namespace context pointer.
    941         */
    942        public Pointer getNamespaceContextPointer() {
    943            throw new UnsupportedOperationException(
    944                    "Namespace registration is not implemented by " + getClass());
    945        }
    946
    947    }