in velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java [730:924]
public boolean setValue(InternalContextAdapter context, Object value)
throws MethodInvocationException
{
try
{
rsvc.getLogContext().pushLogContext(this, uberInfo);
if (astAlternateValue != null)
{
log.error("reference set cannot have a default value {}",
StringUtils.formatFileString(uberInfo));
return false;
}
if (numChildren == 0)
{
context.put(rootString, value);
return true;
}
/*
* The rootOfIntrospection is the object we will
* retrieve from the Context. This is the base
* object we will apply reflection to.
*/
Object result = getRootVariableValue(context);
if (result == null)
{
log.error("reference set is not a valid reference at {}",
StringUtils.formatFileString(uberInfo));
return false;
}
/*
* How many child nodes do we have?
*/
for (int i = 0; i < numChildren - 1; i++)
{
result = jjtGetChild(i).execute(result, context);
if (result == null)
{
if (strictRef)
{
String name = jjtGetChild(i+1).getFirstTokenImage();
throw new MethodInvocationException("Attempted to access '"
+ name + "' on a null value", null, rsvc.getLogContext().getStackTrace(), name, uberInfo.getTemplateName(),
jjtGetChild(i+1).getLine(), jjtGetChild(i+1).getColumn());
}
log.error("reference set is not a valid reference at {}",
StringUtils.formatFileString(uberInfo));
return false;
}
}
if (astIndex != null)
{
// If astIndex is not null then we are actually setting an index reference,
// something of the form $foo[1] =, or in general any reference that ends with
// the brackets. This means that we need to call a more general method
// of the form set(Integer, <something>), or put(Object, <something), where
// the first parameter is the index value and the second is the LHS of the set.
Object argument = astIndex.jjtGetChild(0).value(context);
// If negative, turn -1 into (size - 1)
argument = ASTIndex.adjMinusIndexArg(argument, result, context, astIndex);
Object [] params = {argument, value};
Class<?>[] paramClasses = {params[0] == null ? null : params[0].getClass(),
params[1] == null ? null : params[1].getClass()};
String methodName = "set";
VelMethod method = ClassUtils.getMethod(methodName, params, paramClasses,
result, context, astIndex, false);
if (method == null)
{
// If we can't find a 'set' method, lets try 'put', This warrents a little
// investigation performance wise... if the user is using the hash
// form $foo["blaa"], then it may be expensive to first try and fail on 'set'
// then go to 'put'? The problem is that getMethod will try the cache, then
// perform introspection on 'result' for 'set'
methodName = "put";
method = ClassUtils.getMethod(methodName, params, paramClasses,
result, context, astIndex, false);
}
if (method == null)
{
// couldn't find set or put method, so bail
if (strictRef)
{
throw new VelocityException(
"Found neither a 'set' or 'put' method with param types '("
+ printClass(paramClasses[0]) + "," + printClass(paramClasses[1])
+ ")' on class '" + result.getClass().getName()
+ "' at " + StringUtils.formatFileString(astIndex)
, null, rsvc.getLogContext().getStackTrace());
}
return false;
}
try
{
method.invoke(result, params);
}
catch(RuntimeException e)
{
// Kludge since invoke throws Exception, pass up Runtimes
throw e;
}
catch(Exception e)
{
throw new MethodInvocationException(
"Exception calling method '"
+ methodName + "("
+ printClass(paramClasses[0]) + "," + printClass(paramClasses[1])
+ ")' in " + result.getClass(),
e.getCause(), rsvc.getLogContext().getStackTrace(), identifier, astIndex.getTemplateName(), astIndex.getLine(),
astIndex.getColumn());
}
return true;
}
/*
* We support two ways of setting the value in a #set($ref.foo = $value ):
* 1) ref.setFoo( value )
* 2) ref,put("foo", value ) to parallel the get() map introspection
*/
try
{
VelPropertySet vs =
rsvc.getUberspect().getPropertySet(result, identifier,
value, uberInfo);
if (vs == null)
{
if (strictRef)
{
throw new MethodInvocationException("Object '" + result.getClass().getName() +
"' does not contain property '" + identifier + "'", null, rsvc.getLogContext().getStackTrace(), identifier,
uberInfo.getTemplateName(), uberInfo.getLine(), uberInfo.getColumn());
}
else
{
return false;
}
}
vs.invoke(result, value);
}
catch(InvocationTargetException ite)
{
/*
* this is possible
*/
throw new MethodInvocationException(
"ASTReference: Invocation of method '"
+ identifier + "' in " + result.getClass()
+ " threw exception "
+ ite.getTargetException().toString(),
ite.getTargetException(), rsvc.getLogContext().getStackTrace(), identifier, getTemplateName(), this.getLine(), this.getColumn());
}
/*
* pass through application level runtime exceptions
*/
catch( RuntimeException e )
{
throw e;
}
catch(Exception e)
{
/*
* maybe a security exception?
*/
String msg = "ASTReference setValue(): exception: " + e
+ " template at " + StringUtils.formatFileString(uberInfo);
log.error(msg, e);
throw new VelocityException(msg, e, rsvc.getLogContext().getStackTrace());
}
return true;
}
finally
{
rsvc.getLogContext().popLogContext();
}
}