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 */
21package org.apache.xpath;
22
23import javax.xml.transform.TransformerException;
24
25import org.apache.xalan.res.XSLMessages;
26import org.apache.xpath.objects.XObject;
27import 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 */
37public 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