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 */
017package org.apache.commons.jxpath;
018
019import java.text.DecimalFormatSymbols;
020import java.util.ArrayList;
021import java.util.HashMap;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Locale;
025
026import 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: 1564371 $ $Date: 2014-02-04 17:40:10 +0100 (Di, 04 Feb 2014) $
381 */
382public 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}