001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: VariableStack.java 524812 2007-04-02 15:52:03Z zongaro $
020 */
021 package org.apache.xpath;
022
023 import javax.xml.transform.TransformerException;
024
025 import org.apache.xalan.res.XSLMessages;
026 import org.apache.xpath.objects.XObject;
027 import org.apache.xpath.res.XPATHErrorResources;
028
029 /**
030 * Defines a class to keep track of a stack for
031 * template arguments and variables.
032 *
033 * <p>This has been changed from the previous incarnations of this
034 * class to be fairly low level.</p>
035 * @xsl.usage internal
036 */
037 public class VariableStack implements Cloneable
038 {
039 /**
040 * limitation for 1K
041 */
042 public static final int CLEARLIMITATION= 1024;
043
044 /**
045 * Constructor for a variable stack.
046 */
047 public VariableStack()
048 {
049 reset();
050 }
051
052 /**
053 * Constructor for a variable stack.
054 * @param initStackSize The initial stack size. Must be at least one. The
055 * stack can grow if needed.
056 */
057 public VariableStack(int initStackSize)
058 {
059 // Allow for twice as many variables as stack link entries
060 reset(initStackSize, initStackSize*2);
061 }
062
063 /**
064 * Returns a clone of this variable stack.
065 *
066 * @return a clone of this variable stack.
067 *
068 * @throws CloneNotSupportedException
069 */
070 public synchronized Object clone() throws CloneNotSupportedException
071 {
072
073 VariableStack vs = (VariableStack) super.clone();
074
075 // I *think* I can get away with a shallow clone here?
076 vs._stackFrames = (XObject[]) _stackFrames.clone();
077 vs._links = (int[]) _links.clone();
078
079 return vs;
080 }
081
082 /**
083 * The stack frame where all variables and params will be kept.
084 * @serial
085 */
086 XObject[] _stackFrames;
087
088 /**
089 * The top of the stack frame (<code>_stackFrames</code>).
090 * @serial
091 */
092 int _frameTop;
093
094 /**
095 * The bottom index of the current frame (relative to <code>_stackFrames</code>).
096 * @serial
097 */
098 private int _currentFrameBottom;
099
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