in velocity-engine-core/src/main/java/org/apache/velocity/runtime/directive/VelocimacroProxy.java [193:290]
public boolean render(InternalContextAdapter context, Writer writer,
Node node, Renderable body)
throws IOException
{
int callArgNum = node.jjtGetNumChildren();
// if this macro was invoked by a call directive, we might have a body AST here.
Object oldBodyRef = context.remove(bodyReference);
if (body != null)
{
context.put(bodyReference, body);
callArgNum--; // Remove the body AST from the arg count
}
// is everything copacetic?
checkArgumentCount(node, callArgNum);
checkDepth(context);
// put macro arg values and save the returned old/new value pairs
Object[] values = handleArgValues(context, node, callArgNum);
try
{
// render the velocity macro
context.pushCurrentMacroName(macroName);
nodeTree.render(context, writer);
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
String msg = "VelocimacroProxy.render() : exception VM = #" + macroName + "()";
log.error(msg, e);
throw new VelocityException(msg, e, rsvc.getLogContext().getStackTrace());
}
finally
{
// if MacroOverflowException was thrown then it already empties the stack
// for everything else - e.g. other exceptions - we clean up after ourself
if (context.getCurrentMacroCallDepth() > 0)
context.popCurrentMacroName();
// clean up after the args and bodyRef
// but only if they weren't overridden inside
Object current = context.get(bodyReference);
if (current == body)
{
if (oldBodyRef != null)
{
context.put(bodyReference, oldBodyRef);
}
else
{
context.remove(bodyReference);
}
}
for (int i = 1; i < macroArgs.size(); i++)
{
MacroArg macroArg = macroArgs.get(i);
current = context.get(macroArg.name);
Object given = values[(i-1) * 2 + 1];
Object old = values[(i-1) * 2];
if (current == given || current == null && given == NULL_VALUE_MARKER)
{
if (old == null)
{
context.remove(macroArg.name);
}
else if (old == NULL_VALUE_MARKER)
{
context.put(macroArg.name, null);
}
else
{
context.put(macroArg.name, old);
}
}
if (enableBCmode)
{
/* allow for nested calls */
Deque<String> literalsStack = (Deque<String>)context.get(literalArgArray[i]);
if (literalsStack != null) /* shouldn't be null */
{
literalsStack.removeFirst();
if (literalsStack.size() == 0)
{
context.remove(literalArgArray[i]);
}
}
}
}
}
return true;
}