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