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() < 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) && 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 }