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