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}