in velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java [466:635]
public boolean render(InternalContextAdapter context, Writer writer) throws IOException,
MethodInvocationException
{
try
{
rsvc.getLogContext().pushLogContext(this, uberInfo);
if (referenceType == RUNT)
{
writer.write(literal);
return true;
}
Object value = null;
if (escaped && strictEscape)
{
/*
* If we are in strict mode and the variable is escaped, then don't bother to
* retrieve the value since we won't use it. And if the var is not defined
* it will throw an exception. Set value to TRUE to fall through below with
* simply printing $foo, and not \$foo
*/
value = Boolean.TRUE;
}
else
{
value = execute(null, context);
}
String localNullString = null;
/*
* if this reference is escaped (\$foo) then we want to do one of two things: 1) if this is
* a reference in the context, then we want to print $foo 2) if not, then \$foo (its
* considered schmoo, not VTL)
*/
if (escaped)
{
localNullString = getNullString(context);
if (value == null)
{
writer.write(escPrefix);
writer.write("\\");
writer.write(localNullString);
}
else
{
writer.write(escPrefix);
writer.write(localNullString);
}
return true;
}
/*
* the normal processing
*
* if we have an event cartridge, get a new value object
*/
value = EventHandlerUtil.referenceInsert(rsvc, context, literal, value);
String toString = null;
if (value != null)
{
if (value instanceof Renderable)
{
Renderable renderable = (Renderable)value;
try
{
writer.write(escPrefix);
writer.write(morePrefix);
if (renderable.render(context,writer))
{
return true;
}
}
catch(RuntimeException e)
{
// We commonly get here when an error occurs within a block reference.
// We want to log where the reference is at so that a developer can easily
// know where the offending call is located. This can be seen
// as another element of the error stack we report to log.
log.error("Exception rendering "
+ ((renderable instanceof Reference)? "block ":"Renderable ")
+ rootString + " at " + StringUtils.formatFileString(this));
throw e;
}
}
toString = DuckType.asString(value);
}
if (value == null || toString == null)
{
if (strictRef)
{
if (referenceType != QUIET_REFERENCE)
{
log.error("Prepend the reference with '$!' e.g., $!{}" +
" if you want Velocity to ignore the reference when it evaluates to null",
literal().substring(1));
if (value == null)
{
throw new VelocityException("Reference " + literal()
+ " evaluated to null when attempting to render at "
+ StringUtils.formatFileString(this)
, null, rsvc.getLogContext().getStackTrace());
}
else // toString == null
{
// This will probably rarely happen, but when it does we want to
// inform the user that toString == null so they don't pull there
// hair out wondering why Velocity thinks the value is null.
throw new VelocityException("Reference " + literal()
+ " evaluated to object " + value.getClass().getName()
+ " whose toString() method returned null at "
+ StringUtils.formatFileString(this)
, null, rsvc.getLogContext().getStackTrace());
}
}
return true;
}
/*
* write prefix twice, because it's schmoo, so the \ don't escape each
* other...
*/
localNullString = getNullString(context);
if (!strictEscape)
{
// If in strict escape mode then we only print escape once.
// Yea, I know.. brittle stuff
writer.write(escPrefix);
}
writer.write(escPrefix);
writer.write(morePrefix);
writer.write(localNullString);
if (logOnNull && referenceType != QUIET_REFERENCE)
{
log.debug("Null reference [template '{}', line {}, column {}]: {} cannot be resolved.",
getTemplateName(), this.getLine(), this.getColumn(), this.literal());
}
}
else
{
/*
* non-null processing
*/
writer.write(escPrefix);
writer.write(morePrefix);
if (writer instanceof Filter)
{
((Filter)writer).writeReference(toString);
}
else
{
writer.write(toString);
}
}
return true;
}
finally
{
rsvc.getLogContext().popLogContext();
}
}