in velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java [286:453]
public Object execute(Object o, InternalContextAdapter context)
throws MethodInvocationException
{
try
{
rsvc.getLogContext().pushLogContext(this, uberInfo);
/*
* The only case where 'o' is not null is when this method is called by evaluate().
* Its value is not used, but it is a convention meant to allow statements like
* #if($invalidReference) *not* to trigger an invalid reference event.
* Statements like #if($invalidReference.prop) should *still* trigger an invalid reference event.
* Statements like #if($validReference.invalidProp) should not.
*/
boolean onlyTestingReference = (o != null);
if (referenceType == RUNT)
return null;
/*
* get the root object from the context
*/
Object result = getRootVariableValue(context);
if (result == null && !strictRef)
{
/*
* do not trigger an invalid reference if the reference is present, but with a null value
* don't either for a quiet reference or inside an #if/#elseif evaluation context
*/
if ((referenceType != QUIET_REFERENCE || warnInvalidQuietReferences) &&
(numChildren > 0 ||
(!context.containsKey(rootString) || warnInvalidNullReferences) &&
(!onlyTestingReference || warnInvalidTestedReferences)))
{
result = EventHandlerUtil.invalidGetMethod(rsvc, context,
rsvc.getParserConfiguration().getDollarChar() + rootString, null, null, uberInfo);
}
if (astAlternateValue != null && (!DuckType.asBoolean(result, true)))
{
result = astAlternateValue.value(context);
}
return result;
}
/*
* Iteratively work 'down' (it's flat...) the reference
* to get the value, but check to make sure that
* every result along the path is valid. For example:
*
* $hashtable.Customer.Name
*
* The $hashtable may be valid, but there is no key
* 'Customer' in the hashtable so we want to stop
* when we find a null value and return the null
* so the error gets logged.
*/
try
{
Object previousResult = result;
int failedChild = -1;
for (int i = 0; i < numChildren; i++)
{
if (strictRef && result == null)
{
/*
* At this point we know that an attempt is about to be made
* to call a method or property on a null value.
*/
String name = jjtGetChild(i).getFirstTokenImage();
throw new VelocityException("Attempted to access '"
+ name + "' on a null value at "
+ StringUtils.formatFileString(uberInfo.getTemplateName(),
+ jjtGetChild(i).getLine(), jjtGetChild(i).getColumn()),
null, rsvc.getLogContext().getStackTrace());
}
previousResult = result;
result = jjtGetChild(i).execute(result,context);
if (result == null && !strictRef) // If strict and null then well catch this
// next time through the loop
{
failedChild = i;
break;
}
}
if (result == null)
{
if (failedChild == -1)
{
/*
* do not trigger an invalid reference if the reference is present, but with a null value
* don't either for a quiet reference,
* or inside an #if/#elseif evaluation context when there's no child
*/
if ((!context.containsKey(rootString) || warnInvalidNullReferences) &&
(referenceType != QUIET_REFERENCE || warnInvalidQuietReferences) &&
(!onlyTestingReference || warnInvalidTestedReferences || numChildren > 0))
{
result = EventHandlerUtil.invalidGetMethod(rsvc, context,
rsvc.getParserConfiguration().getDollarChar() + rootString, previousResult, null, uberInfo);
}
}
else
{
Node child = jjtGetChild(failedChild);
// do not call bad reference handler if the getter is present
// (it means the getter has been called and returned null)
// do not either for a quiet reference or if the *last* child failed while testing the reference
Object getter = context.icacheGet(child);
if ((getter == null || warnInvalidNullReferences) &&
(referenceType != QUIET_REFERENCE || warnInvalidQuietReferences) &&
(!onlyTestingReference || warnInvalidTestedReferences || failedChild < numChildren - 1))
{
StringBuilder name = new StringBuilder(String.valueOf(rsvc.getParserConfiguration().getDollarChar())).append(rootString);
for (int i = 0; i <= failedChild; i++)
{
Node node = jjtGetChild(i);
if (node instanceof ASTMethod)
{
name.append(".").append(((ASTMethod) node).getMethodName()).append("()");
}
else
{
name.append(".").append(node.getFirstTokenImage());
}
}
if (child instanceof ASTMethod)
{
String methodName = ((ASTMethod) jjtGetChild(failedChild)).getMethodName();
result = EventHandlerUtil.invalidMethod(rsvc, context,
name.toString(), previousResult, methodName, uberInfo);
}
else
{
String property = jjtGetChild(failedChild).getFirstTokenImage();
result = EventHandlerUtil.invalidGetMethod(rsvc, context,
name.toString(), previousResult, property, uberInfo);
}
}
}
}
// Check alternate value at the end of the evaluation
if (astAlternateValue != null && (!DuckType.asBoolean(result, true)))
{
result = astAlternateValue.value(context);
}
return result;
}
catch(MethodInvocationException mie)
{
mie.setReferenceName(rootString);
throw mie;
}
}
finally
{
rsvc.getLogContext().popLogContext();
}
}