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: VariableStack.java 524812 2007-04-02 15:52:03Z zongaro $
     20  */
     21 package org.apache.xpath;
     22 
     23 import javax.xml.transform.TransformerException;
     24 
     25 import org.apache.xalan.res.XSLMessages;
     26 import org.apache.xpath.objects.XObject;
     27 import org.apache.xpath.res.XPATHErrorResources;
     28 
     29 /**
     30  * Defines a class to keep track of a stack for
     31  * template arguments and variables.
     32  *
     33  * <p>This has been changed from the previous incarnations of this
     34  * class to be fairly low level.</p>
     35  * @xsl.usage internal
     36  */
     37 public class VariableStack implements Cloneable
     38 {
     39   /**
     40    * limitation for 1K
     41    */
     42   public static final int CLEARLIMITATION= 1024;
     43 
     44   /**
     45    * Constructor for a variable stack.
     46    */
     47   public VariableStack()
     48   {
     49     reset();
     50   }
     51 
     52   /**
     53    * Constructor for a variable stack.
     54    * @param initStackSize The initial stack size.  Must be at least one.  The
     55    *                      stack can grow if needed.
     56    */
     57   public VariableStack(int initStackSize)
     58   {
     59     // Allow for twice as many variables as stack link entries
     60     reset(initStackSize, initStackSize*2);
     61   }
     62 
     63   /**
     64    * Returns a clone of this variable stack.
     65    *
     66    * @return  a clone of this variable stack.
     67    *
     68    * @throws CloneNotSupportedException
     69    */
     70   public synchronized Object clone() throws CloneNotSupportedException
     71   {
     72 
     73     VariableStack vs = (VariableStack) super.clone();
     74 
     75     // I *think* I can get away with a shallow clone here?
     76     vs._stackFrames = (XObject[]) _stackFrames.clone();
     77     vs._links = (int[]) _links.clone();
     78 
     79     return vs;
     80   }
     81 
     82   /**
     83    * The stack frame where all variables and params will be kept.
     84    * @serial
     85    */
     86   XObject[] _stackFrames;
     87 
     88   /**
     89    * The top of the stack frame (<code>_stackFrames</code>).
     90    * @serial
     91    */
     92   int _frameTop;
     93 
     94   /**
     95    * The bottom index of the current frame (relative to <code>_stackFrames</code>).
     96    * @serial
     97    */
     98   private int _currentFrameBottom;
     99 
    100   /**
    101    * The stack of frame positions.  I call 'em links because of distant
    102    * <a href="http://math.millikin.edu/mprogers/Courses/currentCourses/CS481-ComputerArchitecture/cs481.Motorola68000.html">
    103    * Motorola 68000 assembler</a> memories.  :-)
    104    * @serial
    105    */
    106   int[] _links;
    107 
    108   /**
    109    * The top of the links stack.
    110    */
    111   int _linksTop;
    112 
    113   /**
    114    * Get the element at the given index, regardless of stackframe.
    115    *
    116    * @param i index from zero.
    117    *
    118    * @return The item at the given index.
    119    */
    120   public XObject elementAt(final int i)
    121   {
    122     return _stackFrames[i];
    123   }
    124 
    125   /**
    126    * Get size of the stack.
    127    *
    128    * @return the total size of the execution stack.
    129    */
    130   public int size()
    131   {
    132     return _frameTop;
    133   }
    134 
    135   /**
    136    * Reset the stack to a start position.
    137    */
    138   public void reset()
    139   {
    140     // If the stack was previously allocated, assume that about the same
    141     // amount of stack space will be needed again; otherwise, use a very
    142     // large stack size.
    143     int linksSize = (_links == null) ? XPathContext.RECURSIONLIMIT
    144                                      : _links.length;
    145     int varArraySize = (_stackFrames == null) ? XPathContext.RECURSIONLIMIT * 2
    146                                               : _stackFrames.length;
    147     reset(linksSize, varArraySize);
    148   }
    149 
    150   /**
    151    * Reset the stack to a start position.
    152    * @param linksSize Initial stack size to use
    153    * @param varArraySize Initial variable array size to use
    154    */
    155   protected void reset(int linksSize, int varArraySize) {
    156     _frameTop = 0;
    157     _linksTop = 0;
    158 
    159     // Don't bother reallocating _links array if it exists already
    160     if (_links == null) {
    161       _links = new int[linksSize];
    162     }
    163 
    164     // Adding one here to the stack of frame positions will allow us always
    165     // to look one under without having to check if we're at zero.
    166     // (As long as the caller doesn't screw up link/unlink.)
    167     _links[_linksTop++] = 0;
    168 
    169     // Get a clean _stackFrames array and discard the old one.
    170     _stackFrames = new XObject[varArraySize];
    171   }
    172 
    173   /**
    174    * Set the current stack frame.
    175    *
    176    * @param sf The new stack frame position.
    177    */
    178   public void setStackFrame(int sf)
    179   {
    180     _currentFrameBottom = sf;
    181   }
    182 
    183   /**
    184    * Get the position from where the search should start,
    185    * which is either the searchStart property, or the top
    186    * of the stack if that value is -1.
    187    *
    188    * @return The current stack frame position.
    189    */
    190   public int getStackFrame()
    191   {
    192     return _currentFrameBottom;
    193   }
    194 
    195   /**
    196    * Allocates memory (called a stackframe) on the stack; used to store
    197    * local variables and parameter arguments.
    198    *
    199    * <p>I use the link/unlink concept because of distant
    200    * <a href="http://math.millikin.edu/mprogers/Courses/currentCourses/CS481-ComputerArchitecture/cs481.Motorola68000.html">
    201    * Motorola 68000 assembler</a> memories.</p>
    202    *
    203    * @param size The size of the stack frame allocation.  This ammount should
    204    * normally be the maximum number of variables that you can have allocated
    205    * at one time in the new stack frame.
    206    *
    207    * @return The bottom of the stack frame, from where local variable addressing
    208    * should start from.
    209    */
    210   public int link(final int size)
    211   {
    212 
    213     _currentFrameBottom = _frameTop;
    214     _frameTop += size;
    215 
    216     if (_frameTop >= _stackFrames.length)
    217     {
    218       XObject newsf[] = new XObject[_stackFrames.length + XPathContext.RECURSIONLIMIT + size];
    219 
    220       System.arraycopy(_stackFrames, 0, newsf, 0, _stackFrames.length);
    221 
    222       _stackFrames = newsf;
    223     }
    224 
    225     if (_linksTop + 1 >= _links.length)
    226     {
    227       int newlinks[] = new int[_links.length + (CLEARLIMITATION * 2)];
    228 
    229       System.arraycopy(_links, 0, newlinks, 0, _links.length);
    230 
    231       _links = newlinks;
    232     }
    233 
    234     _links[_linksTop++] = _currentFrameBottom;
    235 
    236     return _currentFrameBottom;
    237   }
    238 
    239   /**
    240    * Free up the stack frame that was last allocated with
    241    * {@link #link(int size)}.
    242    */
    243   public  void unlink()
    244   {
    245     _frameTop = _links[--_linksTop];
    246     _currentFrameBottom = _links[_linksTop - 1];
    247   }
    248 
    249   /**
    250    * Free up the stack frame that was last allocated with
    251    * {@link #link(int size)}.
    252    * @param currentFrame The current frame to set to
    253    * after the unlink.
    254    */
    255   public  void unlink(int currentFrame)
    256   {
    257     _frameTop = _links[--_linksTop];
    258     _currentFrameBottom = currentFrame;
    259   }
    260 
    261   /**
    262    * Set a local variable or parameter in the current stack frame.
    263    *
    264    *
    265    * @param index Local variable index relative to the current stack
    266    * frame bottom.
    267    *
    268    * @param val The value of the variable that is being set.
    269    */
    270   public void setLocalVariable(int index, XObject val)
    271   {
    272     _stackFrames[index + _currentFrameBottom] = val;
    273   }
    274 
    275   /**
    276    * Set a local variable or parameter in the specified stack frame.
    277    *
    278    *
    279    * @param index Local variable index relative to the current stack
    280    * frame bottom.
    281    * NEEDSDOC @param stackFrame
    282    *
    283    * @param val The value of the variable that is being set.
    284    */
    285   public void setLocalVariable(int index, XObject val, int stackFrame)
    286   {
    287     _stackFrames[index + stackFrame] = val;
    288   }
    289 
    290   /**
    291    * Get a local variable or parameter in the current stack frame.
    292    *
    293    *
    294    * @param xctxt The XPath context, which must be passed in order to
    295    * lazy evaluate variables.
    296    *
    297    * @param index Local variable index relative to the current stack
    298    * frame bottom.
    299    *
    300    * @return The value of the variable.
    301    *
    302    * @throws TransformerException
    303    */
    304   public XObject getLocalVariable(XPathContext xctxt, int index)
    305           throws TransformerException
    306   {
    307 
    308     index += _currentFrameBottom;
    309 
    310     XObject val = _stackFrames[index];
    311 
    312     if(null == val)
    313       throw new TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VARIABLE_ACCESSED_BEFORE_BIND, null),
    314                      xctxt.getSAXLocator());
    315       // "Variable accessed before it is bound!", xctxt.getSAXLocator());
    316 
    317     // Lazy execution of variables.
    318     if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
    319       return (_stackFrames[index] = val.execute(xctxt));
    320 
    321     return val;
    322   }
    323 
    324   /**
    325    * Get a local variable or parameter in the current stack frame.
    326    *
    327    *
    328    * @param index Local variable index relative to the given
    329    * frame bottom.
    330    * NEEDSDOC @param frame
    331    *
    332    * @return The value of the variable.
    333    *
    334    * @throws TransformerException
    335    */
    336   public XObject getLocalVariable(int index, int frame)
    337           throws TransformerException
    338   {
    339 
    340     index += frame;
    341 
    342     XObject val = _stackFrames[index];
    343 
    344     return val;
    345   }
    346 
    347   /**
    348    * Get a local variable or parameter in the current stack frame.
    349    *
    350    *
    351    * @param xctxt The XPath context, which must be passed in order to
    352    * lazy evaluate variables.
    353    *
    354    * @param index Local variable index relative to the current stack
    355    * frame bottom.
    356    *
    357    * @return The value of the variable.
    358    *
    359    * @throws TransformerException
    360    */
    361   public XObject getLocalVariable(XPathContext xctxt, int index, boolean destructiveOK)
    362           throws TransformerException
    363   {
    364 
    365     index += _currentFrameBottom;
    366 
    367     XObject val = _stackFrames[index];
    368 
    369     if(null == val)
    370       throw new TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VARIABLE_ACCESSED_BEFORE_BIND, null),
    371                      xctxt.getSAXLocator());
    372       // "Variable accessed before it is bound!", xctxt.getSAXLocator());
    373 
    374     // Lazy execution of variables.
    375     if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
    376       return (_stackFrames[index] = val.execute(xctxt));
    377 
    378     return destructiveOK ? val : val.getFresh();
    379   }
    380 
    381   /**
    382    * Tell if a local variable has been set or not.
    383    *
    384    * @param index Local variable index relative to the current stack
    385    * frame bottom.
    386    *
    387    * @return true if the value at the index is not null.
    388    *
    389    * @throws TransformerException
    390    */
    391   public boolean isLocalSet(int index) throws TransformerException
    392   {
    393     return (_stackFrames[index + _currentFrameBottom] != null);
    394   }
    395 
    396   /** NEEDSDOC Field m_nulls          */
    397   private static XObject[] m_nulls = new XObject[CLEARLIMITATION];
    398 
    399   /**
    400    * Use this to clear the variables in a section of the stack.  This is
    401    * used to clear the parameter section of the stack, so that default param
    402    * values can tell if they've already been set.  It is important to note that
    403    * this function has a 1K limitation.
    404    *
    405    * @param start The start position, relative to the current local stack frame.
    406    * @param len The number of slots to be cleared.
    407    */
    408   public void clearLocalSlots(int start, int len)
    409   {
    410 
    411     start += _currentFrameBottom;
    412 
    413     System.arraycopy(m_nulls, 0, _stackFrames, start, len);
    414   }
    415 
    416   /**
    417    * Set a global variable or parameter in the global stack frame.
    418    *
    419    *
    420    * @param index Local variable index relative to the global stack frame
    421    * bottom.
    422    *
    423    * @param val The value of the variable that is being set.
    424    */
    425   public void setGlobalVariable(final int index, final XObject val)
    426   {
    427     _stackFrames[index] = val;
    428   }
    429 
    430   /**
    431    * Get a global variable or parameter from the global stack frame.
    432    *
    433    *
    434    * @param xctxt The XPath context, which must be passed in order to
    435    * lazy evaluate variables.
    436    *
    437    * @param index Global variable index relative to the global stack
    438    * frame bottom.
    439    *
    440    * @return The value of the variable.
    441    *
    442    * @throws TransformerException
    443    */
    444   public XObject getGlobalVariable(XPathContext xctxt, final int index)
    445           throws TransformerException
    446   {
    447 
    448     XObject val = _stackFrames[index];
    449 
    450     // Lazy execution of variables.
    451     if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
    452       return (_stackFrames[index] = val.execute(xctxt));
    453 
    454     return val;
    455   }
    456 
    457   /**
    458    * Get a global variable or parameter from the global stack frame.
    459    *
    460    *
    461    * @param xctxt The XPath context, which must be passed in order to
    462    * lazy evaluate variables.
    463    *
    464    * @param index Global variable index relative to the global stack
    465    * frame bottom.
    466    *
    467    * @return The value of the variable.
    468    *
    469    * @throws TransformerException
    470    */
    471   public XObject getGlobalVariable(XPathContext xctxt, final int index, boolean destructiveOK)
    472           throws TransformerException
    473   {
    474 
    475     XObject val = _stackFrames[index];
    476 
    477     // Lazy execution of variables.
    478     if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
    479       return (_stackFrames[index] = val.execute(xctxt));
    480 
    481     return destructiveOK ? val : val.getFresh();
    482   }
    483 
    484   /**
    485    * Get a variable based on it's qualified name.
    486    * This is for external use only.
    487    *
    488    * @param xctxt The XPath context, which must be passed in order to
    489    * lazy evaluate variables.
    490    *
    491    * @param qname The qualified name of the variable.
    492    *
    493    * @return The evaluated value of the variable.
    494    *
    495    * @throws javax.xml.transform.TransformerException
    496    */
    497   public XObject getVariableOrParam(
    498           XPathContext xctxt, org.apache.xml.utils.QName qname)
    499             throws javax.xml.transform.TransformerException
    500   {
    501 
    502     org.apache.xml.utils.PrefixResolver prefixResolver =
    503       xctxt.getNamespaceContext();
    504 
    505     // Get the current ElemTemplateElement, which must be pushed in as the
    506     // prefix resolver, and then walk backwards in document order, searching
    507     // for an xsl:param element or xsl:variable element that matches our
    508     // qname.  If we reach the top level, use the StylesheetRoot's composed
    509     // list of top level variables and parameters.
    510 
    511     if (prefixResolver instanceof org.apache.xalan.templates.ElemTemplateElement)
    512     {
    513 
    514       org.apache.xalan.templates.ElemVariable vvar;
    515 
    516       org.apache.xalan.templates.ElemTemplateElement prev =
    517         (org.apache.xalan.templates.ElemTemplateElement) prefixResolver;
    518 
    519       if (!(prev instanceof org.apache.xalan.templates.Stylesheet))
    520       {
    521         while ( !(prev.getParentNode() instanceof org.apache.xalan.templates.Stylesheet) )
    522         {
    523           org.apache.xalan.templates.ElemTemplateElement savedprev = prev;
    524 
    525           while (null != (prev = prev.getPreviousSiblingElem()))
    526           {
    527             if (prev instanceof org.apache.xalan.templates.ElemVariable)
    528             {
    529               vvar = (org.apache.xalan.templates.ElemVariable) prev;
    530 
    531               if (vvar.getName().equals(qname))
    532                 return getLocalVariable(xctxt, vvar.getIndex());
    533             }
    534           }
    535           prev = savedprev.getParentElem();
    536         }
    537       }
    538 
    539       vvar = prev.getStylesheetRoot().getVariableOrParamComposed(qname);
    540       if (null != vvar)
    541         return getGlobalVariable(xctxt, vvar.getIndex());
    542     }
    543 
    544     throw new javax.xml.transform.TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VAR_NOT_RESOLVABLE, new Object[]{qname.toString()})); //"Variable not resolvable: " + qname);
    545   }
    546 }  // end VariableStack
    547 
    548