Changeset 7489 for trunk/Res_Xalan/org/apache/xpath/XPathContext.java
- Timestamp:
- 11/24/06 14:20:40 (6 years ago)
- File:
-
- 1 edited
-
trunk/Res_Xalan/org/apache/xpath/XPathContext.java (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Res_Xalan/org/apache/xpath/XPathContext.java
r7031 r7489 58 58 public class XPathContext extends DTMManager // implements ExpressionContext 59 59 { 60 IntStack m_last_pushed_rtfdtm=new IntStack(); 61 /** 62 * Stack of cached "reusable" DTMs for Result Tree Fragments. 63 * This is a kluge to handle the problem of starting an RTF before 64 * the old one is complete. 65 * 66 * %REVIEW% I'm using a Vector rather than Stack so we can reuse 67 * the DTMs if the problem occurs multiple times. I'm not sure that's 68 * really a net win versus discarding the DTM and starting a new one... 69 * but the retained RTF DTM will have been tail-pruned so should be small. 70 */ 71 private Vector m_rtfdtm_stack=null; 72 /** Index of currently active RTF DTM in m_rtfdtm_stack */ 73 private int m_which_rtfdtm=-1; 74 75 /** 76 * Most recent "reusable" DTM for Global Result Tree Fragments. No stack is 77 * required since we're never going to pop these. 78 */ 79 private SAX2RTFDTM m_global_rtfdtm=null; 80 81 82 /** 83 * Though XPathContext context extends 84 * the DTMManager, it really is a proxy for this object, which 85 * is the real DTMManager. 86 */ 87 protected DTMManager m_dtmManager = DTMManager.newInstance( 88 org.apache.xpath.objects.XMLStringFactoryImpl.getFactory()); 89 90 /** 91 * Return the DTMManager object. Though XPathContext context extends 92 * the DTMManager, it really is a proxy for the real DTMManager. If a 93 * caller needs to make a lot of calls to the DTMManager, it is faster 94 * if it gets the real one from this function. 95 */ 96 public DTMManager getDTMManager() 97 { 98 return m_dtmManager; 99 } 100 101 /** 102 * Get an instance of a DTM, loaded with the content from the 103 * specified source. If the unique flag is true, a new instance will 104 * always be returned. Otherwise it is up to the DTMManager to return a 105 * new instance or an instance that it already created and may be being used 106 * by someone else. 107 * (I think more parameters will need to be added for error handling, and entity 108 * resolution). 109 * 110 * @param source the specification of the source object, which may be null, 111 * in which case it is assumed that node construction will take 112 * by some other means. 113 * @param unique true if the returned DTM must be unique, probably because it 114 * is going to be mutated. 115 * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may 116 * be null. 117 * @param incremental true if the construction should try and be incremental. 118 * @param doIndexing true if the caller considers it worth it to use 119 * indexing schemes. 120 * 121 * @return a non-null DTM reference. 122 */ 123 public DTM getDTM(javax.xml.transform.Source source, boolean unique, 124 DTMWSFilter wsfilter, 125 boolean incremental, 126 boolean doIndexing) 127 { 128 return m_dtmManager.getDTM(source, unique, wsfilter, 129 incremental, doIndexing); 130 } 131 132 /** 133 * Get an instance of a DTM that "owns" a node handle. 134 * 135 * @param nodeHandle the nodeHandle. 136 * 137 * @return a non-null DTM reference. 138 */ 139 public DTM getDTM(int nodeHandle) 140 { 141 return m_dtmManager.getDTM(nodeHandle); 142 } 143 144 /** 145 * Given a W3C DOM node, try and return a DTM handle. 146 * Note: calling this may be non-optimal. 147 * 148 * @param node Non-null reference to a DOM node. 149 * 150 * @return a valid DTM handle. 151 */ 152 public int getDTMHandleFromNode(org.w3c.dom.Node node) 153 { 154 return m_dtmManager.getDTMHandleFromNode(node); 155 } 156 // 157 // 158 /** 159 * %TBD% Doc 160 */ 161 public int getDTMIdentity(DTM dtm) 162 { 163 return m_dtmManager.getDTMIdentity(dtm); 164 } 165 // 166 /** 167 * Creates an empty <code>DocumentFragment</code> object. 168 * @return A new <code>DocumentFragment handle</code>. 169 */ 170 public DTM createDocumentFragment() 171 { 172 return m_dtmManager.createDocumentFragment(); 173 } 174 // 175 /** 176 * Release a DTM either to a lru pool, or completely remove reference. 177 * DTMs without system IDs are always hard deleted. 178 * State: experimental. 179 * 180 * @param dtm The DTM to be released. 181 * @param shouldHardDelete True if the DTM should be removed no matter what. 182 * @return true if the DTM was removed, false if it was put back in a lru pool. 183 */ 184 public boolean release(DTM dtm, boolean shouldHardDelete) 185 { 186 // %REVIEW% If it's a DTM which may contain multiple Result Tree 187 // Fragments, we can't discard it unless we know not only that it 188 // is empty, but that the XPathContext itself is going away. So do 189 // _not_ accept the request. (May want to do it as part of 190 // reset(), though.) 191 if(m_rtfdtm_stack!=null && m_rtfdtm_stack.contains(dtm)) 192 { 193 return false; 194 } 195 196 return m_dtmManager.release(dtm, shouldHardDelete); 197 } 198 199 /** 200 * Create a new <code>DTMIterator</code> based on an XPath 201 * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or 202 * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>. 203 * 204 * @param xpathCompiler ??? Somehow we need to pass in a subpart of the 205 * expression. I hate to do this with strings, since the larger expression 206 * has already been parsed. 207 * 208 * @param pos The position in the expression. 209 * @return The newly created <code>DTMIterator</code>. 210 */ 211 public DTMIterator createDTMIterator(Object xpathCompiler, int pos) 212 { 213 return m_dtmManager.createDTMIterator(xpathCompiler, pos); 214 } 215 // 216 /** 217 * Create a new <code>DTMIterator</code> based on an XPath 218 * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or 219 * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>. 220 * 221 * @param xpathString Must be a valid string expressing a 222 * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or 223 * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>. 224 * 225 * @param presolver An object that can resolve prefixes to namespace URLs. 226 * 227 * @return The newly created <code>DTMIterator</code>. 228 */ 229 public DTMIterator createDTMIterator(String xpathString, 230 PrefixResolver presolver) 231 { 232 return m_dtmManager.createDTMIterator(xpathString, presolver); 233 } 234 // 235 /** 236 * Create a new <code>DTMIterator</code> based only on a whatToShow and 237 * a DTMFilter. The traversal semantics are defined as the descendant 238 * access. 239 * 240 * @param whatToShow This flag specifies which node types may appear in 241 * the logical view of the tree presented by the iterator. See the 242 * description of <code>NodeFilter</code> for the set of possible 243 * <code>SHOW_</code> values.These flags can be combined using 244 * <code>OR</code>. 245 * @param filter The <code>NodeFilter</code> to be used with this 246 * <code>TreeWalker</code>, or <code>null</code> to indicate no filter. 247 * @param entityReferenceExpansion The value of this flag determines 248 * whether entity reference nodes are expanded. 249 * 250 * @return The newly created <code>NodeIterator</code>. 251 */ 252 public DTMIterator createDTMIterator(int whatToShow, 253 DTMFilter filter, boolean entityReferenceExpansion) 254 { 255 return m_dtmManager.createDTMIterator(whatToShow, filter, entityReferenceExpansion); 256 } 257 258 /** 259 * Create a new <code>DTMIterator</code> that holds exactly one node. 260 * 261 * @param node The node handle that the DTMIterator will iterate to. 262 * 263 * @return The newly created <code>DTMIterator</code>. 264 */ 265 public DTMIterator createDTMIterator(int node) 266 { 267 // DescendantIterator iter = new DescendantIterator(); 268 DTMIterator iter = new org.apache.xpath.axes.OneStepIteratorForward(Axis.SELF); 269 iter.setRoot(node, this); 270 return iter; 271 // return m_dtmManager.createDTMIterator(node); 272 } 273 274 /** 275 * Create an XPathContext instance. 276 */ 277 public XPathContext() 278 { 279 m_prefixResolvers.push(null); 280 m_currentNodes.push(DTM.NULL); 281 m_currentExpressionNodes.push(DTM.NULL); 282 m_saxLocations.push(null); 283 } 284 285 /** 286 * Create an XPathContext instance. 287 * @param owner Value that can be retrieved via the getOwnerObject() method. 288 * @see #getOwnerObject 289 */ 290 public XPathContext(Object owner) 291 { 292 m_owner = owner; 293 try { 294 m_ownerGetErrorListener = m_owner.getClass().getMethod("getErrorListener", new Class[] {}); 295 } 296 catch (NoSuchMethodException nsme) {} 297 m_prefixResolvers.push(null); 298 m_currentNodes.push(DTM.NULL); 299 m_currentExpressionNodes.push(DTM.NULL); 300 m_saxLocations.push(null); 301 } 302 303 /** 304 * Reset for new run. 305 */ 306 public void reset() 307 { 308 // These couldn't be disposed of earlier (see comments in release()); zap them now. 309 if(m_rtfdtm_stack!=null) 310 for (java.util.Enumeration e = m_rtfdtm_stack.elements() ; e.hasMoreElements() ;) 311 m_dtmManager.release((DTM)e.nextElement(), true); 312 313 m_rtfdtm_stack=null; // drop our references too 314 m_which_rtfdtm=-1; 315 316 if(m_global_rtfdtm!=null) 317 m_dtmManager.release(m_global_rtfdtm,true); 318 m_global_rtfdtm=null; 319 320 m_dtmManager = DTMManager.newInstance( 321 org.apache.xpath.objects.XMLStringFactoryImpl.getFactory()); 322 323 m_saxLocations.removeAllElements(); 324 m_axesIteratorStack.removeAllElements(); 325 m_contextNodeLists.removeAllElements(); 326 m_currentExpressionNodes.removeAllElements(); 327 m_currentNodes.removeAllElements(); 328 m_iteratorRoots.RemoveAllNoClear(); 329 m_predicatePos.removeAllElements(); 330 m_predicateRoots.RemoveAllNoClear(); 331 m_prefixResolvers.removeAllElements(); 332 333 m_prefixResolvers.push(null); 334 m_currentNodes.push(DTM.NULL); 335 m_currentExpressionNodes.push(DTM.NULL); 336 m_saxLocations.push(null); 337 } 338 339 /** The current stylesheet locator. */ 340 ObjectStack m_saxLocations = new ObjectStack(RECURSIONLIMIT); 341 342 /** 343 * Set the current locater in the stylesheet. 344 * 345 * @param location The location within the stylesheet. 346 */ 347 public void setSAXLocator(SourceLocator location) 348 { 349 m_saxLocations.setTop(location); 350 } 351 352 /** 353 * Set the current locater in the stylesheet. 354 * 355 * @param location The location within the stylesheet. 356 */ 357 public void pushSAXLocator(SourceLocator location) 358 { 359 m_saxLocations.push(location); 360 } 361 362 /** 363 * Push a slot on the locations stack so that setSAXLocator can be 364 * repeatedly called. 365 * 366 * @param location The location within the stylesheet. 367 */ 368 public void pushSAXLocatorNull() 369 { 370 m_saxLocations.push(null); 371 } 372 373 374 /** 375 * Pop the current locater. 376 */ 377 public void popSAXLocator() 378 { 379 m_saxLocations.pop(); 380 } 381 382 /** 383 * Get the current locater in the stylesheet. 384 * 385 * @return The location within the stylesheet, or null if not known. 386 */ 387 public SourceLocator getSAXLocator() 388 { 389 return (SourceLocator) m_saxLocations.peek(); 390 } 391 392 /** The owner context of this XPathContext. In the case of XSLT, this will be a 393 * Transformer object. 394 */ 395 private Object m_owner; 396 397 /** The owner context of this XPathContext. In the case of XSLT, this will be a 398 * Transformer object. 399 */ 400 private Method m_ownerGetErrorListener; 401 402 /** 403 * Get the "owner" context of this context, which should be, 404 * in the case of XSLT, the Transformer object. This is needed 405 * so that XSLT functions can get the Transformer. 406 * @return The owner object passed into the constructor, or null. 407 */ 408 public Object getOwnerObject() 409 { 410 return m_owner; 411 } 412 413 // ================ VarStack =================== 414 415 /** 416 * The stack of Variable stacks. A VariableStack will be 417 * pushed onto this stack for each template invocation. 418 */ 419 private VariableStack m_variableStacks = new VariableStack(); 420 421 /** 422 * Get the variable stack, which is in charge of variables and 423 * parameters. 424 * 425 * @return the variable stack, which should not be null. 426 */ 427 public final VariableStack getVarStack() 428 { 429 return m_variableStacks; 430 } 431 432 /** 433 * Get the variable stack, which is in charge of variables and 434 * parameters. 435 * 436 * @param varStack non-null reference to the variable stack. 437 */ 438 public final void setVarStack(VariableStack varStack) 439 { 440 m_variableStacks = varStack; 441 } 442 443 // ================ SourceTreeManager =================== 444 445 /** The source tree manager, which associates Source objects to source 446 * tree nodes. */ 447 private SourceTreeManager m_sourceTreeManager = new SourceTreeManager(); 448 449 /** 450 * Get the SourceTreeManager associated with this execution context. 451 * 452 * @return the SourceTreeManager associated with this execution context. 453 */ 454 public final SourceTreeManager getSourceTreeManager() 455 { 456 return m_sourceTreeManager; 457 } 458 459 /** 460 * Set the SourceTreeManager associated with this execution context. 461 * 462 * @param mgr the SourceTreeManager to be associated with this 463 * execution context. 464 */ 465 public void setSourceTreeManager(SourceTreeManager mgr) 466 { 467 m_sourceTreeManager = mgr; 468 } 469 470 // ================================================= 471 472 /** The ErrorListener where errors and warnings are to be reported. */ 473 private ErrorListener m_errorListener; 474 475 /** A default ErrorListener in case our m_errorListener was not specified and our 476 * owner either does not have an ErrorListener or has a null one. 477 */ 478 private ErrorListener m_defaultErrorListener; 479 480 /** 481 * Get the ErrorListener where errors and warnings are to be reported. 482 * 483 * @return A non-null ErrorListener reference. 484 */ 485 public final ErrorListener getErrorListener() 486 { 487 488 if (null != m_errorListener) 489 return m_errorListener; 490 491 ErrorListener retval = null; 492 493 try { 494 if (null != m_ownerGetErrorListener) 495 retval = (ErrorListener) m_ownerGetErrorListener.invoke(m_owner, new Object[] {}); 496 } 497 catch (Exception e) {} 498 499 if (null == retval) 500 { 501 if (null == m_defaultErrorListener) 502 m_defaultErrorListener = new org.apache.xml.utils.DefaultErrorHandler(); 503 retval = m_defaultErrorListener; 504 } 505 506 return retval; 507 } 508 509 /** 510 * Set the ErrorListener where errors and warnings are to be reported. 511 * 512 * @param listener A non-null ErrorListener reference. 513 */ 514 public void setErrorListener(ErrorListener listener) throws IllegalArgumentException 515 { 516 if (listener == null) 517 throw new IllegalArgumentException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, null)); //"Null error handler"); 518 m_errorListener = listener; 519 } 520 521 522 // ================================================= 523 524 /** The TrAX URI Resolver for resolving URIs from the document(...) 525 * function to source tree nodes. */ 526 private URIResolver m_uriResolver; 527 528 /** 529 * Get the URIResolver associated with this execution context. 530 * 531 * @return a URI resolver, which may be null. 532 */ 533 public final URIResolver getURIResolver() 534 { 535 return m_uriResolver; 536 } 537 538 /** 539 * Set the URIResolver associated with this execution context. 540 * 541 * @param resolver the URIResolver to be associated with this 542 * execution context, may be null to clear an already set resolver. 543 */ 544 public void setURIResolver(URIResolver resolver) 545 { 546 m_uriResolver = resolver; 547 } 548 549 // ================================================= 550 551 /** The reader of the primary source tree. */ 552 public XMLReader m_primaryReader; 553 554 /** 555 * Get primary XMLReader associated with this execution context. 556 * 557 * @return The reader of the primary source tree. 558 */ 559 public final XMLReader getPrimaryReader() 560 { 561 return m_primaryReader; 562 } 563 564 /** 565 * Set primary XMLReader associated with this execution context. 566 * 567 * @param reader The reader of the primary source tree. 568 */ 569 public void setPrimaryReader(XMLReader reader) 570 { 571 m_primaryReader = reader; 572 } 573 574 // ================================================= 575 576 577 /** Misnamed string manager for XPath messages. */ 578 // private static XSLMessages m_XSLMessages = new XSLMessages(); 579 580 /** 581 * Tell the user of an assertion error, and probably throw an 582 * exception. 583 * 584 * @param b If false, a TransformerException will be thrown. 585 * @param msg The assertion message, which should be informative. 586 * 587 * @throws javax.xml.transform.TransformerException if b is false. 588 */ 589 private void assertion(boolean b, String msg) throws javax.xml.transform.TransformerException 590 { 591 if (!b) 592 { 593 ErrorListener errorHandler = getErrorListener(); 594 595 if (errorHandler != null) 596 { 597 errorHandler.fatalError( 598 new TransformerException( 599 XSLMessages.createMessage( 600 XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION, 601 new Object[]{ msg }), (SAXSourceLocator)this.getSAXLocator())); 602 } 603 } 604 } 605 606 //========================================================== 607 // SECTION: Execution context state tracking 608 //========================================================== 609 610 /** 611 * The current context node list. 612 */ 613 private Stack m_contextNodeLists = new Stack(); 614 615 public Stack getContextNodeListsStack() { return m_contextNodeLists; } 616 public void setContextNodeListsStack(Stack s) { m_contextNodeLists = s; } 617 618 /** 619 * Get the current context node list. 620 * 621 * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>, 622 * also refered to here as a <term>context node list</term>. 623 */ 624 public final DTMIterator getContextNodeList() 625 { 626 627 if (m_contextNodeLists.size() > 0) 628 return (DTMIterator) m_contextNodeLists.peek(); 629 else 630 return null; 631 } 632 633 /** 634 * Set the current context node list. 635 * 636 * @param nl the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>, 637 * also refered to here as a <term>context node list</term>. 638 * @xsl.usage internal 639 */ 640 public final void pushContextNodeList(DTMIterator nl) 641 { 642 m_contextNodeLists.push(nl); 643 } 644 645 /** 646 * Pop the current context node list. 647 * @xsl.usage internal 648 */ 649 public final void popContextNodeList() 650 { 651 if(m_contextNodeLists.isEmpty()) 652 System.err.println("Warning: popContextNodeList when stack is empty!"); 653 else 654 m_contextNodeLists.pop(); 655 } 656 657 /** 658 * The ammount to use for stacks that record information during the 659 * recursive execution. 660 */ 661 public static final int RECURSIONLIMIT = (1024*4); 662 663 /** The stack of <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a> objects. 664 * Not to be confused with the current node list. %REVIEW% Note that there 665 * are no bounds check and resize for this stack, so if it is blown, it's all 666 * over. */ 667 private IntStack m_currentNodes = new IntStack(RECURSIONLIMIT); 668 669 // private NodeVector m_currentNodes = new NodeVector(); 670 671 public IntStack getCurrentNodeStack() {return m_currentNodes; } 672 public void setCurrentNodeStack(IntStack nv) { m_currentNodes = nv; } 673 674 /** 675 * Get the current context node. 676 * 677 * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>. 678 */ 679 public final int getCurrentNode() 680 { 681 return m_currentNodes.peek(); 682 } 683 684 /** 685 * Set the current context node and expression node. 686 * 687 * @param cn the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>. 688 * @param en the sub-expression context node. 689 */ 690 public final void pushCurrentNodeAndExpression(int cn, int en) 691 { 692 m_currentNodes.push(cn); 693 m_currentExpressionNodes.push(cn); 694 } 695 696 /** 697 * Set the current context node. 698 */ 699 public final void popCurrentNodeAndExpression() 700 { 701 m_currentNodes.quickPop(1); 702 m_currentExpressionNodes.quickPop(1); 703 } 704 705 /** 706 * Push the current context node, expression node, and prefix resolver. 707 * 708 * @param cn the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>. 709 * @param en the sub-expression context node. 710 * @param nc the namespace context (prefix resolver. 711 */ 712 public final void pushExpressionState(int cn, int en, PrefixResolver nc) 713 { 714 m_currentNodes.push(cn); 715 m_currentExpressionNodes.push(cn); 716 m_prefixResolvers.push(nc); 717 } 718 719 /** 720 * Pop the current context node, expression node, and prefix resolver. 721 */ 722 public final void popExpressionState() 723 { 724 m_currentNodes.quickPop(1); 725 m_currentExpressionNodes.quickPop(1); 726 m_prefixResolvers.pop(); 727 } 728 729 730 731 /** 732 * Set the current context node. 733 * 734 * @param n the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>. 735 */ 736 public final void pushCurrentNode(int n) 737 { 738 m_currentNodes.push(n); 739 } 740 741 /** 742 * Pop the current context node. 743 */ 744 public final void popCurrentNode() 745 { 746 m_currentNodes.quickPop(1); 747 } 748 749 /** 750 * Set the current predicate root. 751 */ 752 public final void pushPredicateRoot(int n) 753 { 754 m_predicateRoots.push(n); 755 } 756 757 /** 758 * Pop the current predicate root. 759 */ 760 public final void popPredicateRoot() 761 { 762 m_predicateRoots.popQuick(); 763 } 764 765 /** 766 * Get the current predicate root. 767 */ 768 public final int getPredicateRoot() 769 { 770 return m_predicateRoots.peepOrNull(); 771 } 772 773 /** 774 * Set the current location path iterator root. 775 */ 776 public final void pushIteratorRoot(int n) 777 { 778 m_iteratorRoots.push(n); 779 } 780 781 /** 782 * Pop the current location path iterator root. 783 */ 784 public final void popIteratorRoot() 785 { 786 m_iteratorRoots.popQuick(); 787 } 788 789 /** 790 * Get the current location path iterator root. 791 */ 792 public final int getIteratorRoot() 793 { 794 return m_iteratorRoots.peepOrNull(); 795 } 796 797 /** A stack of the current sub-expression nodes. */ 798 private NodeVector m_iteratorRoots = new NodeVector(); 799 800 /** A stack of the current sub-expression nodes. */ 801 private NodeVector m_predicateRoots = new NodeVector(); 802 803 /** A stack of the current sub-expression nodes. */ 804 private IntStack m_currentExpressionNodes = new IntStack(RECURSIONLIMIT); 805 806 807 public IntStack getCurrentExpressionNodeStack() { return m_currentExpressionNodes; } 808 public void setCurrentExpressionNodeStack(IntStack nv) { m_currentExpressionNodes = nv; } 809 810 private IntStack m_predicatePos = new IntStack(); 811 812 public final int getPredicatePos() 813 { 814 return m_predicatePos.peek(); 815 } 816 817 public final void pushPredicatePos(int n) 818 { 819 m_predicatePos.push(n); 820 } 821 822 public final void popPredicatePos() 823 { 824 m_predicatePos.pop(); 825 } 826 827 /** 828 * Get the current node that is the expression's context (i.e. for current() support). 829 * 830 * @return The current sub-expression node. 831 */ 832 public final int getCurrentExpressionNode() 833 { 834 return m_currentExpressionNodes.peek(); 835 } 836 837 /** 838 * Set the current node that is the expression's context (i.e. for current() support). 839 * 840 * @param n The sub-expression node to be current. 841 */ 842 public final void pushCurrentExpressionNode(int n) 843 { 844 m_currentExpressionNodes.push(n); 845 } 846 847 /** 848 * Pop the current node that is the expression's context 849 * (i.e. for current() support). 850 */ 851 public final void popCurrentExpressionNode() 852 { 853 m_currentExpressionNodes.quickPop(1); 854 } 855 856 private ObjectStack m_prefixResolvers 857 = new ObjectStack(RECURSIONLIMIT); 858 859 /** 860 * Get the current namespace context for the xpath. 861 * 862 * @return the current prefix resolver for resolving prefixes to 863 * namespace URLs. 864 */ 865 public final PrefixResolver getNamespaceContext() 866 { 867 return (PrefixResolver) m_prefixResolvers.peek(); 868 } 869 870 /** 871 * Get the current namespace context for the xpath. 872 * 873 * @param pr the prefix resolver to be used for resolving prefixes to 874 * namespace URLs. 875 */ 876 public final void setNamespaceContext(PrefixResolver pr) 877 { 878 m_prefixResolvers.setTop(pr); 879 } 880 881 /** 882 * Push a current namespace context for the xpath. 883 * 884 * @param pr the prefix resolver to be used for resolving prefixes to 885 * namespace URLs. 886 */ 887 public final void pushNamespaceContext(PrefixResolver pr) 888 { 889 m_prefixResolvers.push(pr); 890 } 891 892 /** 893 * Just increment the namespace contest stack, so that setNamespaceContext 894 * can be used on the slot. 895 */ 896 public final void pushNamespaceContextNull() 897 { 898 m_prefixResolvers.push(null); 899 } 900 901 /** 902 * Pop the current namespace context for the xpath. 903 */ 904 public final void popNamespaceContext() 905 { 906 m_prefixResolvers.pop(); 907 } 908 909 //========================================================== 910 // SECTION: Current TreeWalker contexts (for internal use) 911 //========================================================== 912 913 /** 914 * Stack of AxesIterators. 915 */ 916 private Stack m_axesIteratorStack = new Stack(); 917 918 public Stack getAxesIteratorStackStacks() { return m_axesIteratorStack; } 919 public void setAxesIteratorStackStacks(Stack s) { m_axesIteratorStack = s; } 920 921 /** 922 * Push a TreeWalker on the stack. 923 * 924 * @param iter A sub-context AxesWalker. 925 * @xsl.usage internal 926 */ 927 public final void pushSubContextList(SubContextList iter) 928 { 929 m_axesIteratorStack.push(iter); 930 } 931 932 /** 933 * Pop the last pushed axes iterator. 934 * @xsl.usage internal 935 */ 936 public final void popSubContextList() 937 { 938 m_axesIteratorStack.pop(); 939 } 940 941 /** 942 * Get the current axes iterator, or return null if none. 943 * 944 * @return the sub-context node list. 945 * @xsl.usage internal 946 */ 947 public SubContextList getSubContextList() 948 { 949 return m_axesIteratorStack.isEmpty() 950 ? null : (SubContextList) m_axesIteratorStack.peek(); 951 } 952 953 /** 954 * Get the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a> 955 * as defined by the XSLT spec. 956 * 957 * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>. 958 * @xsl.usage internal 959 */ 960 961 public org.apache.xpath.axes.SubContextList getCurrentNodeList() 962 { 963 return m_axesIteratorStack.isEmpty() 964 ? null : (SubContextList) m_axesIteratorStack.elementAt(0); 965 } 966 //========================================================== 967 // SECTION: Implementation of ExpressionContext interface 968 //========================================================== 969 970 /** 971 * Get the current context node. 972 * @return The current context node. 973 */ 974 public final int getContextNode() 975 { 976 return this.getCurrentNode(); 977 } 978 979 /** 980 * Get the current context node list. 981 * @return An iterator for the current context list, as 982 * defined in XSLT. 983 */ 984 public final DTMIterator getContextNodes() 985 { 986 987 try 988 { 989 DTMIterator cnl = getContextNodeList(); 990 991 if (null != cnl) 992 return cnl.cloneWithReset(); 993 else 994 return null; // for now... this might ought to be an empty iterator. 995 } 996 catch (CloneNotSupportedException cnse) 997 { 998 return null; // error reporting? 999 } 1000 } 1001 1002 XPathExpressionContext expressionContext = new XPathExpressionContext(); 1003 1004 /** 1005 * The the expression context for extensions for this context. 1006 * 1007 * @return An object that implements the ExpressionContext. 1008 */ 1009 public ExpressionContext getExpressionContext() 1010 { 1011 return expressionContext; 1012 } 1013 1014 public class XPathExpressionContext implements ExpressionContext 1015 { 1016 /** 1017 * Return the XPathContext associated with this XPathExpressionContext. 1018 * Extensions should use this judiciously and only when special processing 1019 * requirements cannot be met another way. Consider requesting an enhancement 1020 * to the ExpressionContext interface to avoid having to call this method. 1021 * @return the XPathContext associated with this XPathExpressionContext. 1022 */ 1023 public XPathContext getXPathContext() 1024 { 1025 return XPathContext.this; 1026 } 1027 1028 /** 1029 * Return the DTMManager object. Though XPathContext context extends 1030 * the DTMManager, it really is a proxy for the real DTMManager. If a 1031 * caller needs to make a lot of calls to the DTMManager, it is faster 1032 * if it gets the real one from this function. 1033 */ 1034 public DTMManager getDTMManager() 1035 { 1036 return m_dtmManager; 1037 } 1038 1039 /** 1040 * Get the current context node. 1041 * @return The current context node. 1042 */ 1043 public org.w3c.dom.Node getContextNode() 1044 { 1045 int context = getCurrentNode(); 1046 1047 return getDTM(context).getNode(context); 1048 } 1049 1050 /** 1051 * Get the current context node list. 1052 * @return An iterator for the current context list, as 1053 * defined in XSLT. 1054 */ 1055 public org.w3c.dom.traversal.NodeIterator getContextNodes() 1056 { 1057 return new org.apache.xml.dtm.ref.DTMNodeIterator(getContextNodeList()); 1058 } 1059 1060 /** 1061 * Get the error listener. 1062 * @return The registered error listener. 1063 */ 1064 public ErrorListener getErrorListener() 1065 { 1066 return XPathContext.this.getErrorListener(); 1067 } 1068 1069 /** 1070 * Get the value of a node as a number. 1071 * @param n Node to be converted to a number. May be null. 1072 * @return value of n as a number. 1073 */ 1074 public double toNumber(org.w3c.dom.Node n) 1075 { 1076 // %REVIEW% You can't get much uglier than this... 1077 int nodeHandle = getDTMHandleFromNode(n); 1078 DTM dtm = getDTM(nodeHandle); 1079 XString xobj = (XString)dtm.getStringValue(nodeHandle); 1080 return xobj.num(); 1081 } 1082 1083 /** 1084 * Get the value of a node as a string. 1085 * @param n Node to be converted to a string. May be null. 1086 * @return value of n as a string, or an empty string if n is null. 1087 */ 1088 public String toString(org.w3c.dom.Node n) 1089 { 1090 // %REVIEW% You can't get much uglier than this... 1091 int nodeHandle = getDTMHandleFromNode(n); 1092 DTM dtm = getDTM(nodeHandle); 1093 XMLString strVal = dtm.getStringValue(nodeHandle); 1094 return strVal.toString(); 1095 } 1096 1097 /** 1098 * Get a variable based on it's qualified name. 1099 * @param qname The qualified name of the variable. 1100 * @return The evaluated value of the variable. 1101 * @throws javax.xml.transform.TransformerException 1102 */ 1103 1104 public final XObject getVariableOrParam(org.apache.xml.utils.QName qname) 1105 throws javax.xml.transform.TransformerException 1106 { 1107 return m_variableStacks.getVariableOrParam(XPathContext.this, qname); 1108 } 1109 1110 } 1111 1112 /** 1113 * Get a DTM to be used as a container for a global Result Tree 1114 * Fragment. This will always be an instance of (derived from? equivalent to?) 1115 * SAX2DTM, since each RTF is constructed by temporarily redirecting our SAX 1116 * output to it. It may be a single DTM containing for multiple fragments, 1117 * if the implementation supports that. 1118 * 1119 * Note: The distinction between this method and getRTFDTM() is that the latter 1120 * allocates space from the dynamic variable stack (m_rtfdtm_stack), which may 1121 * be pruned away again as the templates which defined those variables are exited. 1122 * Global variables may be bound late (see XUnresolvedVariable), and never want to 1123 * be discarded, hence we need to allocate them separately and don't actually need 1124 * a stack to track them. 1125 * 1126 * @return a non-null DTM reference. 1127 */ 1128 public DTM getGlobalRTFDTM() 1129 { 1130 // We probably should _NOT_ be applying whitespace filtering at this stage! 1131 // 1132 // Some magic has been applied in DTMManagerDefault to recognize this set of options 1133 // and generate an instance of DTM which can contain multiple documents 1134 // (SAX2RTFDTM). Perhaps not the optimal way of achieving that result, but 1135 // I didn't want to change the manager API at this time, or expose 1136 // too many dependencies on its internals. (Ideally, I'd like to move 1137 // isTreeIncomplete all the way up to DTM, so we wouldn't need to explicitly 1138 // specify the subclass here.) 1139 1140 // If it doesn't exist, or if the one already existing is in the middle of 1141 // being constructed, we need to obtain a new DTM to write into. I'm not sure 1142 // the latter will ever arise, but I'd rather be just a bit paranoid.. 1143 if( m_global_rtfdtm==null || m_global_rtfdtm.isTreeIncomplete() ) 1144 { 1145 m_global_rtfdtm=(SAX2RTFDTM)m_dtmManager.getDTM(null,true,null,false,false); 1146 } 1147 return m_global_rtfdtm; 1148 } 1149 1150 1151 1152 1153 /** 1154 * Get a DTM to be used as a container for a dynamic Result Tree 1155 * Fragment. This will always be an instance of (derived from? equivalent to?) 1156 * SAX2DTM, since each RTF is constructed by temporarily redirecting our SAX 1157 * output to it. It may be a single DTM containing for multiple fragments, 1158 * if the implementation supports that. 1159 * 1160 * @return a non-null DTM reference. 1161 */ 1162 public DTM getRTFDTM() 1163 { 1164 SAX2RTFDTM rtfdtm; 1165 1166 // We probably should _NOT_ be applying whitespace filtering at this stage! 1167 // 1168 // Some magic has been applied in DTMManagerDefault to recognize this set of options 1169 // and generate an instance of DTM which can contain multiple documents 1170 // (SAX2RTFDTM). Perhaps not the optimal way of achieving that result, but 1171 // I didn't want to change the manager API at this time, or expose 1172 // too many dependencies on its internals. (Ideally, I'd like to move 1173 // isTreeIncomplete all the way up to DTM, so we wouldn't need to explicitly 1174 // specify the subclass here.) 1175 1176 if(m_rtfdtm_stack==null) 1177 { 1178 m_rtfdtm_stack=new Vector(); 1179 rtfdtm=(SAX2RTFDTM)m_dtmManager.getDTM(null,true,null,false,false); 1180 m_rtfdtm_stack.addElement(rtfdtm); 1181 ++m_which_rtfdtm; 1182 } 1183 else if(m_which_rtfdtm<0) 1184 { 1185 rtfdtm=(SAX2RTFDTM)m_rtfdtm_stack.elementAt(++m_which_rtfdtm); 1186 } 1187 else 1188 { 1189 rtfdtm=(SAX2RTFDTM)m_rtfdtm_stack.elementAt(m_which_rtfdtm); 1190 1191 // It might already be under construction -- the classic example would be 1192 // an xsl:variable which uses xsl:call-template as part of its value. To 1193 // handle this recursion, we have to start a new RTF DTM, pushing the old 1194 // one onto a stack so we can return to it. This is not as uncommon a case 1195 // as we might wish, unfortunately, as some folks insist on coding XSLT 1196 // as if it were a procedural language... 1197 if(rtfdtm.isTreeIncomplete()) 1198 { 1199 if(++m_which_rtfdtm < m_rtfdtm_stack.size()) 1200 rtfdtm=(SAX2RTFDTM)m_rtfdtm_stack.elementAt(m_which_rtfdtm); 1201 else 1202 { 1203 rtfdtm=(SAX2RTFDTM)m_dtmManager.getDTM(null,true,null,false,false); 1204 m_rtfdtm_stack.addElement(rtfdtm); 1205 } 1206 } 1207 } 1208 1209 return rtfdtm; 1210 } 1211 1212 /** Push the RTFDTM's context mark, to allows discarding RTFs added after this 1213 * point. (If it doesn't exist we don't push, since we might still be able to 1214 * get away with not creating it. That requires that excessive pops be harmless.) 1215 * */ 1216 public void pushRTFContext() 1217 { 1218 m_last_pushed_rtfdtm.push(m_which_rtfdtm); 1219 if(null!=m_rtfdtm_stack) 1220 ((SAX2RTFDTM)(getRTFDTM())).pushRewindMark(); 1221 } 1222 1223 /** Pop the RTFDTM's context mark. This discards any RTFs added after the last 1224 * mark was set. 1225 * 1226 * If there is no RTF DTM, there's nothing to pop so this 1227 * becomes a no-op. If pushes were issued before this was called, we count on 1228 * the fact that popRewindMark is defined such that overpopping just resets 1229 * to empty. 1230 * 1231 * Complicating factor: We need to handle the case of popping back to a previous 1232 * RTF DTM, if one of the weird produce-an-RTF-to-build-an-RTF cases arose. 1233 * Basically: If pop says this DTM is now empty, then return to the previous 1234 * if one exists, in whatever state we left it in. UGLY, but hopefully the 1235 * situation which forces us to consider this will arise exceedingly rarely. 1236 * */ 1237 public void popRTFContext() 1238 { 1239 int previous=m_last_pushed_rtfdtm.pop(); 1240 if(null==m_rtfdtm_stack) 1241 return; 1242 1243 if(m_which_rtfdtm==previous) 1244 { 1245 if(previous>=0) // guard against none-active 1246 { 1247 boolean isEmpty=((SAX2RTFDTM)(m_rtfdtm_stack.elementAt(previous))).popRewindMark(); 1248 } 1249 } 1250 else while(m_which_rtfdtm!=previous) 1251 { 1252 // Empty each DTM before popping, so it's ready for reuse 1253 // _DON'T_ pop the previous, since it's still open (which is why we 1254 // stacked up more of these) and did not receive a mark. 1255 boolean isEmpty=((SAX2RTFDTM)(m_rtfdtm_stack.elementAt(m_which_rtfdtm))).popRewindMark(); 1256 --m_which_rtfdtm; 1257 } 1258 } 60 IntStack m_last_pushed_rtfdtm = new IntStack(); 61 62 /** 63 * Stack of cached "reusable" DTMs for Result Tree Fragments. 64 * This is a kluge to handle the problem of starting an RTF before 65 * the old one is complete. 66 * 67 * %REVIEW% I'm using a Vector rather than Stack so we can reuse 68 * the DTMs if the problem occurs multiple times. I'm not sure that's 69 * really a net win versus discarding the DTM and starting a new one... 70 * but the retained RTF DTM will have been tail-pruned so should be small. 71 */ 72 private Vector m_rtfdtm_stack = null; 73 74 /** Index of currently active RTF DTM in m_rtfdtm_stack */ 75 private int m_which_rtfdtm = -1; 76 77 /** 78 * Most recent "reusable" DTM for Global Result Tree Fragments. No stack is 79 * required since we're never going to pop these. 80 */ 81 private SAX2RTFDTM m_global_rtfdtm = null; 82 83 /** 84 * Though XPathContext context extends 85 * the DTMManager, it really is a proxy for this object, which 86 * is the real DTMManager. 87 */ 88 protected DTMManager m_dtmManager = DTMManager.newInstance(org.apache.xpath.objects.XMLStringFactoryImpl.getFactory()); 89 90 /** 91 * Return the DTMManager object. Though XPathContext context extends 92 * the DTMManager, it really is a proxy for the real DTMManager. If a 93 * caller needs to make a lot of calls to the DTMManager, it is faster 94 * if it gets the real one from this function. 95 */ 96 public DTMManager getDTMManager() { 97 return m_dtmManager; 98 } 99 100 /** 101 * Get an instance of a DTM, loaded with the content from the 102 * specified source. If the unique flag is true, a new instance will 103 * always be returned. Otherwise it is up to the DTMManager to return a 104 * new instance or an instance that it already created and may be being used 105 * by someone else. 106 * (I think more parameters will need to be added for error handling, and entity 107 * resolution). 108 * 109 * @param source the specification of the source object, which may be null, 110 * in which case it is assumed that node construction will take 111 * by some other means. 112 * @param unique true if the returned DTM must be unique, probably because it 113 * is going to be mutated. 114 * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may 115 * be null. 116 * @param incremental true if the construction should try and be incremental. 117 * @param doIndexing true if the caller considers it worth it to use 118 * indexing schemes. 119 * 120 * @return a non-null DTM reference. 121 */ 122 public DTM getDTM(javax.xml.transform.Source source, boolean unique, DTMWSFilter wsfilter, boolean incremental, boolean doIndexing) { 123 return m_dtmManager.getDTM(source, unique, wsfilter, incremental, doIndexing); 124 } 125 126 /** 127 * Get an instance of a DTM that "owns" a node handle. 128 * 129 * @param nodeHandle the nodeHandle. 130 * 131 * @return a non-null DTM reference. 132 */ 133 public DTM getDTM(int nodeHandle) { 134 return m_dtmManager.getDTM(nodeHandle); 135 } 136 137 /** 138 * Given a W3C DOM node, try and return a DTM handle. 139 * Note: calling this may be non-optimal. 140 * 141 * @param node Non-null reference to a DOM node. 142 * 143 * @return a valid DTM handle. 144 */ 145 public int getDTMHandleFromNode(org.w3c.dom.Node node) { 146 return m_dtmManager.getDTMHandleFromNode(node); 147 } 148 149 // 150 // 151 /** 152 * %TBD% Doc 153 */ 154 public int getDTMIdentity(DTM dtm) { 155 return m_dtmManager.getDTMIdentity(dtm); 156 } 157 158 // 159 /** 160 * Creates an empty <code>DocumentFragment</code> object. 161 * @return A new <code>DocumentFragment handle</code>. 162 */ 163 public DTM createDocumentFragment() { 164 return m_dtmManager.createDocumentFragment(); 165 } 166 167 // 168 /** 169 * Release a DTM either to a lru pool, or completely remove reference. 170 * DTMs without system IDs are always hard deleted. 171 * State: experimental. 172 * 173 * @param dtm The DTM to be released. 174 * @param shouldHardDelete True if the DTM should be removed no matter what. 175 * @return true if the DTM was removed, false if it was put back in a lru pool. 176 */ 177 public boolean release(DTM dtm, boolean shouldHardDelete) { 178 // %REVIEW% If it's a DTM which may contain multiple Result Tree 179 // Fragments, we can't discard it unless we know not only that it 180 // is empty, but that the XPathContext itself is going away. So do 181 // _not_ accept the request. (May want to do it as part of 182 // reset(), though.) 183 if (m_rtfdtm_stack != null && m_rtfdtm_stack.contains(dtm)) { return false; } 184 185 return m_dtmManager.release(dtm, shouldHardDelete); 186 } 187 188 /** 189 * Create a new <code>DTMIterator</code> based on an XPath 190 * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or 191 * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>. 192 * 193 * @param xpathCompiler ??? Somehow we need to pass in a subpart of the 194 * expression. I hate to do this with strings, since the larger expression 195 * has already been parsed. 196 * 197 * @param pos The position in the expression. 198 * @return The newly created <code>DTMIterator</code>. 199 */ 200 public DTMIterator createDTMIterator(Object xpathCompiler, int pos) { 201 return m_dtmManager.createDTMIterator(xpathCompiler, pos); 202 } 203 204 // 205 /** 206 * Create a new <code>DTMIterator</code> based on an XPath 207 * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or 208 * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>. 209 * 210 * @param xpathString Must be a valid string expressing a 211 * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or 212 * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>. 213 * 214 * @param presolver An object that can resolve prefixes to namespace URLs. 215 * 216 * @return The newly created <code>DTMIterator</code>. 217 */ 218 public DTMIterator createDTMIterator(String xpathString, PrefixResolver presolver) { 219 return m_dtmManager.createDTMIterator(xpathString, presolver); 220 } 221 222 // 223 /** 224 * Create a new <code>DTMIterator</code> based only on a whatToShow and 225 * a DTMFilter. The traversal semantics are defined as the descendant 226 * access. 227 * 228 * @param whatToShow This flag specifies which node types may appear in 229 * the logical view of the tree presented by the iterator. See the 230 * description of <code>NodeFilter</code> for the set of possible 231 * <code>SHOW_</code> values.These flags can be combined using 232 * <code>OR</code>. 233 * @param filter The <code>NodeFilter</code> to be used with this 234 * <code>TreeWalker</code>, or <code>null</code> to indicate no filter. 235 * @param entityReferenceExpansion The value of this flag determines 236 * whether entity reference nodes are expanded. 237 * 238 * @return The newly created <code>NodeIterator</code>. 239 */ 240 public DTMIterator createDTMIterator(int whatToShow, DTMFilter filter, boolean entityReferenceExpansion) { 241 return m_dtmManager.createDTMIterator(whatToShow, filter, entityReferenceExpansion); 242 } 243 244 /** 245 * Create a new <code>DTMIterator</code> that holds exactly one node. 246 * 247 * @param node The node handle that the DTMIterator will iterate to. 248 * 249 * @return The newly created <code>DTMIterator</code>. 250 */ 251 public DTMIterator createDTMIterator(int node) { 252 // DescendantIterator iter = new DescendantIterator(); 253 DTMIterator iter = new org.apache.xpath.axes.OneStepIteratorForward(Axis.SELF); 254 iter.setRoot(node, this); 255 return iter; 256 // return m_dtmManager.createDTMIterator(node); 257 } 258 259 /** 260 * Create an XPathContext instance. 261 */ 262 public XPathContext() { 263 m_prefixResolvers.push(null); 264 m_currentNodes.push(DTM.NULL); 265 m_currentExpressionNodes.push(DTM.NULL); 266 m_saxLocations.push(null); 267 } 268 269 /** 270 * Create an XPathContext instance. 271 * @param owner Value that can be retrieved via the getOwnerObject() method. 272 * @see #getOwnerObject 273 */ 274 public XPathContext(Object owner) { 275 m_owner = owner; 276 try { 277 m_ownerGetErrorListener = m_owner.getClass().getMethod("getErrorListener", new Class[] {}); 278 } catch (NoSuchMethodException nsme) { 279 } 280 m_prefixResolvers.push(null); 281 m_currentNodes.push(DTM.NULL); 282 m_currentExpressionNodes.push(DTM.NULL); 283 m_saxLocations.push(null); 284 } 285 286 /** 287 * Reset for new run. 288 */ 289 public void reset() { 290 // These couldn't be disposed of earlier (see comments in release()); zap them now. 291 if (m_rtfdtm_stack != null) for (java.util.Enumeration e = m_rtfdtm_stack.elements(); e.hasMoreElements();) 292 m_dtmManager.release((DTM) e.nextElement(), true); 293 294 m_rtfdtm_stack = null; // drop our references too 295 m_which_rtfdtm = -1; 296 297 if (m_global_rtfdtm != null) m_dtmManager.release(m_global_rtfdtm, true); 298 m_global_rtfdtm = null; 299 300 m_dtmManager = DTMManager.newInstance(org.apache.xpath.objects.XMLStringFactoryImpl.getFactory()); 301 302 m_saxLocations.removeAllElements(); 303 m_axesIteratorStack.removeAllElements(); 304 m_contextNodeLists.removeAllElements(); 305 m_currentExpressionNodes.removeAllElements(); 306 m_currentNodes.removeAllElements(); 307 m_iteratorRoots.RemoveAllNoClear(); 308 m_predicatePos.removeAllElements(); 309 m_predicateRoots.RemoveAllNoClear(); 310 m_prefixResolvers.removeAllElements(); 311 312 m_prefixResolvers.push(null); 313 m_currentNodes.push(DTM.NULL); 314 m_currentExpressionNodes.push(DTM.NULL); 315 m_saxLocations.push(null); 316 } 317 318 /** The current stylesheet locator. */ 319 ObjectStack m_saxLocations = new ObjectStack(RECURSIONLIMIT); 320 321 /** 322 * Set the current locater in the stylesheet. 323 * 324 * @param location The location within the stylesheet. 325 */ 326 public void setSAXLocator(SourceLocator location) { 327 m_saxLocations.setTop(location); 328 } 329 330 /** 331 * Set the current locater in the stylesheet. 332 * 333 * @param location The location within the stylesheet. 334 */ 335 public void pushSAXLocator(SourceLocator location) { 336 m_saxLocations.push(location); 337 } 338 339 /** 340 * Push a slot on the locations stack so that setSAXLocator can be 341 * repeatedly called. 342 * 343 * @param location The location within the stylesheet. 344 */ 345 public void pushSAXLocatorNull() { 346 m_saxLocations.push(null); 347 } 348 349 /** 350 * Pop the current locater. 351 */ 352 public void popSAXLocator() { 353 m_saxLocations.pop(); 354 } 355 356 /** 357 * Get the current locater in the stylesheet. 358 * 359 * @return The location within the stylesheet, or null if not known. 360 */ 361 public SourceLocator getSAXLocator() { 362 return (SourceLocator) m_saxLocations.peek(); 363 } 364 365 /** The owner context of this XPathContext. In the case of XSLT, this will be a 366 * Transformer object. 367 */ 368 private Object m_owner; 369 370 /** The owner context of this XPathContext. In the case of XSLT, this will be a 371 * Transformer object. 372 */ 373 private Method m_ownerGetErrorListener; 374 375 /** 376 * Get the "owner" context of this context, which should be, 377 * in the case of XSLT, the Transformer object. This is needed 378 * so that XSLT functions can get the Transformer. 379 * @return The owner object passed into the constructor, or null. 380 */ 381 public Object getOwnerObject() { 382 return m_owner; 383 } 384 385 // ================ VarStack =================== 386 387 /** 388 * The stack of Variable stacks. A VariableStack will be 389 * pushed onto this stack for each template invocation. 390 */ 391 private VariableStack m_variableStacks = new VariableStack(); 392 393 /** 394 * Get the variable stack, which is in charge of variables and 395 * parameters. 396 * 397 * @return the variable stack, which should not be null. 398 */ 399 public final VariableStack getVarStack() { 400 return m_variableStacks; 401 } 402 403 /** 404 * Get the variable stack, which is in charge of variables and 405 * parameters. 406 * 407 * @param varStack non-null reference to the variable stack. 408 */ 409 public final void setVarStack(VariableStack varStack) { 410 m_variableStacks = varStack; 411 } 412 413 // ================ SourceTreeManager =================== 414 415 /** The source tree manager, which associates Source objects to source 416 * tree nodes. */ 417 private SourceTreeManager m_sourceTreeManager = new SourceTreeManager(); 418 419 /** 420 * Get the SourceTreeManager associated with this execution context. 421 * 422 * @return the SourceTreeManager associated with this execution context. 423 */ 424 public final SourceTreeManager getSourceTreeManager() { 425 return m_sourceTreeManager; 426 } 427 428 /** 429 * Set the SourceTreeManager associated with this execution context. 430 * 431 * @param mgr the SourceTreeManager to be associated with this 432 * execution context. 433 */ 434 public void setSourceTreeManager(SourceTreeManager mgr) { 435 m_sourceTreeManager = mgr; 436 } 437 438 // ================================================= 439 440 /** The ErrorListener where errors and warnings are to be reported. */ 441 private ErrorListener m_errorListener; 442 443 /** A default ErrorListener in case our m_errorListener was not specified and our 444 * owner either does not have an ErrorListener or has a null one. 445 */ 446 private ErrorListener m_defaultErrorListener; 447 448 /** 449 * Get the ErrorListener where errors and warnings are to be reported. 450 * 451 * @return A non-null ErrorListener reference. 452 */ 453 public final ErrorListener getErrorListener() { 454 455 if (null != m_errorListener) return m_errorListener; 456 457 ErrorListener retval = null; 458 459 try { 460 if (null != m_ownerGetErrorListener) retval = (ErrorListener) m_ownerGetErrorListener.invoke(m_owner, new Object[] {}); 461 } catch (Exception e) { 462 } 463 464 if (null == retval) { 465 if (null == m_defaultErrorListener) m_defaultErrorListener = new org.apache.xml.utils.DefaultErrorHandler(); 466 retval = m_defaultErrorListener; 467 } 468 469 return retval; 470 } 471 472 /** 473 * Set the ErrorListener where errors and warnings are to be reported. 474 * 475 * @param listener A non-null ErrorListener reference. 476 */ 477 public void setErrorListener(ErrorListener listener) throws IllegalArgumentException { 478 if (listener == null) throw new IllegalArgumentException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, null)); //"Null error handler"); 479 m_errorListener = listener; 480 } 481 482 // ================================================= 483 484 /** The TrAX URI Resolver for resolving URIs from the document(...) 485 * function to source tree nodes. */ 486 private URIResolver m_uriResolver; 487 488 /** 489 * Get the URIResolver associated with this execution context. 490 * 491 * @return a URI resolver, which may be null. 492 */ 493 public final URIResolver getURIResolver() { 494 return m_uriResolver; 495 } 496 497 /** 498 * Set the URIResolver associated with this execution context. 499 * 500 * @param resolver the URIResolver to be associated with this 501 * execution context, may be null to clear an already set resolver. 502 */ 503 public void setURIResolver(URIResolver resolver) { 504 m_uriResolver = resolver; 505 } 506 507 // ================================================= 508 509 /** The reader of the primary source tree. */ 510 public XMLReader m_primaryReader; 511 512 /** 513 * Get primary XMLReader associated with this execution context. 514 * 515 * @return The reader of the primary source tree. 516 */ 517 public final XMLReader getPrimaryReader() { 518 return m_primaryReader; 519 } 520 521 /** 522 * Set primary XMLReader associated with this execution context. 523 * 524 * @param reader The reader of the primary source tree. 525 */ 526 public void setPrimaryReader(XMLReader reader) { 527 m_primaryReader = reader; 528 } 529 530 // ================================================= 531 532 /** Misnamed string manager for XPath messages. */ 533 // private static XSLMessages m_XSLMessages = new XSLMessages(); 534 /** 535 * Tell the user of an assertion error, and probably throw an 536 * exception. 537 * 538 * @param b If false, a TransformerException will be thrown. 539 * @param msg The assertion message, which should be informative. 540 * 541 * @throws javax.xml.transform.TransformerException if b is false. 542 */ 543 private void assertion(boolean b, String msg) throws javax.xml.transform.TransformerException { 544 if (!b) { 545 ErrorListener errorHandler = getErrorListener(); 546 547 if (errorHandler != null) { 548 errorHandler.fatalError(new TransformerException(XSLMessages.createMessage(XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION, new Object[] { msg }), (SAXSourceLocator) this.getSAXLocator())); 549 } 550 } 551 } 552 553 //========================================================== 554 // SECTION: Execution context state tracking 555 //========================================================== 556 557 /** 558 * The current context node list. 559 */ 560 private Stack m_contextNodeLists = new Stack(); 561 562 public Stack getContextNodeListsStack() { 563 return m_contextNodeLists; 564 } 565 566 public void setContextNodeListsStack(Stack s) { 567 m_contextNodeLists = s; 568 } 569 570 /** 571 * Get the current context node list. 572 * 573 * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>, 574 * also refered to here as a <term>context node list</term>. 575 */ 576 public final DTMIterator getContextNodeList() { 577 578 if (m_contextNodeLists.size() > 0) 579 return (DTMIterator) m_contextNodeLists.peek(); 580 else return null; 581 } 582 583 /** 584 * Set the current context node list. 585 * 586 * @param nl the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>, 587 * also refered to here as a <term>context node list</term>. 588 * @xsl.usage internal 589 */ 590 public final void pushContextNodeList(DTMIterator nl) { 591 m_contextNodeLists.push(nl); 592 } 593 594 /** 595 * Pop the current context node list. 596 * @xsl.usage internal 597 */ 598 public final void popContextNodeList() { 599 if (m_contextNodeLists.isEmpty()) 600 System.err.println("Warning: popContextNodeList when stack is empty!"); 601 else m_contextNodeLists.pop(); 602 } 603 604 /** 605 * The ammount to use for stacks that record information during the 606 * recursive execution. 607 */ 608 public static final int RECURSIONLIMIT = (1024 * 4); 609 610 /** The stack of <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a> objects. 611 * Not to be confused with the current node list. %REVIEW% Note that there 612 * are no bounds check and resize for this stack, so if it is blown, it's all 613 * over. */ 614 private IntStack m_currentNodes = new IntStack(RECURSIONLIMIT); 615 616 // private NodeVector m_currentNodes = new NodeVector(); 617 618 public IntStack getCurrentNodeStack() { 619 return m_currentNodes; 620 } 621 622 public void setCurrentNodeStack(IntStack nv) { 623 m_currentNodes = nv; 624 } 625 626 /** 627 * Get the current context node. 628 * 629 * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>. 630 */ 631 public final int getCurrentNode() { 632 return m_currentNodes.peek(); 633 } 634 635 /** 636 * Set the current context node and expression node. 637 * 638 * @param cn the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>. 639 * @param en the sub-expression context node. 640 */ 641 public final void pushCurrentNodeAndExpression(int cn, int en) { 642 m_currentNodes.push(cn); 643 m_currentExpressionNodes.push(cn); 644 } 645 646 /** 647 * Set the current context node. 648 */ 649 public final void popCurrentNodeAndExpression() { 650 m_currentNodes.quickPop(1); 651 m_currentExpressionNodes.quickPop(1); 652 } 653 654 /** 655 * Push the current context node, expression node, and prefix resolver. 656 * 657 * @param cn the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>. 658 * @param en the sub-expression context node. 659 * @param nc the namespace context (prefix resolver. 660 */ 661 public final void pushExpressionState(int cn, int en, PrefixResolver nc) { 662 m_currentNodes.push(cn); 663 m_currentExpressionNodes.push(cn); 664 m_prefixResolvers.push(nc); 665 } 666 667 /** 668 * Pop the current context node, expression node, and prefix resolver. 669 */ 670 public final void popExpressionState() { 671 m_currentNodes.quickPop(1); 672 m_currentExpressionNodes.quickPop(1); 673 m_prefixResolvers.pop(); 674 } 675 676 /** 677 * Set the current context node. 678 * 679 * @param n the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>. 680 */ 681 public final void pushCurrentNode(int n) { 682 m_currentNodes.push(n); 683 } 684 685 /** 686 * Pop the current context node. 687 */ 688 public final void popCurrentNode() { 689 m_currentNodes.quickPop(1); 690 } 691 692 /** 693 * Set the current predicate root. 694 */ 695 public final void pushPredicateRoot(int n) { 696 m_predicateRoots.push(n); 697 } 698 699 /** 700 * Pop the current predicate root. 701 */ 702 public final void popPredicateRoot() { 703 m_predicateRoots.popQuick(); 704 } 705 706 /** 707 * Get the current predicate root. 708 */ 709 public final int getPredicateRoot() { 710 return m_predicateRoots.peepOrNull(); 711 } 712 713 /** 714 * Set the current location path iterator root. 715 */ 716 public final void pushIteratorRoot(int n) { 717 m_iteratorRoots.push(n); 718 } 719 720 /** 721 * Pop the current location path iterator root. 722 */ 723 public final void popIteratorRoot() { 724 m_iteratorRoots.popQuick(); 725 } 726 727 /** 728 * Get the current location path iterator root. 729 */ 730 public final int getIteratorRoot() { 731 return m_iteratorRoots.peepOrNull(); 732 } 733 734 /** A stack of the current sub-expression nodes. */ 735 private NodeVector m_iteratorRoots = new NodeVector(); 736 737 /** A stack of the current sub-expression nodes. */ 738 private NodeVector m_predicateRoots = new NodeVector(); 739 740 /** A stack of the current sub-expression nodes. */ 741 private IntStack m_currentExpressionNodes = new IntStack(RECURSIONLIMIT); 742 743 public IntStack getCurrentExpressionNodeStack() { 744 return m_currentExpressionNodes; 745 } 746 747 public void setCurrentExpressionNodeStack(IntStack nv) { 748 m_currentExpressionNodes = nv; 749 } 750 751 private IntStack m_predicatePos = new IntStack(); 752 753 public final int getPredicatePos() { 754 return m_predicatePos.peek(); 755 } 756 757 public final void pushPredicatePos(int n) { 758 m_predicatePos.push(n); 759 } 760 761 public final void popPredicatePos() { 762 m_predicatePos.pop(); 763 } 764 765 /** 766 * Get the current node that is the expression's context (i.e. for current() support). 767 * 768 * @return The current sub-expression node. 769 */ 770 public final int getCurrentExpressionNode() { 771 return m_currentExpressionNodes.peek(); 772 } 773 774 /** 775 * Set the current node that is the expression's context (i.e. for current() support). 776 * 777 * @param n The sub-expression node to be current. 778 */ 779 public final void pushCurrentExpressionNode(int n) { 780 m_currentExpressionNodes.push(n); 781 } 782 783 /** 784 * Pop the current node that is the expression's context 785 * (i.e. for current() support). 786 */ 787 public final void popCurrentExpressionNode() { 788 m_currentExpressionNodes.quickPop(1); 789 } 790 791 private ObjectStack m_prefixResolvers = new ObjectStack(RECURSIONLIMIT); 792 793 /** 794 * Get the current namespace context for the xpath. 795 * 796 * @return the current prefix resolver for resolving prefixes to 797 * namespace URLs. 798 */ 799 public final PrefixResolver getNamespaceContext() { 800 return (PrefixResolver) m_prefixResolvers.peek(); 801 } 802 803 /** 804 * Get the current namespace context for the xpath. 805 * 806 * @param pr the prefix resolver to be used for resolving prefixes to 807 * namespace URLs. 808 */ 809 public final void setNamespaceContext(PrefixResolver pr) { 810 m_prefixResolvers.setTop(pr); 811 } 812 813 /** 814 * Push a current namespace context for the xpath. 815 * 816 * @param pr the prefix resolver to be used for resolving prefixes to 817 * namespace URLs. 818 */ 819 public final void pushNamespaceContext(PrefixResolver pr) { 820 m_prefixResolvers.push(pr); 821 } 822 823 /** 824 * Just increment the namespace contest stack, so that setNamespaceContext 825 * can be used on the slot. 826 */ 827 public final void pushNamespaceContextNull() { 828 m_prefixResolvers.push(null); 829 } 830 831 /** 832 * Pop the current namespace context for the xpath. 833 */ 834 public final void popNamespaceContext() { 835 m_prefixResolvers.pop(); 836 } 837 838 //========================================================== 839 // SECTION: Current TreeWalker contexts (for internal use) 840 //========================================================== 841 842 /** 843 * Stack of AxesIterators. 844 */ 845 private Stack m_axesIteratorStack = new Stack(); 846 847 public Stack getAxesIteratorStackStacks() { 848 return m_axesIteratorStack; 849 } 850 851 public void setAxesIteratorStackStacks(Stack s) { 852 m_axesIteratorStack = s; 853 } 854 855 /** 856 * Push a TreeWalker on the stack. 857 * 858 * @param iter A sub-context AxesWalker. 859 * @xsl.usage internal 860 */ 861 public final void pushSubContextList(SubContextList iter) { 862 m_axesIteratorStack.push(iter); 863 } 864 865 /** 866 * Pop the last pushed axes iterator. 867 * @xsl.usage internal 868 */ 869 public final void popSubContextList() { 870 m_axesIteratorStack.pop(); 871 } 872 873 /** 874 * Get the current axes iterator, or return null if none. 875 * 876 * @return the sub-context node list. 877 * @xsl.usage internal 878 */ 879 public SubContextList getSubContextList() { 880 return m_axesIteratorStack.isEmpty() ? null : (SubContextList) m_axesIteratorStack.peek(); 881 } 882 883 /** 884 * Get the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a> 885 * as defined by the XSLT spec. 886 * 887 * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>. 888 * @xsl.usage internal 889 */ 890 891 public org.apache.xpath.axes.SubContextList getCurrentNodeList() { 892 return m_axesIteratorStack.isEmpty() ? null : (SubContextList) m_axesIteratorStack.elementAt(0); 893 } 894 895 //========================================================== 896 // SECTION: Implementation of ExpressionContext interface 897 //========================================================== 898 899 /** 900 * Get the current context node. 901 * @return The current context node. 902 */ 903 public final int getContextNode() { 904 return this.getCurrentNode(); 905 } 906 907 /** 908 * Get the current context node list. 909 * @return An iterator for the current context list, as 910 * defined in XSLT. 911 */ 912 public final DTMIterator getContextNodes() { 913 914 try { 915 DTMIterator cnl = getContextNodeList(); 916 917 if (null != cnl) 918 return cnl.cloneWithReset(); 919 else return null; // for now... this might ought to be an empty iterator. 920 } catch (CloneNotSupportedException cnse) { 921 return null; // error reporting? 922 } 923 } 924 925 XPathExpressionContext expressionContext = new XPathExpressionContext(); 926 927 /** 928 * The the expression context for extensions for this context. 929 * 930 * @return An object that implements the ExpressionContext. 931 */ 932 public ExpressionContext getExpressionContext() { 933 return expressionContext; 934 } 935 936 public class XPathExpressionContext implements ExpressionContext { 937 /** 938 * Return the XPathContext associated with this XPathExpressionContext. 939 * Extensions should use this judiciously and only when special processing 940 * requirements cannot be met another way. Consider requesting an enhancement 941 * to the ExpressionContext interface to avoid having to call this method. 942 * @return the XPathContext associated with this XPathExpressionContext. 943 */ 944 public XPathContext getXPathContext() { 945 return XPathContext.this; 946 } 947 948 /** 949 * Return the DTMManager object. Though XPathContext context extends 950 * the DTMManager, it really is a proxy for the real DTMManager. If a 951 * caller needs to make a lot of calls to the DTMManager, it is faster 952 * if it gets the real one from this function. 953 */ 954 public DTMManager getDTMManager() { 955 return m_dtmManager; 956 } 957 958 /** 959 * Get the current context node. 960 * @return The current context node. 961 */ 962 public org.w3c.dom.Node getContextNode() { 963 int context = getCurrentNode(); 964 965 return getDTM(context).getNode(context); 966 } 967 968 /** 969 * Get the current context node list. 970 * @return An iterator for the current context list, as 971 * defined in XSLT. 972 */ 973 public org.w3c.dom.traversal.NodeIterator getContextNodes() { 974 return new org.apache.xml.dtm.ref.DTMNodeIterator(getContextNodeList()); 975 } 976 977 /** 978 * Get the error listener. 979 * @return The registered error listener. 980 */ 981 public ErrorListener getErrorListener() { 982 return XPathContext.this.getErrorListener(); 983 } 984 985 /** 986 * Get the value of a node as a number. 987 * @param n Node to be converted to a number. May be null. 988 * @return value of n as a number. 989 */ 990 public double toNumber(org.w3c.dom.Node n) { 991 // %REVIEW% You can't get much uglier than this... 992 int nodeHandle = getDTMHandleFromNode(n); 993 DTM dtm = getDTM(nodeHandle); 994 XString xobj = (XString) dtm.getStringValue(nodeHandle); 995 return xobj.num(); 996 } 997 998 /** 999 * Get the value of a node as a string. 1000 * @param n Node to be converted to a string. May be null. 1001 * @return value of n as a string, or an empty string if n is null. 1002 */ 1003 public String toString(org.w3c.dom.Node n) { 1004 // %REVIEW% You can't get much uglier than this... 1005 int nodeHandle = getDTMHandleFromNode(n); 1006 DTM dtm = getDTM(nodeHandle); 1007 XMLString strVal = dtm.getStringValue(nodeHandle); 1008 return strVal.toString(); 1009 } 1010 1011 /** 1012 * Get a variable based on it's qualified name. 1013 * @param qname The qualified name of the variable. 1014 * @return The evaluated value of the variable. 1015 * @throws javax.xml.transform.TransformerException 1016 */ 1017 1018 public final XObject getVariableOrParam(org.apache.xml.utils.QName qname) throws javax.xml.transform.TransformerException { 1019 return m_variableStacks.getVariableOrParam(XPathContext.this, qname); 1020 } 1021 1022 } 1023 1024 /** 1025 * Get a DTM to be used as a container for a global Result Tree 1026 * Fragment. This will always be an instance of (derived from? equivalent to?) 1027 * SAX2DTM, since each RTF is constructed by temporarily redirecting our SAX 1028 * output to it. It may be a single DTM containing for multiple fragments, 1029 * if the implementation supports that. 1030 * 1031 * Note: The distinction between this method and getRTFDTM() is that the latter 1032 * allocates space from the dynamic variable stack (m_rtfdtm_stack), which may 1033 * be pruned away again as the templates which defined those variables are exited. 1034 * Global variables may be bound late (see XUnresolvedVariable), and never want to 1035 * be discarded, hence we need to allocate them separately and don't actually need 1036 * a stack to track them. 1037 * 1038 * @return a non-null DTM reference. 1039 */ 1040 public DTM getGlobalRTFDTM() { 1041 // We probably should _NOT_ be applying whitespace filtering at this stage! 1042 // 1043 // Some magic has been applied in DTMManagerDefault to recognize this set of options 1044 // and generate an instance of DTM which can contain multiple documents 1045 // (SAX2RTFDTM). Perhaps not the optimal way of achieving that result, but 1046 // I didn't want to change the manager API at this time, or expose 1047 // too many dependencies on its internals. (Ideally, I'd like to move 1048 // isTreeIncomplete all the way up to DTM, so we wouldn't need to explicitly 1049 // specify the subclass here.) 1050 1051 // If it doesn't exist, or if the one already existing is in the middle of 1052 // being constructed, we need to obtain a new DTM to write into. I'm not sure 1053 // the latter will ever arise, but I'd rather be just a bit paranoid.. 1054 if (m_global_rtfdtm == null || m_global_rtfdtm.isTreeIncomplete()) { 1055 m_global_rtfdtm = (SAX2RTFDTM) m_dtmManager.getDTM(null, true, null, false, false); 1056 } 1057 return m_global_rtfdtm; 1058 } 1059 1060 /** 1061 * Get a DTM to be used as a container for a dynamic Result Tree 1062 * Fragment. This will always be an instance of (derived from? equivalent to?) 1063 * SAX2DTM, since each RTF is constructed by temporarily redirecting our SAX 1064 * output to it. It may be a single DTM containing for multiple fragments, 1065 * if the implementation supports that. 1066 * 1067 * @return a non-null DTM reference. 1068 */ 1069 public DTM getRTFDTM() { 1070 SAX2RTFDTM rtfdtm; 1071 1072 // We probably should _NOT_ be applying whitespace filtering at this stage! 1073 // 1074 // Some magic has been applied in DTMManagerDefault to recognize this set of options 1075 // and generate an instance of DTM which can contain multiple documents 1076 // (SAX2RTFDTM). Perhaps not the optimal way of achieving that result, but 1077 // I didn't want to change the manager API at this time, or expose 1078 // too many dependencies on its internals. (Ideally, I'd like to move 1079 // isTreeIncomplete all the way up to DTM, so we wouldn't need to explicitly 1080 // specify the subclass here.) 1081 1082 if (m_rtfdtm_stack == null) { 1083 m_rtfdtm_stack = new Vector(); 1084 rtfdtm = (SAX2RTFDTM) m_dtmManager.getDTM(null, true, null, false, false); 1085 m_rtfdtm_stack.addElement(rtfdtm); 1086 ++m_which_rtfdtm; 1087 } else if (m_which_rtfdtm < 0) { 1088 rtfdtm = (SAX2RTFDTM) m_rtfdtm_stack.elementAt(++m_which_rtfdtm); 1089 } else { 1090 rtfdtm = (SAX2RTFDTM) m_rtfdtm_stack.elementAt(m_which_rtfdtm); 1091 1092 // It might already be under construction -- the classic example would be 1093 // an xsl:variable which uses xsl:call-template as part of its value. To 1094 // handle this recursion, we have to start a new RTF DTM, pushing the old 1095 // one onto a stack so we can return to it. This is not as uncommon a case 1096 // as we might wish, unfortunately, as some folks insist on coding XSLT 1097 // as if it were a procedural language... 1098 if (rtfdtm.isTreeIncomplete()) { 1099 if (++m_which_rtfdtm < m_rtfdtm_stack.size()) 1100 rtfdtm = (SAX2RTFDTM) m_rtfdtm_stack.elementAt(m_which_rtfdtm); 1101 else { 1102 rtfdtm = (SAX2RTFDTM) m_dtmManager.getDTM(null, true, null, false, false); 1103 m_rtfdtm_stack.addElement(rtfdtm); 1104 } 1105 } 1106 } 1107 1108 return rtfdtm; 1109 } 1110 1111 /** Push the RTFDTM's context mark, to allows discarding RTFs added after this 1112 * point. (If it doesn't exist we don't push, since we might still be able to 1113 * get away with not creating it. That requires that excessive pops be harmless.) 1114 * */ 1115 public void pushRTFContext() { 1116 m_last_pushed_rtfdtm.push(m_which_rtfdtm); 1117 if (null != m_rtfdtm_stack) ((SAX2RTFDTM) (getRTFDTM())).pushRewindMark(); 1118 } 1119 1120 /** Pop the RTFDTM's context mark. This discards any RTFs added after the last 1121 * mark was set. 1122 * 1123 * If there is no RTF DTM, there's nothing to pop so this 1124 * becomes a no-op. If pushes were issued before this was called, we count on 1125 * the fact that popRewindMark is defined such that overpopping just resets 1126 * to empty. 1127 * 1128 * Complicating factor: We need to handle the case of popping back to a previous 1129 * RTF DTM, if one of the weird produce-an-RTF-to-build-an-RTF cases arose. 1130 * Basically: If pop says this DTM is now empty, then return to the previous 1131 * if one exists, in whatever state we left it in. UGLY, but hopefully the 1132 * situation which forces us to consider this will arise exceedingly rarely. 1133 * */ 1134 public void popRTFContext() { 1135 int previous = m_last_pushed_rtfdtm.pop(); 1136 if (null == m_rtfdtm_stack) return; 1137 1138 if (m_which_rtfdtm == previous) { 1139 if (previous >= 0) // guard against none-active 1140 { 1141 ((SAX2RTFDTM) (m_rtfdtm_stack.elementAt(previous))).popRewindMark(); 1142 } 1143 } else while (m_which_rtfdtm != previous) { 1144 // Empty each DTM before popping, so it's ready for reuse 1145 // _DON'T_ pop the previous, since it's still open (which is why we 1146 // stacked up more of these) and did not receive a mark. 1147 ((SAX2RTFDTM) (m_rtfdtm_stack.elementAt(m_which_rtfdtm))).popRewindMark(); 1148 --m_which_rtfdtm; 1149 } 1150 } 1259 1151 }
Note: See TracChangeset
for help on using the changeset viewer.