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.ri; 019 020import java.lang.ref.SoftReference; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.Map; 027import java.util.Map.Entry; 028import java.util.Vector; 029 030import org.apache.commons.jxpath.CompiledExpression; 031import org.apache.commons.jxpath.ExceptionHandler; 032import org.apache.commons.jxpath.Function; 033import org.apache.commons.jxpath.Functions; 034import org.apache.commons.jxpath.JXPathContext; 035import org.apache.commons.jxpath.JXPathException; 036import org.apache.commons.jxpath.JXPathFunctionNotFoundException; 037import org.apache.commons.jxpath.JXPathInvalidSyntaxException; 038import org.apache.commons.jxpath.JXPathNotFoundException; 039import org.apache.commons.jxpath.JXPathTypeConversionException; 040import org.apache.commons.jxpath.Pointer; 041import org.apache.commons.jxpath.ri.axes.InitialContext; 042import org.apache.commons.jxpath.ri.axes.RootContext; 043import org.apache.commons.jxpath.ri.compiler.Expression; 044import org.apache.commons.jxpath.ri.compiler.LocationPath; 045import org.apache.commons.jxpath.ri.compiler.Path; 046import org.apache.commons.jxpath.ri.compiler.TreeCompiler; 047import org.apache.commons.jxpath.ri.model.NodePointer; 048import org.apache.commons.jxpath.ri.model.NodePointerFactory; 049import org.apache.commons.jxpath.ri.model.VariablePointerFactory; 050import org.apache.commons.jxpath.ri.model.beans.BeanPointerFactory; 051import org.apache.commons.jxpath.ri.model.beans.CollectionPointerFactory; 052import org.apache.commons.jxpath.ri.model.container.ContainerPointerFactory; 053import org.apache.commons.jxpath.ri.model.dynamic.DynamicPointerFactory; 054import org.apache.commons.jxpath.util.ClassLoaderUtil; 055import org.apache.commons.jxpath.util.ReverseComparator; 056import org.apache.commons.jxpath.util.TypeUtils; 057 058/** 059 * The reference implementation of JXPathContext. 060 */ 061public class JXPathContextReferenceImpl extends JXPathContext { 062 063 /** 064 * Change this to {@code false} to disable soft caching of CompiledExpressions. 065 */ 066 public static final boolean USE_SOFT_CACHE = true; 067 private static final Compiler COMPILER = new TreeCompiler(); 068 private static Map<String, Object> compiled = new HashMap<>(); 069 private static int cleanupCount; 070 private static NodePointerFactory[] nodeFactoryArray; 071 // The frequency of the cache cleanup 072 private static final int CLEANUP_THRESHOLD = 500; 073 private static final Vector<NodePointerFactory> nodeFactories = new Vector<>(); 074 static { 075 nodeFactories.add(new CollectionPointerFactory()); 076 nodeFactories.add(new BeanPointerFactory()); 077 nodeFactories.add(new DynamicPointerFactory()); 078 nodeFactories.add(new VariablePointerFactory()); 079 // DOM factory is only registered if DOM support is on the classpath 080 final NodePointerFactory domFactory = (NodePointerFactory) allocateConditionally("org.apache.commons.jxpath.ri.model.dom.DOMPointerFactory", 081 "org.w3c.dom.Node"); 082 if (domFactory != null) { 083 nodeFactories.add(domFactory); 084 } 085 // JDOM factory is only registered if JDOM is on the classpath 086 final NodePointerFactory jdomFactory = (NodePointerFactory) allocateConditionally("org.apache.commons.jxpath.ri.model.jdom.JDOMPointerFactory", 087 "org.jdom.Document"); 088 if (jdomFactory != null) { 089 nodeFactories.add(jdomFactory); 090 } 091 // DynaBean factory is only registered if BeanUtils are on the classpath 092 final NodePointerFactory dynaBeanFactory = (NodePointerFactory) allocateConditionally( 093 "org.apache.commons.jxpath.ri.model.dynabeans." + "DynaBeanPointerFactory", "org.apache.commons.beanutils.DynaBean"); 094 if (dynaBeanFactory != null) { 095 nodeFactories.add(dynaBeanFactory); 096 } 097 nodeFactories.add(new ContainerPointerFactory()); 098 createNodeFactoryArray(); 099 } 100 101 /** 102 * Call this with a custom NodePointerFactory to add support for additional types of objects. Make sure the factory returns a name that puts it in the right 103 * position on the list of factories. 104 * 105 * @param factory NodePointerFactory to add 106 */ 107 public static void addNodePointerFactory(final NodePointerFactory factory) { 108 synchronized (nodeFactories) { 109 nodeFactories.add(factory); 110 nodeFactoryArray = null; 111 } 112 } 113 114 /** 115 * Checks if existenceCheckClass exists on the class path. If so, allocates an instance of the specified class, otherwise returns null. 116 * 117 * @param className to instantiate 118 * @param existenceCheckClassName guard class 119 * @return className instance 120 */ 121 public static Object allocateConditionally(final String className, final String existenceCheckClassName) { 122 try { 123 try { 124 ClassLoaderUtil.getClass(existenceCheckClassName, true); 125 } catch (final ClassNotFoundException ex) { 126 return null; 127 } 128 return ClassLoaderUtil.getClass(className, true).getConstructor().newInstance(); 129 } catch (final Exception ex) { 130 throw new JXPathException("Cannot allocate " + className, ex); 131 } 132 } 133 134 /** 135 * Create the default node factory array. 136 */ 137 private static synchronized void createNodeFactoryArray() { 138 if (nodeFactoryArray == null) { 139 nodeFactoryArray = nodeFactories.toArray(new NodePointerFactory[nodeFactories.size()]); 140 Arrays.sort(nodeFactoryArray, (a, b) -> { 141 final int orderA = a.getOrder(); 142 final int orderB = b.getOrder(); 143 return orderA - orderB; 144 }); 145 } 146 } 147 148 /** 149 * Gets the registered NodePointerFactories. 150 * 151 * @return NodePointerFactory[] 152 */ 153 public static NodePointerFactory[] getNodePointerFactories() { 154 return nodeFactoryArray; 155 } 156 157 /** 158 * Removes support for additional types of objects. 159 * 160 * @param factory NodePointerFactory to remove 161 * @return true if this implementation contained the specified element 162 * @since 1.4.0.0 163 */ 164 public static boolean removeNodePointerFactory(final NodePointerFactory factory) { 165 synchronized (nodeFactories) { 166 final boolean remove = nodeFactories.remove(factory); 167 nodeFactoryArray = null; 168 return remove; 169 } 170 } 171 172 /** Namespace resolver */ 173 protected NamespaceResolver namespaceResolver; 174 private Pointer rootPointer; 175 private Pointer contextPointer; 176 177 /** 178 * Constructs a new JXPathContextReferenceImpl. 179 * 180 * @param parentContext parent context 181 * @param contextBean Object 182 */ 183 protected JXPathContextReferenceImpl(final JXPathContext parentContext, final Object contextBean) { 184 this(parentContext, contextBean, null); 185 } 186 187 /** 188 * Constructs a new JXPathContextReferenceImpl. 189 * 190 * @param parentContext parent context 191 * @param contextBean Object 192 * @param contextPointer context pointer 193 */ 194 public JXPathContextReferenceImpl(final JXPathContext parentContext, final Object contextBean, final Pointer contextPointer) { 195 super(parentContext, contextBean); 196 synchronized (nodeFactories) { 197 createNodeFactoryArray(); 198 } 199 if (contextPointer != null) { 200 this.contextPointer = contextPointer; 201 this.rootPointer = NodePointer.newNodePointer(new QName(null, "root"), contextPointer.getRootNode(), getLocale()); 202 } else { 203 this.contextPointer = NodePointer.newNodePointer(new QName(null, "root"), contextBean, getLocale()); 204 this.rootPointer = this.contextPointer; 205 } 206 NamespaceResolver parentNR = null; 207 if (parentContext instanceof JXPathContextReferenceImpl) { 208 parentNR = ((JXPathContextReferenceImpl) parentContext).getNamespaceResolver(); 209 } 210 namespaceResolver = new NamespaceResolver(parentNR); 211 namespaceResolver.setNamespaceContextPointer((NodePointer) this.contextPointer); 212 } 213 214 /** 215 * Checks if the path follows the JXPath restrictions on the type of path that can be passed to create... methods. 216 * 217 * @param expr Expression to check 218 */ 219 private void checkSimplePath(final Expression expr) { 220 if (!(expr instanceof LocationPath) || !((LocationPath) expr).isSimplePath()) { 221 throw new JXPathInvalidSyntaxException( 222 "JXPath can only create a path if it uses exclusively " + "the child:: and attribute:: axes and has " + "no context-dependent predicates"); 223 } 224 } 225 226 /** 227 * Compile the given expression. 228 * 229 * @param xpath to compile 230 * @return Expression 231 */ 232 private Expression compileExpression(final String xpath) { 233 Expression expr; 234 synchronized (compiled) { 235 if (USE_SOFT_CACHE) { 236 expr = null; 237 final SoftReference<Expression> ref = (SoftReference) compiled.get(xpath); 238 if (ref != null) { 239 expr = ref.get(); 240 } 241 } else { 242 expr = (Expression) compiled.get(xpath); 243 } 244 } 245 if (expr != null) { 246 return expr; 247 } 248 expr = (Expression) Parser.parseExpression(xpath, getCompiler()); 249 synchronized (compiled) { 250 if (USE_SOFT_CACHE) { 251 if (cleanupCount++ >= CLEANUP_THRESHOLD) { 252 final Iterator<Entry<String, Object>> it = compiled.entrySet().iterator(); 253 while (it.hasNext()) { 254 final Entry<String, ?> me = it.next(); 255 if (((SoftReference<Expression>) me.getValue()).get() == null) { 256 it.remove(); 257 } 258 } 259 cleanupCount = 0; 260 } 261 compiled.put(xpath, new SoftReference<>(expr)); 262 } else { 263 compiled.put(xpath, expr); 264 } 265 } 266 return expr; 267 } 268 269 @Override 270 protected CompiledExpression compilePath(final String xpath) { 271 return new JXPathCompiledExpression(xpath, compileExpression(xpath)); 272 } 273// private Object getNativeContextNode(Expression expression) { 274// Object node = getNativeContextNode(getContextBean()); 275// if (node == null) { 276// return null; 277// } 278// 279// List vars = expression.getUsedVariables(); 280// if (vars != null) { 281// return null; 282// } 283// 284// return node; 285// } 286// private Object getNativeContextNode(Object bean) { 287// if (bean instanceof Number || bean instanceof String || bean instanceof Boolean) { 288// return bean; 289// } 290// if (bean instanceof Node) { 291// return (Node)bean; 292// } 293// 294// if (bean instanceof Container) { 295// bean = ((Container)bean).getValue(); 296// return getNativeContextNode(bean); 297// } 298// 299// return null; 300// } 301 302 @Override 303 public Pointer createPath(final String xpath) { 304 return createPath(xpath, compileExpression(xpath)); 305 } 306 307 /** 308 * Create the given path. 309 * 310 * @param xpath String 311 * @param expr compiled Expression 312 * @return resulting Pointer 313 */ 314 public Pointer createPath(final String xpath, final Expression expr) { 315 try { 316 final Object result = expr.computeValue(getEvalContext()); 317 Pointer pointer; 318 if (result instanceof Pointer) { 319 pointer = (Pointer) result; 320 } else if (result instanceof EvalContext) { 321 final EvalContext ctx = (EvalContext) result; 322 pointer = ctx.getSingleNodePointer(); 323 } else { 324 checkSimplePath(expr); 325 // This should never happen 326 throw new JXPathException("Cannot create path:" + xpath); 327 } 328 return ((NodePointer) pointer).createPath(this); 329 } catch (final Throwable ex) { 330 throw new JXPathException("Exception trying to create XPath " + xpath, ex); 331 } 332 } 333 334 /** 335 * Create the given path setting its value to value. 336 * 337 * @param xpath String 338 * @param expr compiled Expression 339 * @param value Object 340 * @return resulting Pointer 341 */ 342 public Pointer createPathAndSetValue(final String xpath, final Expression expr, final Object value) { 343 try { 344 return setValue(xpath, expr, value, true); 345 } catch (final Throwable ex) { 346 throw new JXPathException("Exception trying to create XPath " + xpath, ex); 347 } 348 } 349 350 @Override 351 public Pointer createPathAndSetValue(final String xpath, final Object value) { 352 return createPathAndSetValue(xpath, compileExpression(xpath), value); 353 } 354 355 /** 356 * Gets the absolute root context. 357 * 358 * @return EvalContext 359 */ 360 public EvalContext getAbsoluteRootContext() { 361 return new InitialContext(new RootContext(this, getAbsoluteRootPointer())); 362 } 363 364 /** 365 * Gets absolute root pointer. 366 * 367 * @return NodePointer 368 */ 369 private NodePointer getAbsoluteRootPointer() { 370 return (NodePointer) rootPointer; 371 } 372 373 /** 374 * Returns a static instance of TreeCompiler. 375 * 376 * Override this to return an alternate compiler. 377 * 378 * @return Compiler 379 */ 380 protected Compiler getCompiler() { 381 return COMPILER; 382 } 383 384 @Override 385 public Pointer getContextPointer() { 386 return contextPointer; 387 } 388 389 /** 390 * Gets the evaluation context. 391 * 392 * @return EvalContext 393 */ 394 private EvalContext getEvalContext() { 395 return new InitialContext(new RootContext(this, (NodePointer) getContextPointer())); 396 } 397 398 /** 399 * Gets the named Function. 400 * 401 * @param functionName name 402 * @param parameters function args 403 * @return Function 404 */ 405 public Function getFunction(final QName functionName, final Object[] parameters) { 406 final String namespace = functionName.getPrefix(); 407 final String name = functionName.getName(); 408 JXPathContext funcCtx = this; 409 Function func; 410 Functions funcs; 411 while (funcCtx != null) { 412 funcs = funcCtx.getFunctions(); 413 if (funcs != null) { 414 func = funcs.getFunction(namespace, name, parameters); 415 if (func != null) { 416 return func; 417 } 418 } 419 funcCtx = funcCtx.getParentContext(); 420 } 421 throw new JXPathFunctionNotFoundException("Undefined function: " + functionName.toString()); 422 } 423 424 @Override 425 public Pointer getNamespaceContextPointer() { 426 return namespaceResolver.getNamespaceContextPointer(); 427 } 428 429 /** 430 * Gets the namespace resolver. 431 * 432 * @return NamespaceResolver 433 */ 434 public NamespaceResolver getNamespaceResolver() { 435 namespaceResolver.seal(); 436 return namespaceResolver; 437 } 438 439 @Override 440 public String getNamespaceURI(final String prefix) { 441 return namespaceResolver.getNamespaceURI(prefix); 442 } 443 444 @Override 445 public Pointer getPointer(final String xpath) { 446 return getPointer(xpath, compileExpression(xpath)); 447 } 448 449 /** 450 * Gets a pointer to the specified path/expression. 451 * 452 * @param xpath String 453 * @param expr compiled Expression 454 * @return Pointer 455 */ 456 public Pointer getPointer(final String xpath, final Expression expr) { 457 Object result = expr.computeValue(getEvalContext()); 458 if (result instanceof EvalContext) { 459 result = ((EvalContext) result).getSingleNodePointer(); 460 } 461 if (result instanceof Pointer) { 462 if (!isLenient() && !((NodePointer) result).isActual()) { 463 throw new JXPathNotFoundException("No pointer for xpath: " + xpath); 464 } 465 return (Pointer) result; 466 } 467 return NodePointer.newNodePointer(null, result, getLocale()); 468 } 469 470 /** 471 * {@inheritDoc} 472 * 473 * @see org.apache.commons.jxpath.JXPathContext#getPrefix(java.lang.String) 474 */ 475 @Override 476 public String getPrefix(final String namespaceURI) { 477 return namespaceResolver.getPrefix(namespaceURI); 478 } 479 480 @Override 481 public JXPathContext getRelativeContext(final Pointer pointer) { 482 final Object contextBean = pointer.getNode(); 483 if (contextBean == null) { 484 throw new JXPathException("Cannot create a relative context for a non-existent node: " + pointer); 485 } 486 return new JXPathContextReferenceImpl(this, contextBean, pointer); 487 } 488 489 /** 490 * Traverses the XPath and returns the resulting object. Primitive types are wrapped into objects. 491 * 492 * @param xpath expression 493 * @return Object found 494 */ 495 @Override 496 public Object getValue(final String xpath) { 497 final Expression expression = compileExpression(xpath); 498// TODO: (work in progress) - trying to integrate with Xalan 499// Object ctxNode = getNativeContextNode(expression); 500// if (ctxNode != null) { 501// System.err.println("WILL USE XALAN: " + xpath); 502// CachedXPathAPI api = new CachedXPathAPI(); 503// try { 504// if (expression instanceof Path) { 505// Node node = api.selectSingleNode((Node)ctxNode, xpath); 506// System.err.println("NODE: " + node); 507// if (node == null) { 508// return null; 509// } 510// return new DOMNodePointer(node, null).getValue(); 511// } 512// else { 513// XObject object = api.eval((Node)ctxNode, xpath); 514// switch (object.getType()) { 515// case XObject.CLASS_STRING: return object.str(); 516// case XObject.CLASS_NUMBER: return new Double(object.num()); 517// case XObject.CLASS_BOOLEAN: return new Boolean(object.bool()); 518// default: 519// System.err.println("OTHER TYPE: " + object.getTypeString()); 520// } 521// } 522// } 523// catch (TransformerException e) { 524// // TODO Auto-generated catch block 525// e.printStackTrace(); 526// } 527// return 528// } 529 return getValue(xpath, expression); 530 } 531 532 /** 533 * Calls getValue(xpath), converts the result to the required type and returns the result of the conversion. 534 * 535 * @param xpath expression 536 * @param requiredType Class 537 * @return Object 538 */ 539 @Override 540 public Object getValue(final String xpath, final Class requiredType) { 541 final Expression expr = compileExpression(xpath); 542 return getValue(xpath, expr, requiredType); 543 } 544 545 /** 546 * Gets the value indicated. 547 * 548 * @param xpath String 549 * @param expr Expression 550 * @return Object 551 */ 552 public Object getValue(final String xpath, final Expression expr) { 553 Object result = expr.computeValue(getEvalContext()); 554 if (result == null) { 555 if (expr instanceof Path && !isLenient()) { 556 throw new JXPathNotFoundException("No value for xpath: " + xpath); 557 } 558 return null; 559 } 560 if (result instanceof EvalContext) { 561 final EvalContext ctx = (EvalContext) result; 562 result = ctx.getSingleNodePointer(); 563 if (!isLenient() && result == null) { 564 throw new JXPathNotFoundException("No value for xpath: " + xpath); 565 } 566 } 567 if (result instanceof NodePointer) { 568 result = ((NodePointer) result).getValuePointer(); 569 if (!isLenient()) { 570 NodePointer.verify((NodePointer) result); 571 } 572 result = ((NodePointer) result).getValue(); 573 } 574 return result; 575 } 576 577 /** 578 * Gets the value indicated. 579 * 580 * @param xpath expression 581 * @param expr compiled Expression 582 * @param requiredType Class 583 * @return Object 584 */ 585 public Object getValue(final String xpath, final Expression expr, final Class requiredType) { 586 Object value = getValue(xpath, expr); 587 if (value != null && requiredType != null) { 588 if (!TypeUtils.canConvert(value, requiredType)) { 589 throw new JXPathTypeConversionException("Invalid expression type. '" + xpath + "' returns " + value.getClass().getName() 590 + ". It cannot be converted to " + requiredType.getName()); 591 } 592 value = TypeUtils.convert(value, requiredType); 593 } 594 return value; 595 } 596 597 /** 598 * Gets a VariablePointer for the given variable name. 599 * 600 * @param qName variable name 601 * @return NodePointer 602 */ 603 public NodePointer getVariablePointer(final QName qName) { 604 return NodePointer.newNodePointer(qName, VariablePointerFactory.contextWrapper(this), getLocale()); 605 } 606 607 /** 608 * Traverses the XPath and returns a Iterator of all results found for the path. If the XPath matches no properties in the graph, the Iterator will not be 609 * null. 610 * 611 * @param xpath expression 612 * @return Iterator 613 */ 614 @Override 615 public Iterator iterate(final String xpath) { 616 return iterate(xpath, compileExpression(xpath)); 617 } 618 619 /** 620 * Traverses the XPath and returns a Iterator of all results found for the path. If the XPath matches no properties in the graph, the Iterator will not be 621 * null. 622 * 623 * @param xpath expression 624 * @param expr compiled Expression 625 * @return Iterator 626 */ 627 public Iterator iterate(final String xpath, final Expression expr) { 628 return expr.iterate(getEvalContext()); 629 } 630 631 /** 632 * 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, 633 * the Iterator be empty, but not null. 634 * 635 * @param xpath expression 636 * @return Iterator 637 */ 638 @Override 639 public Iterator<Pointer> iteratePointers(final String xpath) { 640 return iteratePointers(xpath, compileExpression(xpath)); 641 } 642 643 /** 644 * 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, 645 * the Iterator be empty, but not null. 646 * 647 * @param xpath expression 648 * @param expr compiled Expression 649 * @return Iterator 650 */ 651 public Iterator<Pointer> iteratePointers(final String xpath, final Expression expr) { 652 return expr.iteratePointers(getEvalContext()); 653 } 654 655 @Override 656 public void registerNamespace(final String prefix, final String namespaceURI) { 657 if (namespaceResolver.isSealed()) { 658 namespaceResolver = (NamespaceResolver) namespaceResolver.clone(); 659 } 660 namespaceResolver.registerNamespace(prefix, namespaceURI); 661 } 662 663 @Override 664 public void removeAll(final String xpath) { 665 removeAll(xpath, compileExpression(xpath)); 666 } 667 668 /** 669 * Remove all matching nodes. 670 * 671 * @param xpath expression 672 * @param expr compiled Expression 673 */ 674 public void removeAll(final String xpath, final Expression expr) { 675 try { 676 final ArrayList<NodePointer> list = new ArrayList<>(); 677 Iterator<NodePointer> it = expr.iteratePointers(getEvalContext()); 678 while (it.hasNext()) { 679 list.add(it.next()); 680 } 681 Collections.sort(list, ReverseComparator.INSTANCE); 682 it = list.iterator(); 683 if (it.hasNext()) { 684 final NodePointer pointer = it.next(); 685 pointer.remove(); 686 while (it.hasNext()) { 687 removePath(it.next().asPath()); 688 } 689 } 690 } catch (final Throwable ex) { 691 throw new JXPathException("Exception trying to remove all for xpath " + xpath, ex); 692 } 693 } 694 695 @Override 696 public void removePath(final String xpath) { 697 removePath(xpath, compileExpression(xpath)); 698 } 699 700 /** 701 * Remove the specified path. 702 * 703 * @param xpath expression 704 * @param expr compiled Expression 705 */ 706 public void removePath(final String xpath, final Expression expr) { 707 try { 708 final NodePointer pointer = (NodePointer) getPointer(xpath, expr); 709 if (pointer != null) { 710 pointer.remove(); 711 } 712 } catch (final Throwable ex) { 713 throw new JXPathException("Exception trying to remove XPath " + xpath, ex); 714 } 715 } 716 717 /** 718 * {@inheritDoc} 719 */ 720 @Override 721 public void setExceptionHandler(final ExceptionHandler exceptionHandler) { 722 if (rootPointer instanceof NodePointer) { 723 ((NodePointer) rootPointer).setExceptionHandler(exceptionHandler); 724 } 725 } 726 727 @Override 728 public void setNamespaceContextPointer(final Pointer pointer) { 729 if (namespaceResolver.isSealed()) { 730 namespaceResolver = (NamespaceResolver) namespaceResolver.clone(); 731 } 732 namespaceResolver.setNamespaceContextPointer((NodePointer) pointer); 733 } 734 735 /** 736 * Sets the value of XPath to value. 737 * 738 * @param xpath path 739 * @param expr compiled Expression 740 * @param value Object 741 */ 742 public void setValue(final String xpath, final Expression expr, final Object value) { 743 try { 744 setValue(xpath, expr, value, false); 745 } catch (final Throwable ex) { 746 throw new JXPathException("Exception trying to set value with XPath " + xpath, ex); 747 } 748 } 749 750 /** 751 * Sets the specified value. 752 * 753 * @param xpath path 754 * @param expr compiled Expression 755 * @param value destination value 756 * @param create whether to create missing node(s) 757 * @return Pointer created 758 */ 759 private Pointer setValue(final String xpath, final Expression expr, final Object value, final boolean create) { 760 final Object result = expr.computeValue(getEvalContext()); 761 Pointer pointer; 762 if (result instanceof Pointer) { 763 pointer = (Pointer) result; 764 } else if (result instanceof EvalContext) { 765 final EvalContext ctx = (EvalContext) result; 766 pointer = ctx.getSingleNodePointer(); 767 } else { 768 if (create) { 769 checkSimplePath(expr); 770 } 771 // This should never happen 772 throw new JXPathException("Cannot set value for xpath: " + xpath); 773 } 774 if (create) { 775 pointer = ((NodePointer) pointer).createPath(this, value); 776 } else { 777 pointer.setValue(value); 778 } 779 return pointer; 780 } 781 782 @Override 783 public void setValue(final String xpath, final Object value) { 784 setValue(xpath, compileExpression(xpath), value); 785 } 786}