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    */
VariableStack()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    */
VariableStack(int initStackSize)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    */
clone()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    */
elementAt(final int i)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    */
size()130   public int size()
131   {
132     return _frameTop;
133   }
134 
135   /**
136    * Reset the stack to a start position.
137    */
reset()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    */
reset(int linksSize, int varArraySize)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    */
setStackFrame(int sf)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    */
getStackFrame()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    */
link(final int size)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    */
unlink()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    */
unlink(int currentFrame)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    */
setLocalVariable(int index, XObject val)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    */
setLocalVariable(int index, XObject val, int stackFrame)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    */
getLocalVariable(XPathContext xctxt, int index)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    */
getLocalVariable(int index, int frame)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    */
getLocalVariable(XPathContext xctxt, int index, boolean destructiveOK)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    */
isLocalSet(int index)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    */
clearLocalSlots(int start, int len)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    */
setGlobalVariable(final int index, final XObject val)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    */
getGlobalVariable(XPathContext xctxt, final int index)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    */
getGlobalVariable(XPathContext xctxt, final int index, boolean destructiveOK)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    */
getVariableOrParam( XPathContext xctxt, org.apache.xml.utils.QName qname)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