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