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