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