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