View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.jxpath;
19  
20  import java.text.DecimalFormatSymbols;
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Locale;
26  
27  import org.apache.commons.jxpath.util.KeyManagerUtils;
28  
29  /**
30   * JXPathContext provides APIs for the traversal of graphs of JavaBeans using the XPath syntax. Using JXPathContext, you can read and write properties of
31   * JavaBeans, arrays, collections and maps. JXPathContext uses JavaBeans introspection to enumerate and access JavaBeans properties.
32   * <p>
33   * JXPathContext allows alternative implementations. This is why instead of allocating JXPathContext directly, you should call a static {@code newContext}
34   * method. This method will utilize the {@link JXPathContextFactory} API to locate a suitable implementation of JXPath. Bundled with JXPath comes a default
35   * implementation called Reference Implementation.
36   * </p>
37   *
38   * <h2>JXPath Interprets XPath Syntax on Java Object Graphs</h2>
39   *
40   * JXPath uses an intuitive interpretation of the XPath syntax in the context of Java object graphs. Here are some examples:
41   *
42   * <h3>Example 1: JavaBean Property Access</h3>
43   *
44   * JXPath can be used to access properties of a JavaBean.
45   *
46   * <pre>
47   * public class Employee {
48   *    public String getFirstName(){
49   *       ...
50   *    }
51   * }
52   *
53   * Employee emp = new Employee();
54   * ...
55   *
56   * JXPathContext context = JXPathContext.newContext(emp);
57   * String fName = (String)context.getValue("firstName");
58   * </pre>
59   *
60   * 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
61   * of getFirstName() on the bean.
62   *
63   * <h3>Example 2: Nested Bean Property Access</h3> JXPath can traverse object graphs:
64   *
65   * <pre>
66   * public class Employee {
67   *    public Address getHomeAddress(){
68   *       ...
69   *    }
70   * }
71   * public class Address {
72   *    public String getStreetNumber(){
73   *       ...
74   *    }
75   * }
76   *
77   * Employee emp = new Employee();
78   * ...
79   *
80   * JXPathContext context = JXPathContext.newContext(emp);
81   * String sNumber = (String)context.getValue("homeAddress/streetNumber");
82   * </pre>
83   *
84   * In this case XPath is used to access a property of a nested bean.
85   * <p>
86   * 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:
87   * </p>
88   *
89   * <pre>
90   *
91   * Address addr = (Address) context.getValue("homeAddress");
92   * </pre>
93   *
94   * <h3>Example 3: Collection Subscripts</h3>
95   * <p>
96   * JXPath can extract elements from arrays and collections.
97   * </p>
98   *
99   * <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  */
407 public 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 }