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