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 018package org.apache.commons.jxpath; 019 020import java.text.DecimalFormatSymbols; 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Locale; 026 027import org.apache.commons.jxpath.util.KeyManagerUtils; 028 029/** 030 * JXPathContext provides APIs for the traversal of graphs of JavaBeans using the XPath syntax. Using JXPathContext, you can read and write properties of 031 * JavaBeans, arrays, collections and maps. JXPathContext uses JavaBeans introspection to enumerate and access JavaBeans properties. 032 * <p> 033 * JXPathContext allows alternative implementations. This is why instead of allocating JXPathContext directly, you should call a static {@code newContext} 034 * method. This method will utilize the {@link JXPathContextFactory} API to locate a suitable implementation of JXPath. Bundled with JXPath comes a default 035 * implementation called Reference Implementation. 036 * </p> 037 * 038 * <h2>JXPath Interprets XPath Syntax on Java Object Graphs</h2> 039 * 040 * JXPath uses an intuitive interpretation of the XPath syntax in the context of Java object graphs. Here are some examples: 041 * 042 * <h3>Example 1: JavaBean Property Access</h3> 043 * 044 * JXPath can be used to access properties of a JavaBean. 045 * 046 * <pre> 047 * public class Employee { 048 * public String getFirstName(){ 049 * ... 050 * } 051 * } 052 * 053 * Employee emp = new Employee(); 054 * ... 055 * 056 * JXPathContext context = JXPathContext.newContext(emp); 057 * String fName = (String)context.getValue("firstName"); 058 * </pre> 059 * 060 * In this example, we are using JXPath to access a property of the {@code emp} bean. In this simple case the invocation of JXPath is equivalent to invocation 061 * of getFirstName() on the bean. 062 * 063 * <h3>Example 2: Nested Bean Property Access</h3> JXPath can traverse object graphs: 064 * 065 * <pre> 066 * public class Employee { 067 * public Address getHomeAddress(){ 068 * ... 069 * } 070 * } 071 * public class Address { 072 * public String getStreetNumber(){ 073 * ... 074 * } 075 * } 076 * 077 * Employee emp = new Employee(); 078 * ... 079 * 080 * JXPathContext context = JXPathContext.newContext(emp); 081 * String sNumber = (String)context.getValue("homeAddress/streetNumber"); 082 * </pre> 083 * 084 * In this case XPath is used to access a property of a nested bean. 085 * <p> 086 * A property identified by the XPath does not have to be a "leaf" property. For instance, we can extract the whole Address object in above example: 087 * </p> 088 * 089 * <pre> 090 * 091 * Address addr = (Address) context.getValue("homeAddress"); 092 * </pre> 093 * 094 * <h3>Example 3: Collection Subscripts</h3> 095 * <p> 096 * JXPath can extract elements from arrays and collections. 097 * </p> 098 * 099 * <pre> 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 * </pre> 112 * 113 * A collection can be an arbitrary array or an instance of java.util. Collection. 114 * <p> 115 * Note: in XPath the first element of a collection has index 1, not 0. 116 * </p> 117 * 118 * <h3>Example 4: Map Element Access</h3> 119 * 120 * JXPath supports maps. To get a value use its key. 121 * 122 * <pre> 123 * public class Employee { 124 * public Map getAddresses(){ 125 * return addressMap; 126 * } 127 * 128 * public void addAddress(String key, Address address){ 129 * addressMap.put(key, address); 130 * } 131 * ... 132 * } 133 * 134 * Employee emp = new Employee(); 135 * emp.addAddress("home", new Address(...)); 136 * emp.addAddress("office", new Address(...)); 137 * ... 138 * 139 * JXPathContext context = JXPathContext.newContext(emp); 140 * String homeZipCode = (String)context.getValue("addresses/home/zipCode"); 141 * </pre> 142 * 143 * <p> 144 * Often you will need to use the alternative syntax for accessing Map elements: 145 * </p> 146 * 147 * <pre> 148 * 149 * String homeZipCode = (String) context.getValue("addresses[@name='home']/zipCode"); 150 * </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 dynamic properties can be handled by JXPath provided that its class is registered with the 156 * {@link JXPathIntrospector}. 157 * 158 * <h3>Example 5: Retrieving Multiple Results</h3> 159 * 160 * JXPath can retrieve multiple objects from a graph. Note that the method called in this case is not {@code getValue}, but {@code iterate}. 161 * 162 * <pre>{@code 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 * }</pre> 175 * 176 * This returns a list of at most three books from the array of all books written by the author. 177 * 178 * <h3>Example 6: Setting Properties</h3> JXPath can be used to modify property values. 179 * 180 * <pre> 181 * public class Employee { 182 * public Address getAddress() { 183 * ... 184 * } 185 * 186 * public void setAddress(Address address) { 187 * ... 188 * } 189 * } 190 * 191 * Employee emp = new Employee(); 192 * Address addr = new Address(); 193 * ... 194 * 195 * JXPathContext context = JXPathContext.newContext(emp); 196 * context.setValue("address", addr); 197 * context.setValue("address/zipCode", "90190"); 198 * 199 * </pre> 200 * 201 * <h3>Example 7: Creating objects</h3> 202 * <p> 203 * JXPath can be used to create new objects. First, create a subclass of {@link AbstractFactory AbstractFactory} and 204 * install it on the JXPathContext. Then call {@link JXPathContext#createPath createPathAndSetValue()} instead of "setValue". JXPathContext will invoke your 205 * AbstractFactory when it discovers that an intermediate node of the path is <strong>null</strong>. It will not override existing nodes. 206 * </p> 207 * 208 * <pre> 209 * public class AddressFactory extends AbstractFactory { 210 * public boolean createObject(JXPathContext context, 211 * Pointer pointer, Object parent, String name, int index){ 212 * if ((parent instanceof Employee) && name.equals("address"){ 213 * ((Employee)parent).setAddress(new Address()); 214 * return true; 215 * } 216 * return false; 217 * } 218 * } 219 * 220 * JXPathContext context = JXPathContext.newContext(emp); 221 * context.setFactory(new AddressFactory()); 222 * context.createPathAndSetValue("address/zipCode", "90190"); 223 * </pre> 224 * 225 * <h3>Example 8: Using Variables</h3> 226 * <p> 227 * JXPath supports the notion of variables. The XPath syntax for accessing variables is <em>"$varName"</em>. 228 * </p> 229 * 230 * <pre> 231 * public class Author { 232 * public Book[] getBooks(){ 233 * ... 234 * } 235 * } 236 * 237 * Author auth = new Author(); 238 * ... 239 * 240 * JXPathContext context = JXPathContext.newContext(auth); 241 * context.getVariables().declareVariable("index", Integer.valueOf(2)); 242 * 243 * Book secondBook = (Book)context.getValue("books[$index]"); 244 * </pre> 245 * 246 * <p> 247 * You can also set variables using JXPath: 248 * </p> 249 * 250 * <pre> 251 * context.setValue("$index", Integer.valueOf(3)); 252 * </pre> 253 * 254 * <p> 255 * Note: you can only <em>change</em> the value of an existing variable this way, you cannot <em>define</em> a new variable. 256 * </p> 257 * 258 * <p> 259 * When a variable contains a JavaBean or a collection, you can traverse the bean or collection as well: 260 * </p> 261 * 262 * <pre> 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 * </pre> 273 * 274 * <h3>Example 9: Using Nested Contexts</h3> 275 * <p> 276 * If you need to use the same set of variable while interpreting XPaths with different beans, it makes sense to put 277 * the variables in a separate context and specify that context as a parent context every time you allocate a new JXPathContext for a JavaBean. 278 * </p> 279 * 280 * <pre> 281 * JXPathContext varContext = JXPathContext.newContext(null); 282 * varContext.getVariables().declareVariable("title", "Java"); 283 * JXPathContext context = JXPathContext.newContext(varContext, auth); 284 * Iterator javaBooks = context.iterate("books[title = $title]"); 285 * </pre> 286 * 287 * <h3>Using Custom Variable Pools</h3> 288 * <p> 289 * By default, JXPathContext creates a HashMap of variables. However, you can substitute a custom implementation of the 290 * Variables interface to make JXPath work with an alternative source of variables. For example, you can define implementations of Variables that cover a 291 * servlet context, HTTP request or any similar structure. 292 * </p> 293 * 294 * <h3>Example 10: Using Standard Extension Functions</h3> Using the standard extension functions, you can call methods on objects, static methods on classes 295 * and create objects using any constructor. The class names should be fully qualified. 296 * <p> 297 * Here's how you can create new objects: 298 * </p> 299 * 300 * <pre> 301 * 302 * Book book = (Book) context.getValue("org.apache.commons.jxpath.example.Book.new ('John Updike')"); 303 * </pre> 304 * 305 * <p> 306 * Here's how you can call static methods: 307 * </p> 308 * 309 * <pre> 310 * 311 * Book book = (Book) context.getValue("org. apache.commons.jxpath.example.Book.getBestBook('John Updike')"); 312 * </pre> 313 * 314 * <p> 315 * Here's how you can call regular methods: 316 * </p> 317 * 318 * <pre> 319 * 320 * String firstName = (String) context.getValue("getAuthorsFirstName($book)"); 321 * </pre> 322 * 323 * <p> 324 * As you can see, the target of the method is specified as the first parameter of the function. 325 * </p> 326 * 327 * <h3>Example 11: Using Custom Extension Functions</h3> 328 * <p> 329 * Collections of custom extension functions can be implemented as {@link Functions Functions} objects or 330 * as Java classes, whose methods become extenstion functions. 331 * </p> 332 * <p> 333 * Let's say the following class implements various formatting operations: 334 * </p> 335 * 336 * <pre> 337 * public class Formats { 338 * public static String date(Date d, String pattern){ 339 * return new SimpleDateFormat(pattern).format(d); 340 * } 341 * ... 342 * } 343 * </pre> 344 * 345 * <p> 346 * We can register this class with a JXPathContext: 347 * </p> 348 * 349 * <pre> 350 * context.setFunctions(new ClassFunctions(Formats.class, "format")); 351 * ... 352 * 353 * context.getVariables().declareVariable("today", new Date()); 354 * String today = (String)context.getValue("format:date($today, 'MM/dd/yyyy')"); 355 * 356 * </pre> 357 * 358 * <p> 359 * You can also register whole packages of Java classes using PackageFunctions. 360 * </p> 361 * <p> 362 * Also, see {@link FunctionLibrary FunctionLibrary}, which is a class that allows you to register multiple sets of extension functions with the same 363 * JXPathContext. 364 * </p> 365 * 366 * <h2>Configuring JXPath</h2> 367 * 368 * <p> 369 * JXPath uses JavaBeans introspection to discover properties of JavaBeans. You can provide alternative property lists by supplying custom JXPathBeanInfo 370 * classes (see {@link JXPathBeanInfo JXPathBeanInfo}). 371 * </p> 372 * 373 * <h2>Notes</h2> 374 * <ul> 375 * <li>JXPath does not support DOM attributes for non-DOM objects. Even though XPaths like "para[@type='warning']" are legitimate, they will always produce 376 * empty results. The only attribute supported for JavaBeans is "name". The XPath "foo/bar" is equivalent to "foo[@name='bar']".</li> 377 * 378 * <li id='matches_no_property_in_the_graph'>The term <b>matches no property in the graph</b> is used throughout the documentation. It describes a property or 379 * path that can be determined as not belonging to the graph. Determining whether a property or path belongs to the graph depends on the type of object being 380 * used as {@code cotextBean} (see {@link #newContext(Object)}). It is only possible strongly typed models where a specific Java model is used as context. It is 381 * not possible with dynamic models such Maps or DOM implementations. 382 * <p> 383 * When a XPath does not match a property in the graph, the methods of this class that retrieve a pointer will generally behave in the following way, depending 384 * on the last value configured with {@link #setLenient(boolean)}: 385 * </p> 386 * 387 * <ol style='list-style:upper-alpha'> 388 * <li>If {@code lenient} is {@code false} (default) - methods will throw {@link JXPathNotFoundException}. 389 * <li>If {@code lenient} is {@code true} - methods will throw no exception and return a value appropriate for that method to express the absence: might be a 390 * Java {@code null} or a {@link Pointer} whose {@link Pointer#getValue()} returns {@code null}, depends on the method. 391 * </ol> 392 * </li> 393 * </ul> 394 * 395 * <p> 396 * See also: 397 * </p> 398 * <ul> 399 * <li>See <a href="http://www.w3schools.com/xpath">XPath Tutorial by W3Schools</a></li> 400 * <li>See also <a href="http://www.w3.org/TR/xpath">XML Path Language (XPath) Version 1.0</a></li> 401 * </ul> 402 * 403 * <p> 404 * You will also find more information and examples in the <a href="https://commons.apache.org/proper/jxpath/apidocs/index.html">JXPath User's Guide</a> 405 * </p> 406 */ 407public abstract class JXPathContext { 408 409 private static volatile JXPathContextFactory contextFactory; 410 private static volatile JXPathContext compilationContext; 411 private static final PackageFunctions GENERIC_FUNCTIONS = new PackageFunctions("", null); 412 413 /** 414 * Compiles the supplied XPath and returns an internal representation of the path that can then be evaluated. Use CompiledExpressions when you need to 415 * evaluate the same expression multiple times and there is a convenient place to cache CompiledExpression between invocations. 416 * 417 * @param xpath to compile 418 * @return CompiledExpression 419 */ 420 public static CompiledExpression compile(final String xpath) { 421 if (compilationContext == null) { 422 compilationContext = newContext(null); 423 } 424 return compilationContext.compilePath(xpath); 425 } 426 427 /** 428 * Acquires a context factory and caches it. 429 * 430 * @return JXPathContextFactory 431 */ 432 private static JXPathContextFactory getContextFactory() { 433 if (contextFactory == null) { 434 contextFactory = JXPathContextFactory.newInstance(); 435 } 436 return contextFactory; 437 } 438 439 /** 440 * Creates a new JXPathContext with the specified bean as the root node and the specified parent context. Variables defined in a parent context can be 441 * referenced in XPaths passed to the child context. 442 * 443 * @param parentContext parent context 444 * @param contextBean Object 445 * @return JXPathContext 446 */ 447 public static JXPathContext newContext(final JXPathContext parentContext, final Object contextBean) { 448 return getContextFactory().newContext(parentContext, contextBean); 449 } 450 451 /** 452 * Creates a new JXPathContext with the specified object as the root node. 453 * 454 * @param contextBean Object 455 * @return JXPathContext 456 */ 457 public static JXPathContext newContext(final Object contextBean) { 458 return getContextFactory().newContext(null, contextBean); 459 } 460 461 /** Parent context */ 462 protected JXPathContext parentContext; 463 /** Context bean */ 464 protected Object contextBean; 465 /** Variables */ 466 protected Variables vars; 467 /** Functions */ 468 protected Functions functions; 469 /** AbstractFactory */ 470 protected AbstractFactory factory; 471 /** IdentityManager */ 472 protected IdentityManager idManager; 473 /** KeyManager */ 474 protected KeyManager keyManager; 475 /** Decimal format map */ 476 protected HashMap<String, DecimalFormatSymbols> decimalFormats; 477 private Locale locale; 478 private boolean lenientSet; 479 private boolean lenient; 480 481 /** 482 * This constructor should remain protected - it is to be overridden by subclasses, but never explicitly invoked by clients. 483 * 484 * @param parentContext parent context 485 * @param contextBean Object 486 */ 487 protected JXPathContext(final JXPathContext parentContext, final Object contextBean) { 488 this.parentContext = parentContext; 489 this.contextBean = contextBean; 490 } 491 492 /** 493 * Overridden by each concrete implementation of JXPathContext to perform compilation. Is called by {@code compile()}. 494 * 495 * @param xpath to compile 496 * @return CompiledExpression 497 */ 498 protected abstract CompiledExpression compilePath(String xpath); 499 500 /** 501 * Creates missing elements of the path by invoking an {@link AbstractFactory}, which should first be installed on the context by calling 502 * {@link #setFactory}. 503 * <p> 504 * Will throw an exception if the AbstractFactory fails to create an instance for a path element. 505 * 506 * @param xpath indicating destination to create 507 * @return pointer to new location 508 */ 509 public abstract Pointer createPath(String xpath); 510 511 /** 512 * The same as setValue, except it creates intermediate elements of the path by invoking an {@link AbstractFactory}, which should first be installed on the 513 * context by calling {@link #setFactory}. 514 * <p> 515 * Will throw an exception if one of the following conditions occurs: 516 * <ul> 517 * <li>Elements of the XPath aleady exist, but the path does not in fact describe an existing property 518 * <li>The AbstractFactory fails to create an instance for an intermediate element. 519 * <li>The property is not writable (no public, non-static set method) 520 * </ul> 521 * 522 * @param xpath indicating position to create 523 * @param value to set 524 * @return pointer to new location 525 */ 526 public abstract Pointer createPathAndSetValue(String xpath, Object value); 527 528 /** 529 * Returns the JavaBean associated with this context. 530 * 531 * @return Object 532 */ 533 public Object getContextBean() { 534 return contextBean; 535 } 536 537 /** 538 * Returns a Pointer for the context bean. 539 * 540 * @return Pointer 541 */ 542 public abstract Pointer getContextPointer(); 543 544 /** 545 * Gets the named DecimalFormatSymbols. 546 * 547 * @param name key 548 * @return DecimalFormatSymbols 549 * @see #setDecimalFormatSymbols(String, DecimalFormatSymbols) 550 */ 551 public synchronized DecimalFormatSymbols getDecimalFormatSymbols(final String name) { 552 if (decimalFormats == null) { 553 return parentContext == null ? null : parentContext.getDecimalFormatSymbols(name); 554 } 555 return decimalFormats.get(name); 556 } 557 558 /** 559 * Returns the AbstractFactory installed on this context. If none has been installed and this context has a parent context, returns the parent's factory. 560 * Otherwise returns null. 561 * 562 * @return AbstractFactory 563 */ 564 public AbstractFactory getFactory() { 565 if (factory == null && parentContext != null) { 566 return parentContext.getFactory(); 567 } 568 return factory; 569 } 570 571 /** 572 * Returns the set of functions installed on the context. 573 * 574 * @return Functions 575 */ 576 public Functions getFunctions() { 577 if (functions != null) { 578 return functions; 579 } 580 if (parentContext == null) { 581 return GENERIC_FUNCTIONS; 582 } 583 return null; 584 } 585 586 /** 587 * Returns this context's identity manager. If none has been installed, returns the identity manager of the parent context. 588 * 589 * @return IdentityManager 590 */ 591 public IdentityManager getIdentityManager() { 592 if (idManager == null && parentContext != null) { 593 return parentContext.getIdentityManager(); 594 } 595 return idManager; 596 } 597 598 /** 599 * Returns this context's key manager. If none has been installed, returns the key manager of the parent context. 600 * 601 * @return KeyManager 602 */ 603 public KeyManager getKeyManager() { 604 if (keyManager == null && parentContext != null) { 605 return parentContext.getKeyManager(); 606 } 607 return keyManager; 608 } 609 610 /** 611 * Returns the locale set with setLocale. If none was set and the context has a parent, returns the parent's locale. Otherwise, returns Locale.getDefault(). 612 * 613 * @return Locale 614 */ 615 public synchronized Locale getLocale() { 616 if (locale == null) { 617 if (parentContext != null) { 618 return parentContext.getLocale(); 619 } 620 locale = Locale.getDefault(); 621 } 622 return locale; 623 } 624 625 /** 626 * Returns the namespace context pointer set with {@link #setNamespaceContextPointer(Pointer) setNamespaceContextPointer()} or, if none has been specified, 627 * the context pointer otherwise. 628 * 629 * @return The namespace context pointer. 630 */ 631 public Pointer getNamespaceContextPointer() { 632 throw new UnsupportedOperationException("Namespace registration is not implemented by " + getClass()); 633 } 634 635 /** 636 * Given a prefix, returns a registered namespace URI. If the requested prefix was not defined explicitly using the registerNamespace method, JXPathContext 637 * will then check the context node to see if the prefix is defined there. See {@link #setNamespaceContextPointer(Pointer) setNamespaceContextPointer}. 638 * 639 * @param prefix The namespace prefix to look up 640 * @return namespace URI or null if the prefix is undefined. 641 */ 642 public String getNamespaceURI(final String prefix) { 643 throw new UnsupportedOperationException("Namespace registration is not implemented by " + getClass()); 644 } 645 646 /** 647 * Locates a NodeSet by key/value. 648 * 649 * @param key string 650 * @param value object 651 * @return NodeSet found 652 */ 653 public NodeSet getNodeSetByKey(final String key, final Object value) { 654 final KeyManager manager = getKeyManager(); 655 if (manager != null) { 656 return KeyManagerUtils.getExtendedKeyManager(manager).getNodeSetByKey(this, key, value); 657 } 658 throw new JXPathException("Cannot find an element by key - " + "no KeyManager has been specified"); 659 } 660 661 /** 662 * Returns the parent context of this context or null. 663 * 664 * @return JXPathContext 665 */ 666 public JXPathContext getParentContext() { 667 return parentContext; 668 } 669 670 /** 671 * Traverses the XPath and returns a Pointer. A Pointer provides easy access to a property. 672 * <p> 673 * If the XPath <a href='#matches_no_property_in_the_graph'>matches no properties in the graph</a> the behavior depends on the value that has been 674 * configured with {@link #setLenient(boolean)}: 675 * </p> 676 * <ul> 677 * <li>{@code false} (default) the method will throw a {@link JXPathNotFoundException}. 678 * <li>{@code true} the method returns a pointer whose {@link Pointer#getValue()} method will always return null. 679 * </ul> 680 * 681 * @param xpath desired 682 * @return Pointer A {@link Pointer}, never {@code null}. 683 * @throws JXPathNotFoundException see method description. 684 */ 685 public abstract Pointer getPointer(String xpath); 686 687 /** 688 * Locates a Node by its ID. 689 * 690 * @param id is the ID of the sought node. 691 * @return Pointer 692 */ 693 public Pointer getPointerByID(final String id) { 694 final IdentityManager manager = getIdentityManager(); 695 if (manager != null) { 696 return manager.getPointerByID(this, id); 697 } 698 throw new JXPathException("Cannot find an element by ID - " + "no IdentityManager has been specified"); 699 } 700 701 /** 702 * Locates a Node by a key value. 703 * 704 * @param key string 705 * @param value string 706 * @return Pointer found 707 */ 708 public Pointer getPointerByKey(final String key, final String value) { 709 final KeyManager manager = getKeyManager(); 710 if (manager != null) { 711 return manager.getPointerByKey(this, key, value); 712 } 713 throw new JXPathException("Cannot find an element by key - " + "no KeyManager has been specified"); 714 } 715 716 /** 717 * Gets the prefix associated with the specifed namespace URI. 718 * 719 * @param namespaceURI the ns URI to check. 720 * @return String prefix 721 * @since JXPath 1.3 722 */ 723 public String getPrefix(final String namespaceURI) { 724 throw new UnsupportedOperationException("Namespace registration is not implemented by " + getClass()); 725 } 726 727 /** 728 * Returns a JXPathContext that is relative to the current JXPathContext. The supplied pointer becomes the context pointer of the new context. The relative 729 * context inherits variables, extension functions, locale etc from the parent context. 730 * 731 * @param pointer Pointer 732 * @return JXPathContext 733 */ 734 public abstract JXPathContext getRelativeContext(Pointer pointer); 735 736 /** 737 * Evaluates the XPath and returns the resulting object. Primitive types are wrapped into objects. 738 * 739 * @param xpath to evaluate 740 * @return Object found 741 */ 742 public abstract Object getValue(String xpath); 743 744 /** 745 * Evaluates the xpath, converts the result to the specified class and returns the resulting object. 746 * 747 * @param xpath to evaluate 748 * @param requiredType required type 749 * @return Object found 750 */ 751 public abstract Object getValue(String xpath, Class requiredType); 752 753 /** 754 * Returns the variable pool associated with the context. If no such pool was specified with the {@link #setVariables} method, returns the default 755 * implementation of Variables, {@link BasicVariables BasicVariables}. 756 * 757 * @return Variables 758 */ 759 public Variables getVariables() { 760 if (vars == null) { 761 vars = new BasicVariables(); 762 } 763 return vars; 764 } 765 766 /** 767 * Tests whether this JXPathContext is lenient. 768 * 769 * @return boolean 770 * @see #setLenient(boolean) 771 */ 772 public synchronized boolean isLenient() { 773 if (!lenientSet && parentContext != null) { 774 return parentContext.isLenient(); 775 } 776 return lenient; 777 } 778 779 /** 780 * Traverses the XPath and returns an Iterator of all results found for the path. If the XPath matches no properties in the graph, the Iterator will be 781 * empty, but not null. 782 * 783 * @param <E> the type of elements returned by the iterator. 784 * @param xpath to iterate 785 * @return Iterator 786 */ 787 public abstract <E> Iterator<E> iterate(String xpath); 788 789 /** 790 * Traverses the XPath and returns an Iterator of Pointers. A Pointer provides easy access to a property. If the XPath matches no properties in the graph, 791 * the Iterator be empty, but not null. 792 * 793 * @param xpath to iterate 794 * @return Iterator 795 */ 796 public abstract Iterator<Pointer> iteratePointers(String xpath); 797 798 /** 799 * Registers a namespace prefix. 800 * 801 * @param prefix A namespace prefix 802 * @param namespaceURI A URI for that prefix 803 */ 804 public void registerNamespace(final String prefix, final String namespaceURI) { 805 throw new UnsupportedOperationException("Namespace registration is not implemented by " + getClass()); 806 } 807 808 /** 809 * Removes all elements of the object graph described by the xpath. 810 * 811 * @param xpath indicating positions to remove 812 */ 813 public abstract void removeAll(String xpath); 814 815 /** 816 * Removes the element of the object graph described by the xpath. 817 * 818 * @param xpath indicating position to remove 819 */ 820 public abstract void removePath(String xpath); 821 822 /** 823 * Finds all nodes that match the specified XPath. 824 * 825 * @param xpath the xpath to be evaluated 826 * @return a list of found objects 827 */ 828 public List selectNodes(final String xpath) { 829 final ArrayList list = new ArrayList(); 830 final Iterator<Pointer> iterator = iteratePointers(xpath); 831 while (iterator.hasNext()) { 832 final Pointer pointer = iterator.next(); 833 list.add(pointer.getNode()); 834 } 835 return list; 836 } 837 838 /** 839 * Finds the first object that matches the specified XPath. It is equivalent to {@code getPointer(xpath).getNode()}. Note that this method produces the same 840 * result as {@code getValue()} on object models like JavaBeans, but a different result for DOM/JDOM etc., because it returns the Node itself, rather than 841 * its textual contents. 842 * 843 * @param xpath the xpath to be evaluated 844 * @return the found object 845 */ 846 public Object selectSingleNode(final String xpath) { 847 final Pointer pointer = getPointer(xpath); 848 return pointer == null ? null : pointer.getNode(); 849 } 850 851 /** 852 * Sets {@link DecimalFormatSymbols} for a given name. The DecimalFormatSymbols can be referenced as the third, optional argument in the invocation of 853 * {@code format-number (number,format,decimal-format-name)} function. By default, JXPath uses the symbols for the current locale. 854 * 855 * @param name the format name or null for default format. 856 * @param symbols DecimalFormatSymbols 857 */ 858 public synchronized void setDecimalFormatSymbols(final String name, final DecimalFormatSymbols symbols) { 859 if (decimalFormats == null) { 860 decimalFormats = new HashMap<>(); 861 } 862 decimalFormats.put(name, symbols); 863 } 864 865 /** 866 * Sets the ExceptionHandler used by this context, if any. 867 * 868 * @param exceptionHandler to set 869 * @since 1.4.0 870 */ 871 public void setExceptionHandler(final ExceptionHandler exceptionHandler) { 872 throw new UnsupportedOperationException("ExceptionHandler registration is not implemented by " + getClass()); 873 } 874 875 /** 876 * Install an abstract factory that should be used by the {@code createPath()} and {@code createPathAndSetValue()} methods. 877 * 878 * @param factory AbstractFactory 879 */ 880 public void setFactory(final AbstractFactory factory) { 881 this.factory = factory; 882 } 883 884 /** 885 * Install a library of extension functions. 886 * 887 * @param functions Functions 888 * @see FunctionLibrary 889 */ 890 public void setFunctions(final Functions functions) { 891 this.functions = functions; 892 } 893 894 /** 895 * Install an identity manager that will be used by the context to look up a node by its ID. 896 * 897 * @param idManager IdentityManager to set 898 */ 899 public void setIdentityManager(final IdentityManager idManager) { 900 this.idManager = idManager; 901 } 902 903 /** 904 * Install a key manager that will be used by the context to look up a node by a key value. 905 * 906 * @param keyManager KeyManager 907 */ 908 public void setKeyManager(final KeyManager keyManager) { 909 this.keyManager = keyManager; 910 } 911 912 /** 913 * If the context is in the lenient mode, then getValue() returns null for inexistent paths. Otherwise, a path that does not map to an existing property 914 * will throw an exception. Note that if the property exists, but its value is null, the exception is <em>not</em> thrown. 915 * <p> 916 * By default, lenient = false 917 * 918 * @param lenient flag 919 */ 920 public synchronized void setLenient(final boolean lenient) { 921 this.lenient = lenient; 922 lenientSet = true; 923 } 924 925 /** 926 * Sets the locale for this context. The value of the "lang" attribute as well as the lang() function will be affected by the locale. By default, JXPath 927 * uses {@code Locale.getDefault()} 928 * 929 * @param locale Locale 930 */ 931 public synchronized void setLocale(final Locale locale) { 932 this.locale = locale; 933 } 934 935 /** 936 * Namespace prefixes can be defined implicitly by specifying a pointer to a context where the namespaces are defined. By default, NamespaceContextPointer 937 * is the same as the Context Pointer, see {@link #getContextPointer() getContextPointer()} 938 * 939 * @param namespaceContextPointer The pointer to the context where prefixes used in XPath expressions should be resolved. 940 */ 941 public void setNamespaceContextPointer(final Pointer namespaceContextPointer) { 942 throw new UnsupportedOperationException("Namespace registration is not implemented by " + getClass()); 943 } 944 945 /** 946 * Modifies the value of the property described by the supplied xpath. Will throw an exception if one of the following conditions occurs: 947 * <ul> 948 * <li>The XPath does not in fact describe an existing property 949 * <li>The property is not writable (no public, non-static set method) 950 * </ul> 951 * 952 * @param xpath indicating position 953 * @param value to set 954 */ 955 public abstract void setValue(String xpath, Object value); 956 957 /** 958 * Installs a custom implementation of the Variables interface. 959 * 960 * @param vars Variables 961 */ 962 public void setVariables(final Variables vars) { 963 this.vars = vars; 964 } 965}