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