1 16 package org.apache.commons.jxpath.ri; 17 18 19 import java.lang.ref.SoftReference ; 20 import java.util.ArrayList ; 21 import java.util.Arrays ; 22 import java.util.Collections ; 23 import java.util.Comparator ; 24 import java.util.HashMap ; 25 import java.util.Iterator ; 26 import java.util.Map ; 27 import java.util.Vector ; 28 import java.util.Map.Entry; 29 30 import org.apache.commons.jxpath.CompiledExpression; 31 import org.apache.commons.jxpath.Function; 32 import org.apache.commons.jxpath.Functions; 33 import org.apache.commons.jxpath.JXPathContext; 34 import org.apache.commons.jxpath.JXPathException; 35 import org.apache.commons.jxpath.Pointer; 36 import org.apache.commons.jxpath.Variables; 37 import org.apache.commons.jxpath.ri.axes.InitialContext; 38 import org.apache.commons.jxpath.ri.axes.RootContext; 39 import org.apache.commons.jxpath.ri.compiler.Expression; 40 import org.apache.commons.jxpath.ri.compiler.LocationPath; 41 import org.apache.commons.jxpath.ri.compiler.Path; 42 import org.apache.commons.jxpath.ri.compiler.TreeCompiler; 43 import org.apache.commons.jxpath.ri.model.NodePointer; 44 import org.apache.commons.jxpath.ri.model.NodePointerFactory; 45 import org.apache.commons.jxpath.ri.model.VariablePointer; 46 import org.apache.commons.jxpath.ri.model.beans.BeanPointerFactory; 47 import org.apache.commons.jxpath.ri.model.beans.CollectionPointerFactory; 48 import org.apache.commons.jxpath.ri.model.container.ContainerPointerFactory; 49 import org.apache.commons.jxpath.ri.model.dynamic.DynamicPointerFactory; 50 import org.apache.commons.jxpath.util.TypeUtils; 51 52 58 public class JXPathContextReferenceImpl extends JXPathContext { 59 60 64 public static final boolean USE_SOFT_CACHE = true; 65 66 private static final Compiler COMPILER = new TreeCompiler(); 67 private static Map compiled = new HashMap (); 68 private static int cleanupCount = 0; 69 70 private static Vector nodeFactories = new Vector (); 71 private static NodePointerFactory nodeFactoryArray[] = null; 72 static { 73 nodeFactories.add(new CollectionPointerFactory()); 74 nodeFactories.add(new BeanPointerFactory()); 75 nodeFactories.add(new DynamicPointerFactory()); 76 77 Object domFactory = allocateConditionally( 79 "org.apache.commons.jxpath.ri.model.dom.DOMPointerFactory", 80 "org.w3c.dom.Node"); 81 if (domFactory != null) { 82 nodeFactories.add(domFactory); 83 } 84 85 Object jdomFactory = allocateConditionally( 87 "org.apache.commons.jxpath.ri.model.jdom.JDOMPointerFactory", 88 "org.jdom.Document"); 89 if (jdomFactory != null) { 90 nodeFactories.add(jdomFactory); 91 } 92 93 Object dynaBeanFactory = 95 allocateConditionally( 96 "org.apache.commons.jxpath.ri.model.dynabeans." 97 + "DynaBeanPointerFactory", 98 "org.apache.commons.beanutils.DynaBean"); 99 if (dynaBeanFactory != null) { 100 nodeFactories.add(dynaBeanFactory); 101 } 102 103 nodeFactories.add(new ContainerPointerFactory()); 104 createNodeFactoryArray(); 105 } 106 107 private Pointer rootPointer; 108 private Pointer contextPointer; 109 110 protected NamespaceResolver namespaceResolver; 111 112 private static final int CLEANUP_THRESHOLD = 500; 114 115 protected JXPathContextReferenceImpl(JXPathContext parentContext, 116 Object contextBean) 117 { 118 this(parentContext, contextBean, null); 119 } 120 121 public JXPathContextReferenceImpl( 122 JXPathContext parentContext, 123 Object contextBean, 124 Pointer contextPointer) 125 { 126 super(parentContext, contextBean); 127 128 synchronized (nodeFactories) { 129 createNodeFactoryArray(); 130 } 131 132 if (contextPointer != null) { 133 this.contextPointer = contextPointer; 134 this.rootPointer = 135 NodePointer.newNodePointer( 136 new QName(null, "root"), 137 contextPointer.getRootNode(), 138 getLocale()); 139 } 140 else { 141 this.contextPointer = 142 NodePointer.newNodePointer( 143 new QName(null, "root"), 144 contextBean, 145 getLocale()); 146 this.rootPointer = this.contextPointer; 147 } 148 149 namespaceResolver = new NamespaceResolver(); 150 namespaceResolver 151 .setNamespaceContextPointer((NodePointer) this.contextPointer); 152 } 153 154 private static void createNodeFactoryArray() { 155 if (nodeFactoryArray == null) { 156 nodeFactoryArray = 157 (NodePointerFactory[]) nodeFactories. 158 toArray(new NodePointerFactory[0]); 159 Arrays.sort(nodeFactoryArray, new Comparator () { 160 public int compare(Object a, Object b) { 161 int orderA = ((NodePointerFactory) a).getOrder(); 162 int orderB = ((NodePointerFactory) b).getOrder(); 163 return orderA - orderB; 164 } 165 }); 166 } 167 } 168 169 174 public static void addNodePointerFactory(NodePointerFactory factory) { 175 synchronized (nodeFactories) { 176 nodeFactories.add(factory); 177 nodeFactoryArray = null; 178 } 179 } 180 181 public static NodePointerFactory[] getNodePointerFactories() { 182 return nodeFactoryArray; 183 } 184 185 190 protected Compiler getCompiler() { 191 return COMPILER; 192 } 193 194 protected CompiledExpression compilePath(String xpath) { 195 return new JXPathCompiledExpression(xpath, compileExpression(xpath)); 196 } 197 198 private Expression compileExpression(String xpath) { 199 Expression expr; 200 201 synchronized (compiled) { 202 if (USE_SOFT_CACHE) { 203 expr = null; 204 SoftReference ref = (SoftReference ) compiled.get(xpath); 205 if (ref != null) { 206 expr = (Expression) ref.get(); 207 } 208 } 209 else { 210 expr = (Expression) compiled.get(xpath); 211 } 212 } 213 214 if (expr != null) { 215 return expr; 216 } 217 218 expr = (Expression) Parser.parseExpression(xpath, getCompiler()); 219 220 synchronized (compiled) { 221 if (USE_SOFT_CACHE) { 222 if (cleanupCount++ >= CLEANUP_THRESHOLD) { 223 Iterator it = compiled.entrySet().iterator(); 224 while (it.hasNext()) { 225 Entry me = (Entry) it.next(); 226 if (((SoftReference ) me.getValue()).get() == null) { 227 it.remove(); 228 } 229 } 230 cleanupCount = 0; 231 } 232 compiled.put(xpath, new SoftReference (expr)); 233 } 234 else { 235 compiled.put(xpath, expr); 236 } 237 } 238 239 return expr; 240 } 241 242 246 public Object getValue(String xpath) { 247 Expression expression = compileExpression(xpath); 248 280 return getValue(xpath, expression); 281 } 282 283 297 313 public Object getValue(String xpath, Expression expr) { 314 Object result = expr.computeValue(getEvalContext()); 315 if (result == null) { 316 if (expr instanceof Path) { 317 if (!isLenient()) { 318 throw new JXPathException("No value for xpath: " + xpath); 319 } 320 } 321 return null; 322 } 323 if (result instanceof EvalContext) { 324 EvalContext ctx = (EvalContext) result; 325 result = ctx.getSingleNodePointer(); 326 if (!isLenient() && result == null) { 327 throw new JXPathException("No value for xpath: " + xpath); 328 } 329 } 330 if (result instanceof NodePointer) { 331 result = ((NodePointer) result).getValuePointer(); 332 if (!isLenient() && !((NodePointer) result).isActual()) { 333 NodePointer parent = 340 ((NodePointer) result).getImmediateParentPointer(); 341 if (parent == null 342 || !parent.isContainer() 343 || !parent.isActual()) { 344 throw new JXPathException("No value for xpath: " + xpath); 345 } 346 } 347 result = ((NodePointer) result).getValue(); 348 } 349 return result; 350 } 351 352 356 public Object getValue(String xpath, Class requiredType) { 357 Expression expr = compileExpression(xpath); 358 return getValue(xpath, expr, requiredType); 359 } 360 361 public Object getValue(String xpath, Expression expr, Class requiredType) { 362 Object value = getValue(xpath, expr); 363 if (value != null && requiredType != null) { 364 if (!TypeUtils.canConvert(value, requiredType)) { 365 throw new JXPathException( 366 "Invalid expression type. '" 367 + xpath 368 + "' returns " 369 + value.getClass().getName() 370 + ". It cannot be converted to " 371 + requiredType.getName()); 372 } 373 value = TypeUtils.convert(value, requiredType); 374 } 375 return value; 376 } 377 378 383 public Iterator iterate(String xpath) { 384 return iterate(xpath, compileExpression(xpath)); 385 } 386 387 public Iterator iterate(String xpath, Expression expr) { 388 return expr.iterate(getEvalContext()); 389 } 390 391 public Pointer getPointer(String xpath) { 392 return getPointer(xpath, compileExpression(xpath)); 393 } 394 395 public Pointer getPointer(String xpath, Expression expr) { 396 Object result = expr.computeValue(getEvalContext()); 397 if (result instanceof EvalContext) { 398 result = ((EvalContext) result).getSingleNodePointer(); 399 } 400 if (result instanceof Pointer) { 401 if (!isLenient() && !((NodePointer) result).isActual()) { 402 throw new JXPathException("No pointer for xpath: " + xpath); 403 } 404 return (Pointer) result; 405 } 406 else { 407 return NodePointer.newNodePointer(null, result, getLocale()); 408 } 409 } 410 411 public void setValue(String xpath, Object value) { 412 setValue(xpath, compileExpression(xpath), value); 413 } 414 415 416 public void setValue(String xpath, Expression expr, Object value) { 417 try { 418 setValue(xpath, expr, value, false); 419 } 420 catch (Throwable ex) { 421 throw new JXPathException( 422 "Exception trying to set value with xpath " + xpath, ex); 423 } 424 } 425 426 public Pointer createPath(String xpath) { 427 return createPath(xpath, compileExpression(xpath)); 428 } 429 430 public Pointer createPath(String xpath, Expression expr) { 431 try { 432 Object result = expr.computeValue(getEvalContext()); 433 Pointer pointer = null; 434 435 if (result instanceof Pointer) { 436 pointer = (Pointer) result; 437 } 438 else if (result instanceof EvalContext) { 439 EvalContext ctx = (EvalContext) result; 440 pointer = ctx.getSingleNodePointer(); 441 } 442 else { 443 checkSimplePath(expr); 444 throw new JXPathException("Cannot create path:" + xpath); 446 } 447 return ((NodePointer) pointer).createPath(this); 448 } 449 catch (Throwable ex) { 450 throw new JXPathException( 451 "Exception trying to create xpath " + xpath, 452 ex); 453 } 454 } 455 456 public Pointer createPathAndSetValue(String xpath, Object value) { 457 return createPathAndSetValue(xpath, compileExpression(xpath), value); 458 } 459 460 public Pointer createPathAndSetValue( 461 String xpath, 462 Expression expr, 463 Object value) 464 { 465 try { 466 return setValue(xpath, expr, value, true); 467 } 468 catch (Throwable ex) { 469 throw new JXPathException( 470 "Exception trying to create xpath " + xpath, 471 ex); 472 } 473 } 474 475 private Pointer setValue( 476 String xpath, 477 Expression expr, 478 Object value, 479 boolean create) 480 { 481 Object result = expr.computeValue(getEvalContext()); 482 Pointer pointer = null; 483 484 if (result instanceof Pointer) { 485 pointer = (Pointer) result; 486 } 487 else if (result instanceof EvalContext) { 488 EvalContext ctx = (EvalContext) result; 489 pointer = ctx.getSingleNodePointer(); 490 } 491 else { 492 if (create) { 493 checkSimplePath(expr); 494 } 495 496 throw new JXPathException("Cannot set value for xpath: " + xpath); 498 } 499 if (create) { 500 pointer = ((NodePointer) pointer).createPath(this, value); 501 } 502 else { 503 pointer.setValue(value); 504 } 505 return pointer; 506 } 507 508 512 private void checkSimplePath(Expression expr) { 513 if (!(expr instanceof LocationPath) 514 || !((LocationPath) expr).isSimplePath()) { 515 throw new JXPathException( 516 "JXPath can only create a path if it uses exclusively " 517 + "the child:: and attribute:: axes and has " 518 + "no context-dependent predicates"); 519 } 520 } 521 522 528 public Iterator iteratePointers(String xpath) { 529 return iteratePointers(xpath, compileExpression(xpath)); 530 } 531 532 public Iterator iteratePointers(String xpath, Expression expr) { 533 return expr.iteratePointers(getEvalContext()); 534 } 535 536 public void removePath(String xpath) { 537 removePath(xpath, compileExpression(xpath)); 538 } 539 540 public void removePath(String xpath, Expression expr) { 541 try { 542 NodePointer pointer = (NodePointer) getPointer(xpath, expr); 543 if (pointer != null) { 544 ((NodePointer) pointer).remove(); 545 } 546 } 547 catch (Throwable ex) { 548 throw new JXPathException( 549 "Exception trying to remove xpath " + xpath, 550 ex); 551 } 552 } 553 554 public void removeAll(String xpath) { 555 removeAll(xpath, compileExpression(xpath)); 556 } 557 558 public void removeAll(String xpath, Expression expr) { 559 try { 560 ArrayList list = new ArrayList (); 561 Iterator it = expr.iteratePointers(getEvalContext()); 562 while (it.hasNext()) { 563 list.add(it.next()); 564 } 565 Collections.sort(list); 566 for (int i = list.size() - 1; i >= 0; i--) { 567 NodePointer pointer = (NodePointer) list.get(i); 568 pointer.remove(); 569 } 570 } 571 catch (Throwable ex) { 572 throw new JXPathException( 573 "Exception trying to remove all for xpath " + xpath, 574 ex); 575 } 576 } 577 578 public JXPathContext getRelativeContext(Pointer pointer) { 579 Object contextBean = pointer.getNode(); 580 if (contextBean == null) { 581 throw new JXPathException( 582 "Cannot create a relative context for a non-existent node: " 583 + pointer); 584 } 585 return new JXPathContextReferenceImpl(this, contextBean, pointer); 586 } 587 588 public Pointer getContextPointer() { 589 return contextPointer; 590 } 591 592 private NodePointer getAbsoluteRootPointer() { 593 return (NodePointer) rootPointer; 594 } 595 596 private EvalContext getEvalContext() { 597 return new InitialContext(new RootContext(this, 598 (NodePointer) getContextPointer())); 599 } 600 601 public EvalContext getAbsoluteRootContext() { 602 return new InitialContext(new RootContext(this, 603 getAbsoluteRootPointer())); 604 } 605 606 public NodePointer getVariablePointer(QName name) { 607 String varName = name.toString(); 608 JXPathContext varCtx = this; 609 Variables vars = null; 610 while (varCtx != null) { 611 vars = varCtx.getVariables(); 612 if (vars.isDeclaredVariable(varName)) { 613 break; 614 } 615 varCtx = varCtx.getParentContext(); 616 vars = null; 617 } 618 if (vars != null) { 619 return new VariablePointer(vars, name); 620 } 621 else { 622 return new VariablePointer(name); 626 } 627 } 628 629 public Function getFunction(QName functionName, Object [] parameters) { 630 String namespace = functionName.getPrefix(); 631 String name = functionName.getName(); 632 JXPathContext funcCtx = this; 633 Function func = null; 634 Functions funcs; 635 while (funcCtx != null) { 636 funcs = funcCtx.getFunctions(); 637 if (funcs != null) { 638 func = funcs.getFunction(namespace, name, parameters); 639 if (func != null) { 640 return func; 641 } 642 } 643 funcCtx = funcCtx.getParentContext(); 644 } 645 throw new JXPathException( 646 "Undefined function: " + functionName.toString()); 647 } 648 649 public void registerNamespace(String prefix, String namespaceURI) { 650 if (namespaceResolver.isSealed()) { 651 namespaceResolver = (NamespaceResolver) namespaceResolver.clone(); 652 } 653 namespaceResolver.registerNamespace(prefix, namespaceURI); 654 } 655 656 public String getNamespaceURI(String prefix) { 657 return namespaceResolver.getNamespaceURI(prefix); 658 } 659 660 public void setNamespaceContextPointer(Pointer pointer) { 661 if (namespaceResolver.isSealed()) { 662 namespaceResolver = (NamespaceResolver) namespaceResolver.clone(); 663 } 664 namespaceResolver.setNamespaceContextPointer((NodePointer) pointer); 665 } 666 667 public Pointer getNamespaceContextPointer() { 668 return namespaceResolver.getNamespaceContextPointer(); 669 } 670 671 public NamespaceResolver getNamespaceResolver() { 672 namespaceResolver.seal(); 673 return namespaceResolver; 674 } 675 676 680 public static Object allocateConditionally( 681 String className, 682 String existenceCheckClassName) 683 { 684 try { 685 try { 686 Class.forName(existenceCheckClassName); 687 } 688 catch (ClassNotFoundException ex) { 689 return null; 690 } 691 692 Class cls = Class.forName(className); 693 return cls.newInstance(); 694 } 695 catch (Exception ex) { 696 throw new JXPathException("Cannot allocate " + className, ex); 697 } 698 } 699 } | Popular Tags |