throws IOException, MethodInvocationException, MacroOverflowException
{
// wrap the current context and add the macro arguments
RuntimeServices rsvc=VelocityUtil.getEngine().getRuntimeServices();
// the creation of this context is a major bottleneck (incl 2x HashMap)
final ProxyVMContext vmc = new ProxyVMContext(context, rsvc, localContextScope);
int callArguments = node.jjtGetNumChildren();
if (callArguments > 0)
{
// the 0th element is the macro name
for (int i = 1; i < argArray.length && i <= callArguments; i++)
{
/*
* literalArgArray[i] is needed for "render literal if null" functionality.
* The value is used in ASTReference render-method.
*
* The idea is to avoid generating the literal until absolutely necessary.
*
* This makes VMReferenceMungeVisitor obsolete and it would not work anyway
* when the macro AST is shared
*/
vmc.addVMProxyArg(context, argArray[i], literalArgArray[i], node.jjtGetChild(i - 1));
}
}
// if this macro was invoked by a call directive, we might have a body AST here. Put it into context.
if( body != null )
{
vmc.addVMProxyArg(context, bodyReference, "", body);
}
/*
* check that we aren't already at the max call depth
*/
if (maxCallDepth > 0 && maxCallDepth == vmc.getCurrentMacroCallDepth())
{
Object[] stack = vmc.getMacroNameStack();
StringBuffer out = new StringBuffer(100)
.append("Max calling depth of ").append(maxCallDepth)
.append(" was exceeded in macro '").append(macroName)
.append("' with Call Stack:");
for (int i = 0; i < stack.length; i++)
{
if (i != 0)
{
out.append("->");
}
out.append(stack[i]);
}
out.append(" at " + VelocityException.formatFileString(this));
Logger.error(this,out.toString());
// clean out the macro stack, since we just broke it
while (vmc.getCurrentMacroCallDepth() > 0)
{
vmc.popCurrentMacroName();
}
throw new MacroOverflowException(out.toString());
}
try
{
// render the velocity macro
vmc.pushCurrentMacroName(macroName);
nodeTree.render(vmc, writer);
vmc.popCurrentMacroName();
return true;
}
catch (RuntimeException e)
{
throw e;