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