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    package org.apache.commons.jxpath;
018    
019    import java.text.DecimalFormatSymbols;
020    import java.util.ArrayList;
021    import java.util.HashMap;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.Locale;
025    
026    import org.apache.commons.jxpath.util.KeyManagerUtils;
027    
028    /**
029     * JXPathContext  provides APIs for the traversal of graphs of JavaBeans using
030     * the XPath syntax. Using JXPathContext, you can read and write properties of
031     * JavaBeans, arrays, collections and maps. JXPathContext uses JavaBeans
032     * introspection to enumerate and access JavaBeans properties.
033     * <p>
034     * JXPathContext  allows alternative implementations. This is why instead of
035     * allocating JXPathContext directly, you should call a static
036     * <code>newContext</code> method.  This method will utilize the
037     * {@link JXPathContextFactory} API to locate a suitable implementation of
038     * JXPath. Bundled with JXPath comes a default implementation called Reference
039     * Implementation.
040     * </p>
041     *
042     * <h2>JXPath Interprets XPath Syntax on Java Object Graphs</h2>
043     *
044     * JXPath uses an intuitive interpretation of the xpath syntax in the context
045     * of Java object graphs. Here are some examples:
046     *
047     * <h3>Example 1: JavaBean Property Access</h3>
048     *
049     * JXPath can be used to access properties of a JavaBean.
050     *
051     * <pre><blockquote>
052     * public class Employee {
053     *    public String getFirstName(){
054     *       ...
055     *    }
056     * }
057     *
058     * Employee emp = new Employee();
059     * ...
060     *
061     * JXPathContext context = JXPathContext.newContext(emp);
062     * String fName = (String)context.getValue("firstName");
063     * </blockquote></pre>
064     *
065     * In  this example, we are using JXPath to access a property of the
066     * <code>emp</code> bean. In this simple case the invocation of JXPath is
067     * equivalent to invocation of getFirstName() on the bean.
068     *
069     * <h3>Example 2: Nested Bean Property Access</h3>
070     * JXPath can traverse object graphs:
071     *
072     * <pre><blockquote>
073     * public class Employee {
074     *    public Address getHomeAddress(){
075     *       ...
076     *    }
077     * }
078     * public class Address {
079     *    public String getStreetNumber(){
080     *       ...
081     *    }
082     * }
083     *
084     * Employee emp = new Employee();
085     * ...
086     *
087     * JXPathContext context = JXPathContext.newContext(emp);
088     * String sNumber = (String)context.getValue("homeAddress/streetNumber");
089     * </blockquote></pre>
090     *
091     * In this case XPath is used to access a property of a nested bean.
092     * <p>
093     * A property identified by the xpath does not have to be a "leaf" property.
094     * For instance, we can extract the whole Address object in above example:
095     *
096     * <pre><blockquote>
097     *    Address addr = (Address)context.getValue("homeAddress");
098     * </blockquote></pre>
099     * </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: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
381     */
382    public abstract class JXPathContext {
383        private static JXPathContextFactory contextFactory;
384        private static 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    }