Home | History | Annotate | Download | only in xpath
      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    */
    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