001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: VariableStack.java,v 1.47 2005/01/23 01:02:10 mcnamara Exp $
018: */
019: package org.apache.xpath;
020:
021: import javax.xml.transform.TransformerException;
022:
023: import org.apache.xalan.res.XSLMessages;
024: import org.apache.xpath.objects.XObject;
025: import org.apache.xpath.res.XPATHErrorResources;
026:
027: /**
028: * Defines a class to keep track of a stack for
029: * template arguments and variables.
030: *
031: * <p>This has been changed from the previous incarnations of this
032: * class to be fairly low level.</p>
033: * @xsl.usage internal
034: */
035: public class VariableStack implements Cloneable {
036: /**
037: * limitation for 1K
038: */
039: public static final int CLEARLIMITATION = 1024;
040:
041: /**
042: * Constructor for a variable stack.
043: */
044: public VariableStack() {
045: reset();
046: }
047:
048: /**
049: * Returns a clone of this variable stack.
050: *
051: * @return a clone of this variable stack.
052: *
053: * @throws CloneNotSupportedException
054: */
055: public synchronized Object clone()
056: throws CloneNotSupportedException {
057:
058: VariableStack vs = (VariableStack) super .clone();
059:
060: // I *think* I can get away with a shallow clone here?
061: vs._stackFrames = (XObject[]) _stackFrames.clone();
062: vs._links = (int[]) _links.clone();
063:
064: return vs;
065: }
066:
067: /**
068: * The stack frame where all variables and params will be kept.
069: * @serial
070: */
071: XObject[] _stackFrames = new XObject[XPathContext.RECURSIONLIMIT * 2];
072:
073: /**
074: * The top of the stack frame (<code>_stackFrames</code>).
075: * @serial
076: */
077: int _frameTop;
078:
079: /**
080: * The bottom index of the current frame (relative to <code>_stackFrames</code>).
081: * @serial
082: */
083: private int _currentFrameBottom;
084:
085: /**
086: * The stack of frame positions. I call 'em links because of distant
087: * <a href="http://math.millikin.edu/mprogers/Courses/currentCourses/CS481-ComputerArchitecture/cs481.Motorola68000.html">
088: * Motorola 68000 assembler</a> memories. :-)
089: * @serial
090: */
091: int[] _links = new int[XPathContext.RECURSIONLIMIT];
092:
093: /**
094: * The top of the links stack.
095: */
096: int _linksTop;
097:
098: /**
099: * Get the element at the given index, regardless of stackframe.
100: *
101: * @param i index from zero.
102: *
103: * @return The item at the given index.
104: */
105: public XObject elementAt(final int i) {
106: return _stackFrames[i];
107: }
108:
109: /**
110: * Get size of the stack.
111: *
112: * @return the total size of the execution stack.
113: */
114: public int size() {
115: return _frameTop;
116: }
117:
118: /**
119: * Reset the stack to a start position.
120: *
121: * @return the total size of the execution stack.
122: */
123: public void reset() {
124:
125: _frameTop = 0;
126: _linksTop = 0;
127:
128: // Adding one here to the stack of frame positions will allow us always
129: // to look one under without having to check if we're at zero.
130: // (As long as the caller doesn't screw up link/unlink.)
131: _links[_linksTop++] = 0;
132: _stackFrames = new XObject[_stackFrames.length];
133: }
134:
135: /**
136: * Set the current stack frame.
137: *
138: * @param sf The new stack frame position.
139: */
140: public void setStackFrame(int sf) {
141: _currentFrameBottom = sf;
142: }
143:
144: /**
145: * Get the position from where the search should start,
146: * which is either the searchStart property, or the top
147: * of the stack if that value is -1.
148: *
149: * @return The current stack frame position.
150: */
151: public int getStackFrame() {
152: return _currentFrameBottom;
153: }
154:
155: /**
156: * Allocates memory (called a stackframe) on the stack; used to store
157: * local variables and parameter arguments.
158: *
159: * <p>I use the link/unlink concept because of distant
160: * <a href="http://math.millikin.edu/mprogers/Courses/currentCourses/CS481-ComputerArchitecture/cs481.Motorola68000.html">
161: * Motorola 68000 assembler</a> memories.</p>
162: *
163: * @param size The size of the stack frame allocation. This ammount should
164: * normally be the maximum number of variables that you can have allocated
165: * at one time in the new stack frame.
166: *
167: * @return The bottom of the stack frame, from where local variable addressing
168: * should start from.
169: */
170: public int link(final int size) {
171:
172: _currentFrameBottom = _frameTop;
173: _frameTop += size;
174:
175: if (_frameTop >= _stackFrames.length) {
176: XObject newsf[] = new XObject[_stackFrames.length
177: + XPathContext.RECURSIONLIMIT + size];
178:
179: System.arraycopy(_stackFrames, 0, newsf, 0,
180: _stackFrames.length);
181:
182: _stackFrames = newsf;
183: }
184:
185: if (_linksTop + 1 >= _links.length) {
186: int newlinks[] = new int[_links.length
187: + (CLEARLIMITATION * 2)];
188:
189: System.arraycopy(_links, 0, newlinks, 0, _links.length);
190:
191: _links = newlinks;
192: }
193:
194: _links[_linksTop++] = _currentFrameBottom;
195:
196: return _currentFrameBottom;
197: }
198:
199: /**
200: * Free up the stack frame that was last allocated with
201: * {@link #link(int size)}.
202: */
203: public void unlink() {
204: _frameTop = _links[--_linksTop];
205: _currentFrameBottom = _links[_linksTop - 1];
206: }
207:
208: /**
209: * Free up the stack frame that was last allocated with
210: * {@link #link(int size)}.
211: * @param currentFrame The current frame to set to
212: * after the unlink.
213: */
214: public void unlink(int currentFrame) {
215: _frameTop = _links[--_linksTop];
216: _currentFrameBottom = currentFrame;
217: }
218:
219: /**
220: * Set a local variable or parameter in the current stack frame.
221: *
222: *
223: * @param index Local variable index relative to the current stack
224: * frame bottom.
225: *
226: * @param val The value of the variable that is being set.
227: */
228: public void setLocalVariable(int index, XObject val) {
229: _stackFrames[index + _currentFrameBottom] = val;
230: }
231:
232: /**
233: * Set a local variable or parameter in the specified stack frame.
234: *
235: *
236: * @param index Local variable index relative to the current stack
237: * frame bottom.
238: * NEEDSDOC @param stackFrame
239: *
240: * @param val The value of the variable that is being set.
241: */
242: public void setLocalVariable(int index, XObject val, int stackFrame) {
243: _stackFrames[index + stackFrame] = val;
244: }
245:
246: /**
247: * Get a local variable or parameter in the current stack frame.
248: *
249: *
250: * @param xctxt The XPath context, which must be passed in order to
251: * lazy evaluate variables.
252: *
253: * @param index Local variable index relative to the current stack
254: * frame bottom.
255: *
256: * @return The value of the variable.
257: *
258: * @throws TransformerException
259: */
260: public XObject getLocalVariable(XPathContext xctxt, int index)
261: throws TransformerException {
262:
263: index += _currentFrameBottom;
264:
265: XObject val = _stackFrames[index];
266:
267: if (null == val)
268: throw new TransformerException(
269: XSLMessages
270: .createXPATHMessage(
271: XPATHErrorResources.ER_VARIABLE_ACCESSED_BEFORE_BIND,
272: null), xctxt.getSAXLocator());
273: // "Variable accessed before it is bound!", xctxt.getSAXLocator());
274:
275: // Lazy execution of variables.
276: if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
277: return (_stackFrames[index] = val.execute(xctxt));
278:
279: return val;
280: }
281:
282: /**
283: * Get a local variable or parameter in the current stack frame.
284: *
285: *
286: * @param index Local variable index relative to the given
287: * frame bottom.
288: * NEEDSDOC @param frame
289: *
290: * @return The value of the variable.
291: *
292: * @throws TransformerException
293: */
294: public XObject getLocalVariable(int index, int frame)
295: throws TransformerException {
296:
297: index += frame;
298:
299: XObject val = _stackFrames[index];
300:
301: return val;
302: }
303:
304: /**
305: * Get a local variable or parameter in the current stack frame.
306: *
307: *
308: * @param xctxt The XPath context, which must be passed in order to
309: * lazy evaluate variables.
310: *
311: * @param index Local variable index relative to the current stack
312: * frame bottom.
313: *
314: * @return The value of the variable.
315: *
316: * @throws TransformerException
317: */
318: public XObject getLocalVariable(XPathContext xctxt, int index,
319: boolean destructiveOK) throws TransformerException {
320:
321: index += _currentFrameBottom;
322:
323: XObject val = _stackFrames[index];
324:
325: if (null == val)
326: throw new TransformerException(
327: XSLMessages
328: .createXPATHMessage(
329: XPATHErrorResources.ER_VARIABLE_ACCESSED_BEFORE_BIND,
330: null), xctxt.getSAXLocator());
331: // "Variable accessed before it is bound!", xctxt.getSAXLocator());
332:
333: // Lazy execution of variables.
334: if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
335: return (_stackFrames[index] = val.execute(xctxt));
336:
337: return destructiveOK ? val : val.getFresh();
338: }
339:
340: /**
341: * Tell if a local variable has been set or not.
342: *
343: * @param index Local variable index relative to the current stack
344: * frame bottom.
345: *
346: * @return true if the value at the index is not null.
347: *
348: * @throws TransformerException
349: */
350: public boolean isLocalSet(int index) throws TransformerException {
351: return (_stackFrames[index + _currentFrameBottom] != null);
352: }
353:
354: /** NEEDSDOC Field m_nulls */
355: private static XObject[] m_nulls = new XObject[CLEARLIMITATION];
356:
357: /**
358: * Use this to clear the variables in a section of the stack. This is
359: * used to clear the parameter section of the stack, so that default param
360: * values can tell if they've already been set. It is important to note that
361: * this function has a 1K limitation.
362: *
363: * @param start The start position, relative to the current local stack frame.
364: * @param len The number of slots to be cleared.
365: */
366: public void clearLocalSlots(int start, int len) {
367:
368: start += _currentFrameBottom;
369:
370: System.arraycopy(m_nulls, 0, _stackFrames, start, len);
371: }
372:
373: /**
374: * Set a global variable or parameter in the global stack frame.
375: *
376: *
377: * @param index Local variable index relative to the global stack frame
378: * bottom.
379: *
380: * @param val The value of the variable that is being set.
381: */
382: public void setGlobalVariable(final int index, final XObject val) {
383: _stackFrames[index] = val;
384: }
385:
386: /**
387: * Get a global variable or parameter from the global stack frame.
388: *
389: *
390: * @param xctxt The XPath context, which must be passed in order to
391: * lazy evaluate variables.
392: *
393: * @param index Global variable index relative to the global stack
394: * frame bottom.
395: *
396: * @return The value of the variable.
397: *
398: * @throws TransformerException
399: */
400: public XObject getGlobalVariable(XPathContext xctxt, final int index)
401: throws TransformerException {
402:
403: XObject val = _stackFrames[index];
404:
405: // Lazy execution of variables.
406: if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
407: return (_stackFrames[index] = val.execute(xctxt));
408:
409: return val;
410: }
411:
412: /**
413: * Get a global variable or parameter from the global stack frame.
414: *
415: *
416: * @param xctxt The XPath context, which must be passed in order to
417: * lazy evaluate variables.
418: *
419: * @param index Global variable index relative to the global stack
420: * frame bottom.
421: *
422: * @return The value of the variable.
423: *
424: * @throws TransformerException
425: */
426: public XObject getGlobalVariable(XPathContext xctxt,
427: final int index, boolean destructiveOK)
428: throws TransformerException {
429:
430: XObject val = _stackFrames[index];
431:
432: // Lazy execution of variables.
433: if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
434: return (_stackFrames[index] = val.execute(xctxt));
435:
436: return destructiveOK ? val : val.getFresh();
437: }
438:
439: /**
440: * Get a variable based on it's qualified name.
441: * This is for external use only.
442: *
443: * @param xctxt The XPath context, which must be passed in order to
444: * lazy evaluate variables.
445: *
446: * @param qname The qualified name of the variable.
447: *
448: * @return The evaluated value of the variable.
449: *
450: * @throws javax.xml.transform.TransformerException
451: */
452: public XObject getVariableOrParam(XPathContext xctxt,
453: org.apache.xml.utils.QName qname)
454: throws javax.xml.transform.TransformerException {
455:
456: org.apache.xml.utils.PrefixResolver prefixResolver = xctxt
457: .getNamespaceContext();
458:
459: // Get the current ElemTemplateElement, which must be pushed in as the
460: // prefix resolver, and then walk backwards in document order, searching
461: // for an xsl:param element or xsl:variable element that matches our
462: // qname. If we reach the top level, use the StylesheetRoot's composed
463: // list of top level variables and parameters.
464:
465: if (prefixResolver instanceof org.apache.xalan.templates.ElemTemplateElement) {
466:
467: org.apache.xalan.templates.ElemVariable vvar;
468:
469: org.apache.xalan.templates.ElemTemplateElement prev = (org.apache.xalan.templates.ElemTemplateElement) prefixResolver;
470:
471: if (!(prev instanceof org.apache.xalan.templates.Stylesheet)) {
472: while (!(prev.getParentNode() instanceof org.apache.xalan.templates.Stylesheet)) {
473: org.apache.xalan.templates.ElemTemplateElement savedprev = prev;
474:
475: while (null != (prev = prev
476: .getPreviousSiblingElem())) {
477: if (prev instanceof org.apache.xalan.templates.ElemVariable) {
478: vvar = (org.apache.xalan.templates.ElemVariable) prev;
479:
480: if (vvar.getName().equals(qname))
481: return getLocalVariable(xctxt, vvar
482: .getIndex());
483: }
484: }
485: prev = savedprev.getParentElem();
486: }
487: }
488:
489: vvar = prev.getStylesheetRoot().getVariableOrParamComposed(
490: qname);
491: if (null != vvar)
492: return getGlobalVariable(xctxt, vvar.getIndex());
493: }
494:
495: throw new javax.xml.transform.TransformerException(XSLMessages
496: .createXPATHMessage(
497: XPATHErrorResources.ER_VAR_NOT_RESOLVABLE,
498: new Object[] { qname.toString() })); //"Variable not resolvable: " + qname);
499: }
500: } // end VariableStack
|