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