548 lines
15 KiB
Java
548 lines
15 KiB
Java
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/*
|
|
* $Id: VariableStack.java 524812 2007-04-02 15:52:03Z zongaro $
|
|
*/
|
|
package org.apache.xpath;
|
|
|
|
import javax.xml.transform.TransformerException;
|
|
|
|
import org.apache.xalan.res.XSLMessages;
|
|
import org.apache.xpath.objects.XObject;
|
|
import org.apache.xpath.res.XPATHErrorResources;
|
|
|
|
/**
|
|
* Defines a class to keep track of a stack for
|
|
* template arguments and variables.
|
|
*
|
|
* <p>This has been changed from the previous incarnations of this
|
|
* class to be fairly low level.</p>
|
|
* @xsl.usage internal
|
|
*/
|
|
public class VariableStack implements Cloneable
|
|
{
|
|
/**
|
|
* limitation for 1K
|
|
*/
|
|
public static final int CLEARLIMITATION= 1024;
|
|
|
|
/**
|
|
* Constructor for a variable stack.
|
|
*/
|
|
public VariableStack()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
/**
|
|
* Constructor for a variable stack.
|
|
* @param initStackSize The initial stack size. Must be at least one. The
|
|
* stack can grow if needed.
|
|
*/
|
|
public VariableStack(int initStackSize)
|
|
{
|
|
// Allow for twice as many variables as stack link entries
|
|
reset(initStackSize, initStackSize*2);
|
|
}
|
|
|
|
/**
|
|
* Returns a clone of this variable stack.
|
|
*
|
|
* @return a clone of this variable stack.
|
|
*
|
|
* @throws CloneNotSupportedException
|
|
*/
|
|
public synchronized Object clone() throws CloneNotSupportedException
|
|
{
|
|
|
|
VariableStack vs = (VariableStack) super.clone();
|
|
|
|
// I *think* I can get away with a shallow clone here?
|
|
vs._stackFrames = (XObject[]) _stackFrames.clone();
|
|
vs._links = (int[]) _links.clone();
|
|
|
|
return vs;
|
|
}
|
|
|
|
/**
|
|
* The stack frame where all variables and params will be kept.
|
|
* @serial
|
|
*/
|
|
XObject[] _stackFrames;
|
|
|
|
/**
|
|
* The top of the stack frame (<code>_stackFrames</code>).
|
|
* @serial
|
|
*/
|
|
int _frameTop;
|
|
|
|
/**
|
|
* The bottom index of the current frame (relative to <code>_stackFrames</code>).
|
|
* @serial
|
|
*/
|
|
private int _currentFrameBottom;
|
|
|
|
/**
|
|
* The stack of frame positions. I call 'em links because of distant
|
|
* <a href="http://math.millikin.edu/mprogers/Courses/currentCourses/CS481-ComputerArchitecture/cs481.Motorola68000.html">
|
|
* Motorola 68000 assembler</a> memories. :-)
|
|
* @serial
|
|
*/
|
|
int[] _links;
|
|
|
|
/**
|
|
* The top of the links stack.
|
|
*/
|
|
int _linksTop;
|
|
|
|
/**
|
|
* Get the element at the given index, regardless of stackframe.
|
|
*
|
|
* @param i index from zero.
|
|
*
|
|
* @return The item at the given index.
|
|
*/
|
|
public XObject elementAt(final int i)
|
|
{
|
|
return _stackFrames[i];
|
|
}
|
|
|
|
/**
|
|
* Get size of the stack.
|
|
*
|
|
* @return the total size of the execution stack.
|
|
*/
|
|
public int size()
|
|
{
|
|
return _frameTop;
|
|
}
|
|
|
|
/**
|
|
* Reset the stack to a start position.
|
|
*/
|
|
public void reset()
|
|
{
|
|
// If the stack was previously allocated, assume that about the same
|
|
// amount of stack space will be needed again; otherwise, use a very
|
|
// large stack size.
|
|
int linksSize = (_links == null) ? XPathContext.RECURSIONLIMIT
|
|
: _links.length;
|
|
int varArraySize = (_stackFrames == null) ? XPathContext.RECURSIONLIMIT * 2
|
|
: _stackFrames.length;
|
|
reset(linksSize, varArraySize);
|
|
}
|
|
|
|
/**
|
|
* Reset the stack to a start position.
|
|
* @param linksSize Initial stack size to use
|
|
* @param varArraySize Initial variable array size to use
|
|
*/
|
|
protected void reset(int linksSize, int varArraySize) {
|
|
_frameTop = 0;
|
|
_linksTop = 0;
|
|
|
|
// Don't bother reallocating _links array if it exists already
|
|
if (_links == null) {
|
|
_links = new int[linksSize];
|
|
}
|
|
|
|
// Adding one here to the stack of frame positions will allow us always
|
|
// to look one under without having to check if we're at zero.
|
|
// (As long as the caller doesn't screw up link/unlink.)
|
|
_links[_linksTop++] = 0;
|
|
|
|
// Get a clean _stackFrames array and discard the old one.
|
|
_stackFrames = new XObject[varArraySize];
|
|
}
|
|
|
|
/**
|
|
* Set the current stack frame.
|
|
*
|
|
* @param sf The new stack frame position.
|
|
*/
|
|
public void setStackFrame(int sf)
|
|
{
|
|
_currentFrameBottom = sf;
|
|
}
|
|
|
|
/**
|
|
* Get the position from where the search should start,
|
|
* which is either the searchStart property, or the top
|
|
* of the stack if that value is -1.
|
|
*
|
|
* @return The current stack frame position.
|
|
*/
|
|
public int getStackFrame()
|
|
{
|
|
return _currentFrameBottom;
|
|
}
|
|
|
|
/**
|
|
* Allocates memory (called a stackframe) on the stack; used to store
|
|
* local variables and parameter arguments.
|
|
*
|
|
* <p>I use the link/unlink concept because of distant
|
|
* <a href="http://math.millikin.edu/mprogers/Courses/currentCourses/CS481-ComputerArchitecture/cs481.Motorola68000.html">
|
|
* Motorola 68000 assembler</a> memories.</p>
|
|
*
|
|
* @param size The size of the stack frame allocation. This ammount should
|
|
* normally be the maximum number of variables that you can have allocated
|
|
* at one time in the new stack frame.
|
|
*
|
|
* @return The bottom of the stack frame, from where local variable addressing
|
|
* should start from.
|
|
*/
|
|
public int link(final int size)
|
|
{
|
|
|
|
_currentFrameBottom = _frameTop;
|
|
_frameTop += size;
|
|
|
|
if (_frameTop >= _stackFrames.length)
|
|
{
|
|
XObject newsf[] = new XObject[_stackFrames.length + XPathContext.RECURSIONLIMIT + size];
|
|
|
|
System.arraycopy(_stackFrames, 0, newsf, 0, _stackFrames.length);
|
|
|
|
_stackFrames = newsf;
|
|
}
|
|
|
|
if (_linksTop + 1 >= _links.length)
|
|
{
|
|
int newlinks[] = new int[_links.length + (CLEARLIMITATION * 2)];
|
|
|
|
System.arraycopy(_links, 0, newlinks, 0, _links.length);
|
|
|
|
_links = newlinks;
|
|
}
|
|
|
|
_links[_linksTop++] = _currentFrameBottom;
|
|
|
|
return _currentFrameBottom;
|
|
}
|
|
|
|
/**
|
|
* Free up the stack frame that was last allocated with
|
|
* {@link #link(int size)}.
|
|
*/
|
|
public void unlink()
|
|
{
|
|
_frameTop = _links[--_linksTop];
|
|
_currentFrameBottom = _links[_linksTop - 1];
|
|
}
|
|
|
|
/**
|
|
* Free up the stack frame that was last allocated with
|
|
* {@link #link(int size)}.
|
|
* @param currentFrame The current frame to set to
|
|
* after the unlink.
|
|
*/
|
|
public void unlink(int currentFrame)
|
|
{
|
|
_frameTop = _links[--_linksTop];
|
|
_currentFrameBottom = currentFrame;
|
|
}
|
|
|
|
/**
|
|
* Set a local variable or parameter in the current stack frame.
|
|
*
|
|
*
|
|
* @param index Local variable index relative to the current stack
|
|
* frame bottom.
|
|
*
|
|
* @param val The value of the variable that is being set.
|
|
*/
|
|
public void setLocalVariable(int index, XObject val)
|
|
{
|
|
_stackFrames[index + _currentFrameBottom] = val;
|
|
}
|
|
|
|
/**
|
|
* Set a local variable or parameter in the specified stack frame.
|
|
*
|
|
*
|
|
* @param index Local variable index relative to the current stack
|
|
* frame bottom.
|
|
* NEEDSDOC @param stackFrame
|
|
*
|
|
* @param val The value of the variable that is being set.
|
|
*/
|
|
public void setLocalVariable(int index, XObject val, int stackFrame)
|
|
{
|
|
_stackFrames[index + stackFrame] = val;
|
|
}
|
|
|
|
/**
|
|
* Get a local variable or parameter in the current stack frame.
|
|
*
|
|
*
|
|
* @param xctxt The XPath context, which must be passed in order to
|
|
* lazy evaluate variables.
|
|
*
|
|
* @param index Local variable index relative to the current stack
|
|
* frame bottom.
|
|
*
|
|
* @return The value of the variable.
|
|
*
|
|
* @throws TransformerException
|
|
*/
|
|
public XObject getLocalVariable(XPathContext xctxt, int index)
|
|
throws TransformerException
|
|
{
|
|
|
|
index += _currentFrameBottom;
|
|
|
|
XObject val = _stackFrames[index];
|
|
|
|
if(null == val)
|
|
throw new TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VARIABLE_ACCESSED_BEFORE_BIND, null),
|
|
xctxt.getSAXLocator());
|
|
// "Variable accessed before it is bound!", xctxt.getSAXLocator());
|
|
|
|
// Lazy execution of variables.
|
|
if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
|
|
return (_stackFrames[index] = val.execute(xctxt));
|
|
|
|
return val;
|
|
}
|
|
|
|
/**
|
|
* Get a local variable or parameter in the current stack frame.
|
|
*
|
|
*
|
|
* @param index Local variable index relative to the given
|
|
* frame bottom.
|
|
* NEEDSDOC @param frame
|
|
*
|
|
* @return The value of the variable.
|
|
*
|
|
* @throws TransformerException
|
|
*/
|
|
public XObject getLocalVariable(int index, int frame)
|
|
throws TransformerException
|
|
{
|
|
|
|
index += frame;
|
|
|
|
XObject val = _stackFrames[index];
|
|
|
|
return val;
|
|
}
|
|
|
|
/**
|
|
* Get a local variable or parameter in the current stack frame.
|
|
*
|
|
*
|
|
* @param xctxt The XPath context, which must be passed in order to
|
|
* lazy evaluate variables.
|
|
*
|
|
* @param index Local variable index relative to the current stack
|
|
* frame bottom.
|
|
*
|
|
* @return The value of the variable.
|
|
*
|
|
* @throws TransformerException
|
|
*/
|
|
public XObject getLocalVariable(XPathContext xctxt, int index, boolean destructiveOK)
|
|
throws TransformerException
|
|
{
|
|
|
|
index += _currentFrameBottom;
|
|
|
|
XObject val = _stackFrames[index];
|
|
|
|
if(null == val)
|
|
throw new TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VARIABLE_ACCESSED_BEFORE_BIND, null),
|
|
xctxt.getSAXLocator());
|
|
// "Variable accessed before it is bound!", xctxt.getSAXLocator());
|
|
|
|
// Lazy execution of variables.
|
|
if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
|
|
return (_stackFrames[index] = val.execute(xctxt));
|
|
|
|
return destructiveOK ? val : val.getFresh();
|
|
}
|
|
|
|
/**
|
|
* Tell if a local variable has been set or not.
|
|
*
|
|
* @param index Local variable index relative to the current stack
|
|
* frame bottom.
|
|
*
|
|
* @return true if the value at the index is not null.
|
|
*
|
|
* @throws TransformerException
|
|
*/
|
|
public boolean isLocalSet(int index) throws TransformerException
|
|
{
|
|
return (_stackFrames[index + _currentFrameBottom] != null);
|
|
}
|
|
|
|
/** NEEDSDOC Field m_nulls */
|
|
private static XObject[] m_nulls = new XObject[CLEARLIMITATION];
|
|
|
|
/**
|
|
* Use this to clear the variables in a section of the stack. This is
|
|
* used to clear the parameter section of the stack, so that default param
|
|
* values can tell if they've already been set. It is important to note that
|
|
* this function has a 1K limitation.
|
|
*
|
|
* @param start The start position, relative to the current local stack frame.
|
|
* @param len The number of slots to be cleared.
|
|
*/
|
|
public void clearLocalSlots(int start, int len)
|
|
{
|
|
|
|
start += _currentFrameBottom;
|
|
|
|
System.arraycopy(m_nulls, 0, _stackFrames, start, len);
|
|
}
|
|
|
|
/**
|
|
* Set a global variable or parameter in the global stack frame.
|
|
*
|
|
*
|
|
* @param index Local variable index relative to the global stack frame
|
|
* bottom.
|
|
*
|
|
* @param val The value of the variable that is being set.
|
|
*/
|
|
public void setGlobalVariable(final int index, final XObject val)
|
|
{
|
|
_stackFrames[index] = val;
|
|
}
|
|
|
|
/**
|
|
* Get a global variable or parameter from the global stack frame.
|
|
*
|
|
*
|
|
* @param xctxt The XPath context, which must be passed in order to
|
|
* lazy evaluate variables.
|
|
*
|
|
* @param index Global variable index relative to the global stack
|
|
* frame bottom.
|
|
*
|
|
* @return The value of the variable.
|
|
*
|
|
* @throws TransformerException
|
|
*/
|
|
public XObject getGlobalVariable(XPathContext xctxt, final int index)
|
|
throws TransformerException
|
|
{
|
|
|
|
XObject val = _stackFrames[index];
|
|
|
|
// Lazy execution of variables.
|
|
if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
|
|
return (_stackFrames[index] = val.execute(xctxt));
|
|
|
|
return val;
|
|
}
|
|
|
|
/**
|
|
* Get a global variable or parameter from the global stack frame.
|
|
*
|
|
*
|
|
* @param xctxt The XPath context, which must be passed in order to
|
|
* lazy evaluate variables.
|
|
*
|
|
* @param index Global variable index relative to the global stack
|
|
* frame bottom.
|
|
*
|
|
* @return The value of the variable.
|
|
*
|
|
* @throws TransformerException
|
|
*/
|
|
public XObject getGlobalVariable(XPathContext xctxt, final int index, boolean destructiveOK)
|
|
throws TransformerException
|
|
{
|
|
|
|
XObject val = _stackFrames[index];
|
|
|
|
// Lazy execution of variables.
|
|
if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
|
|
return (_stackFrames[index] = val.execute(xctxt));
|
|
|
|
return destructiveOK ? val : val.getFresh();
|
|
}
|
|
|
|
/**
|
|
* Get a variable based on it's qualified name.
|
|
* This is for external use only.
|
|
*
|
|
* @param xctxt The XPath context, which must be passed in order to
|
|
* lazy evaluate variables.
|
|
*
|
|
* @param qname The qualified name of the variable.
|
|
*
|
|
* @return The evaluated value of the variable.
|
|
*
|
|
* @throws javax.xml.transform.TransformerException
|
|
*/
|
|
public XObject getVariableOrParam(
|
|
XPathContext xctxt, org.apache.xml.utils.QName qname)
|
|
throws javax.xml.transform.TransformerException
|
|
{
|
|
|
|
org.apache.xml.utils.PrefixResolver prefixResolver =
|
|
xctxt.getNamespaceContext();
|
|
|
|
// Get the current ElemTemplateElement, which must be pushed in as the
|
|
// prefix resolver, and then walk backwards in document order, searching
|
|
// for an xsl:param element or xsl:variable element that matches our
|
|
// qname. If we reach the top level, use the StylesheetRoot's composed
|
|
// list of top level variables and parameters.
|
|
|
|
if (prefixResolver instanceof org.apache.xalan.templates.ElemTemplateElement)
|
|
{
|
|
|
|
org.apache.xalan.templates.ElemVariable vvar;
|
|
|
|
org.apache.xalan.templates.ElemTemplateElement prev =
|
|
(org.apache.xalan.templates.ElemTemplateElement) prefixResolver;
|
|
|
|
if (!(prev instanceof org.apache.xalan.templates.Stylesheet))
|
|
{
|
|
while ( !(prev.getParentNode() instanceof org.apache.xalan.templates.Stylesheet) )
|
|
{
|
|
org.apache.xalan.templates.ElemTemplateElement savedprev = prev;
|
|
|
|
while (null != (prev = prev.getPreviousSiblingElem()))
|
|
{
|
|
if (prev instanceof org.apache.xalan.templates.ElemVariable)
|
|
{
|
|
vvar = (org.apache.xalan.templates.ElemVariable) prev;
|
|
|
|
if (vvar.getName().equals(qname))
|
|
return getLocalVariable(xctxt, vvar.getIndex());
|
|
}
|
|
}
|
|
prev = savedprev.getParentElem();
|
|
}
|
|
}
|
|
|
|
vvar = prev.getStylesheetRoot().getVariableOrParamComposed(qname);
|
|
if (null != vvar)
|
|
return getGlobalVariable(xctxt, vvar.getIndex());
|
|
}
|
|
|
|
throw new javax.xml.transform.TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VAR_NOT_RESOLVABLE, new Object[]{qname.toString()})); //"Variable not resolvable: " + qname);
|
|
}
|
|
} // end VariableStack
|
|
|