View Javadoc

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