001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jxpath;
017:
018: import java.text.DecimalFormatSymbols;
019: import java.util.ArrayList;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Locale;
024:
025: /**
026: * JXPathContext provides APIs for the traversal of graphs of JavaBeans using
027: * the XPath syntax. Using JXPathContext, you can read and write properties of
028: * JavaBeans, arrays, collections and maps. JXPathContext uses JavaBeans
029: * introspection to enumerate and access JavaBeans properties.
030: * <p>
031: * JXPathContext allows alternative implementations. This is why instead of
032: * allocating JXPathContext directly, you should call a static
033: * <code>newContext</code> method. This method will utilize the
034: * JXPathContextFactory API to locate a suitable implementation of JXPath.
035: * Bundled with JXPath comes a default implementation called Reference
036: * Implementation.
037: * </p>
038: *
039: * <h2>JXPath Interprets XPath Syntax on Java Object Graphs</h2>
040: *
041: * JXPath uses an intuitive interpretation of the xpath syntax in the context
042: * of Java object graphs. Here are some examples:
043: *
044: * <h3>Example 1: JavaBean Property Access</h3>
045: *
046: * JXPath can be used to access properties of a JavaBean.
047: *
048: * <pre><blockquote>
049: * public class Employee {
050: * public String getFirstName(){
051: * ...
052: * }
053: * }
054: *
055: * Employee emp = new Employee();
056: * ...
057: *
058: * JXPathContext context = JXPathContext.newContext(emp);
059: * String fName = (String)context.getValue("firstName");
060: * </blockquote></pre>
061: *
062: * In this example, we are using JXPath to access a property of the
063: * <code>emp</code> bean. In this simple case the invocation of JXPath is
064: * equivalent to invocation of getFirstName() on the bean.
065: *
066: * <h3>Example 2: Nested Bean Property Access</h3>
067: * JXPath can traverse object graphs:
068: *
069: * <pre><blockquote>
070: * public class Employee {
071: * public Address getHomeAddress(){
072: * ...
073: * }
074: * }
075: * public class Address {
076: * public String getStreetNumber(){
077: * ...
078: * }
079: * }
080: *
081: * Employee emp = new Employee();
082: * ...
083: *
084: * JXPathContext context = JXPathContext.newContext(emp);
085: * String sNumber = (String)context.getValue("homeAddress/streetNumber");
086: * </blockquote></pre>
087: *
088: * In this case XPath is used to access a property of a nested bean.
089: * <p>
090: * A property identified by the xpath does not have to be a "leaf" property.
091: * For instance, we can extract the whole Address object in above example:
092: *
093: * <pre><blockquote>
094: * Address addr = (Address)context.getValue("homeAddress");
095: * </blockquote></pre>
096: * </p>
097: *
098: * <h3>Example 3: Collection Subscripts</h3>
099: * 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 = new PackageFunctions(
396: "", 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(JXPathContext parentContext,
411: Object contextBean) {
412: return getContextFactory().newContext(parentContext,
413: contextBean);
414: }
415:
416: /**
417: * Acquires a context factory and caches it.
418: */
419: private static JXPathContextFactory getContextFactory() {
420: if (contextFactory == null) {
421: contextFactory = JXPathContextFactory.newInstance();
422: }
423: return contextFactory;
424: }
425:
426: /**
427: * This constructor should remain protected - it is to be overridden by
428: * subclasses, but never explicitly invoked by clients.
429: */
430: protected JXPathContext(JXPathContext parentContext,
431: 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: } else {
546: locale = Locale.getDefault();
547: }
548: }
549: return locale;
550: }
551:
552: /**
553: * Sets DecimalFormatSymbols for a given name. The DecimalFormatSymbols can
554: * be referenced as the third, optional argument in the invocation of
555: * <code>format-number (number,format,decimal-format-name)</code> function.
556: * By default, JXPath uses the symbols for the current locale.
557: *
558: * @param name the format name or null for default format.
559: */
560: public void setDecimalFormatSymbols(String name,
561: DecimalFormatSymbols symbols) {
562: if (decimalFormats == null) {
563: decimalFormats = new HashMap();
564: }
565: decimalFormats.put(name, symbols);
566: }
567:
568: /**
569: * @see #setDecimalFormatSymbols(String, DecimalFormatSymbols)
570: */
571: public DecimalFormatSymbols getDecimalFormatSymbols(String name) {
572: if (decimalFormats == null) {
573: if (parentContext != null) {
574: return parentContext.getDecimalFormatSymbols(name);
575: }
576: return null;
577: }
578: return (DecimalFormatSymbols) decimalFormats.get(name);
579: }
580:
581: /**
582: * If the context is in the lenient mode, then getValue() returns null
583: * for inexistent paths. Otherwise, a path that does not map to
584: * an existing property will throw an exception. Note that if the
585: * property exists, but its value is null, the exception is <i>not</i>
586: * thrown.
587: * <p>
588: * By default, lenient = false
589: */
590: public void setLenient(boolean lenient) {
591: this .lenient = lenient;
592: lenientSet = true;
593: }
594:
595: /**
596: * @see #setLenient(boolean)
597: */
598: public boolean isLenient() {
599: if (!lenientSet && parentContext != null) {
600: return parentContext.isLenient();
601: }
602: return lenient;
603: }
604:
605: /**
606: * Compiles the supplied XPath and returns an internal representation
607: * of the path that can then be evaluated. Use CompiledExpressions
608: * when you need to evaluate the same expression multiple times
609: * and there is a convenient place to cache CompiledExpression
610: * between invocations.
611: */
612: public static CompiledExpression compile(String xpath) {
613: if (compilationContext == null) {
614: compilationContext = JXPathContext.newContext(null);
615: }
616: return compilationContext.compilePath(xpath);
617: }
618:
619: /**
620: * Overridden by each concrete implementation of JXPathContext
621: * to perform compilation. Is called by <code>compile()</code>.
622: */
623: protected abstract CompiledExpression compilePath(String xpath);
624:
625: /**
626: * Finds the first object that matches the specified XPath. It is equivalent
627: * to <code>getPointer(xpath).getNode()</code>. Note, that this method
628: * produces the same result as <code>getValue()</code> on object models
629: * like JavaBeans, but a different result for DOM/JDOM etc., because it
630: * returns the Node itself, rather than its textual contents.
631: *
632: * @param xpath the xpath to be evaluated
633: * @return the found object
634: */
635: public Object selectSingleNode(String xpath) {
636: Pointer pointer = getPointer(xpath);
637: if (pointer == null) {
638: return null;
639: }
640: return pointer.getNode();
641: }
642:
643: /**
644: * Finds all nodes that match the specified XPath.
645: *
646: * @param xpath the xpath to be evaluated
647: * @return a list of found objects
648: */
649: public List selectNodes(String xpath) {
650: ArrayList list = new ArrayList();
651: Iterator iterator = iteratePointers(xpath);
652: while (iterator.hasNext()) {
653: Pointer pointer = (Pointer) iterator.next();
654: list.add(pointer.getNode());
655: }
656: return list;
657: }
658:
659: /**
660: * Evaluates the xpath and returns the resulting object. Primitive
661: * types are wrapped into objects.
662: */
663: public abstract Object getValue(String xpath);
664:
665: /**
666: * Evaluates the xpath, converts the result to the specified class and
667: * returns the resulting object.
668: */
669: public abstract Object getValue(String xpath, Class requiredType);
670:
671: /**
672: * Modifies the value of the property described by the supplied xpath.
673: * Will throw an exception if one of the following conditions occurs:
674: * <ul>
675: * <li>The xpath does not in fact describe an existing property
676: * <li>The property is not writable (no public, non-static set method)
677: * </ul>
678: */
679: public abstract void setValue(String xpath, Object value);
680:
681: /**
682: * Creates missing elements of the path by invoking an AbstractFactory,
683: * which should first be installed on the context by calling "setFactory".
684: * <p>
685: * Will throw an exception if the AbstractFactory fails to create
686: * an instance for a path element.
687: */
688: public abstract Pointer createPath(String xpath);
689:
690: /**
691: * The same as setValue, except it creates intermediate elements of
692: * the path by invoking an AbstractFactory, which should first be
693: * installed on the context by calling "setFactory".
694: * <p>
695: * Will throw an exception if one of the following conditions occurs:
696: * <ul>
697: * <li>Elements of the xpath aleady exist, but the path does not in
698: * fact describe an existing property
699: * <li>The AbstractFactory fails to create an instance for an intermediate
700: * element.
701: * <li>The property is not writable (no public, non-static set method)
702: * </ul>
703: */
704: public abstract Pointer createPathAndSetValue(String xpath,
705: Object value);
706:
707: /**
708: * Removes the element of the object graph described by the xpath.
709: */
710: public abstract void removePath(String xpath);
711:
712: /**
713: * Removes all elements of the object graph described by the xpath.
714: */
715: public abstract void removeAll(String xpath);
716:
717: /**
718: * Traverses the xpath and returns an Iterator of all results found
719: * for the path. If the xpath matches no properties
720: * in the graph, the Iterator will be empty, but not null.
721: */
722: public abstract Iterator iterate(String xpath);
723:
724: /**
725: * Traverses the xpath and returns a Pointer.
726: * A Pointer provides easy access to a property.
727: * If the xpath matches no properties
728: * in the graph, the pointer will be null.
729: */
730: public abstract Pointer getPointer(String xpath);
731:
732: /**
733: * Traverses the xpath and returns an Iterator of Pointers.
734: * A Pointer provides easy access to a property.
735: * If the xpath matches no properties
736: * in the graph, the Iterator be empty, but not null.
737: */
738: public abstract Iterator iteratePointers(String xpath);
739:
740: /**
741: * Install an identity manager that will be used by the context
742: * to look up a node by its ID.
743: */
744: public void setIdentityManager(IdentityManager idManager) {
745: this .idManager = idManager;
746: }
747:
748: /**
749: * Returns this context's identity manager. If none has been installed,
750: * returns the identity manager of the parent context.
751: */
752: public IdentityManager getIdentityManager() {
753: if (idManager == null && parentContext != null) {
754: return parentContext.getIdentityManager();
755: }
756: return idManager;
757: }
758:
759: /**
760: * Locates a Node by its ID.
761: *
762: * @param id is the ID of the sought node.
763: */
764: public Pointer getPointerByID(String id) {
765: IdentityManager manager = getIdentityManager();
766: if (manager != null) {
767: return manager.getPointerByID(this , id);
768: } else {
769: throw new JXPathException("Cannot find an element by ID - "
770: + "no IdentityManager has been specified");
771: }
772: }
773:
774: /**
775: * Install a key manager that will be used by the context
776: * to look up a node by a key value.
777: */
778: public void setKeyManager(KeyManager keyManager) {
779: this .keyManager = keyManager;
780: }
781:
782: /**
783: * Returns this context's key manager. If none has been installed,
784: * returns the key manager of the parent context.
785: */
786: public KeyManager getKeyManager() {
787: if (keyManager == null && parentContext != null) {
788: return parentContext.getKeyManager();
789: }
790: return keyManager;
791: }
792:
793: /**
794: * Locates a Node by a key value.
795: */
796: public Pointer getPointerByKey(String key, String value) {
797: KeyManager manager = getKeyManager();
798: if (manager != null) {
799: return manager.getPointerByKey(this , key, value);
800: } else {
801: throw new JXPathException(
802: "Cannot find an element by key - "
803: + "no KeyManager has been specified");
804: }
805: }
806:
807: /**
808: * Registers a namespace prefix.
809: *
810: * @param prefix A namespace prefix
811: * @param namespaceURI A URI for that prefix
812: */
813: public void registerNamespace(String prefix, String namespaceURI) {
814: throw new UnsupportedOperationException(
815: "Namespace registration is not implemented by "
816: + getClass());
817: }
818:
819: /**
820: * Given a prefix, returns a registered namespace URI. If the requested
821: * prefix was not defined explicitly using the registerNamespace method,
822: * JXPathContext will then check the context node to see if the prefix is
823: * defined there. See
824: * {@link #setNamespaceContextPointer(Pointer) setNamespaceContextPointer}.
825: *
826: * @param prefix The namespace prefix to look up
827: * @return namespace URI or null if the prefix is undefined.
828: */
829: public String getNamespaceURI(String prefix) {
830: throw new UnsupportedOperationException(
831: "Namespace registration is not implemented by "
832: + getClass());
833: }
834:
835: /**
836: * Namespace prefixes can be defined implicitly by specifying a pointer to a
837: * context where the namespaces are defined. By default,
838: * NamespaceContextPointer is the same as the Context Pointer, see
839: * {@link #getContextPointer() getContextPointer()}
840: *
841: * @param contextPointer The pointer to the context where prefixes used in
842: * XPath expressions should be resolved.
843: */
844: public void setNamespaceContextPointer(
845: Pointer namespaceContextPointer) {
846: throw new UnsupportedOperationException(
847: "Namespace registration is not implemented by "
848: + getClass());
849: }
850:
851: /**
852: * Returns the namespace context pointer set with
853: * {@link #setNamespaceContextPointer(Pointer) setNamespaceContextPointer()}
854: * or, if none has been specified, the context pointer otherwise.
855: *
856: * @return The namespace context pointer.
857: */
858: public Pointer getNamespaceContextPointer() {
859: throw new UnsupportedOperationException(
860: "Namespace registration is not implemented by "
861: + getClass());
862: }
863: }
|