1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the  "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18/*
19 * $Id: XPathContext.java 524809 2007-04-02 15:51:51Z zongaro $
20 */
21package org.apache.xpath;
22
23import java.lang.reflect.Method;
24import java.util.Stack;
25import java.util.Vector;
26import java.util.HashMap;
27import java.util.Iterator;
28
29import javax.xml.transform.ErrorListener;
30import javax.xml.transform.SourceLocator;
31import javax.xml.transform.TransformerException;
32import javax.xml.transform.URIResolver;
33
34import org.apache.xalan.extensions.ExpressionContext;
35import org.apache.xalan.res.XSLMessages;
36import org.apache.xml.dtm.Axis;
37import org.apache.xml.dtm.DTM;
38import org.apache.xml.dtm.DTMFilter;
39import org.apache.xml.dtm.DTMIterator;
40import org.apache.xml.dtm.DTMManager;
41import org.apache.xml.dtm.DTMWSFilter;
42import org.apache.xml.dtm.ref.sax2dtm.SAX2RTFDTM;
43import org.apache.xml.utils.IntStack;
44import org.apache.xml.utils.NodeVector;
45import org.apache.xml.utils.ObjectStack;
46import org.apache.xml.utils.PrefixResolver;
47import org.apache.xml.utils.SAXSourceLocator;
48import org.apache.xml.utils.XMLString;
49import org.apache.xpath.axes.SubContextList;
50import org.apache.xpath.objects.XObject;
51import org.apache.xpath.objects.DTMXRTreeFrag;
52import org.apache.xpath.objects.XString;
53import org.apache.xpath.res.XPATHErrorResources;
54
55import org.xml.sax.XMLReader;
56
57/**
58 * Default class for the runtime execution context for XPath.
59 *
60 * <p>This class extends DTMManager but does not directly implement it.</p>
61 * @xsl.usage advanced
62 */
63public class XPathContext extends DTMManager // implements ExpressionContext
64{
65	IntStack m_last_pushed_rtfdtm=new IntStack();
66  /**
67   * Stack of cached "reusable" DTMs for Result Tree Fragments.
68   * This is a kluge to handle the problem of starting an RTF before
69   * the old one is complete.
70   *
71   * %REVIEW% I'm using a Vector rather than Stack so we can reuse
72   * the DTMs if the problem occurs multiple times. I'm not sure that's
73   * really a net win versus discarding the DTM and starting a new one...
74   * but the retained RTF DTM will have been tail-pruned so should be small.
75   */
76  private Vector m_rtfdtm_stack=null;
77  /** Index of currently active RTF DTM in m_rtfdtm_stack */
78  private int m_which_rtfdtm=-1;
79
80 /**
81   * Most recent "reusable" DTM for Global Result Tree Fragments. No stack is
82   * required since we're never going to pop these.
83   */
84  private SAX2RTFDTM m_global_rtfdtm=null;
85
86  /**
87   * HashMap of cached the DTMXRTreeFrag objects, which are identified by DTM IDs.
88   * The object are just wrappers for DTMs which are used in  XRTreeFrag.
89   */
90  private HashMap m_DTMXRTreeFrags = null;
91
92  /**
93   * state of the secure processing feature.
94   */
95  private boolean m_isSecureProcessing = false;
96
97  /**
98   * Though XPathContext context extends
99   * the DTMManager, it really is a proxy for this object, which
100   * is the real DTMManager.
101   */
102  protected DTMManager m_dtmManager = DTMManager.newInstance(
103                   org.apache.xpath.objects.XMLStringFactoryImpl.getFactory());
104
105  /**
106   * Return the DTMManager object.  Though XPathContext context extends
107   * the DTMManager, it really is a proxy for the real DTMManager.  If a
108   * caller needs to make a lot of calls to the DTMManager, it is faster
109   * if it gets the real one from this function.
110   */
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}
1353