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