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 }