/** * @param context * @param variable * @return The evaluated value of the variable. * @throws MethodInvocationException */ public Object getVariableValue(Context context, String variable) throws MethodInvocationException { Object obj = null; try { obj = context.get(variable); } catch (RuntimeException e) { log.error( "Exception calling reference $" + variable + " at " + Log.formatFileString(uberInfo)); throw e; } if (strictRef && obj == null) { if (!context.containsKey(variable)) { log.error( "Variable $" + variable + " has not been set at " + Log.formatFileString(uberInfo)); throw new MethodInvocationException( "Variable $" + variable + " has not been set", null, identifier, uberInfo.getTemplateName(), uberInfo.getLine(), uberInfo.getColumn()); } } return obj; }
/** * Sets the value of a complex reference (something like $foo.bar) Currently used by * ASTSetReference() * * @see ASTSetDirective * @param context context object containing this reference * @param value Object to set as value * @return true if successful, false otherwise * @throws MethodInvocationException */ public boolean setValue(InternalContextAdapter context, Object value) throws MethodInvocationException { if (jjtGetNumChildren() == 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 = getVariableValue(context, rootString); if (result == null) { String msg = "reference set is not a valid reference at " + Log.formatFileString(uberInfo); log.error(msg); 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).getFirstToken().image; throw new MethodInvocationException( "Attempted to access '" + name + "' on a null value", null, name, uberInfo.getTemplateName(), jjtGetChild(i + 1).getLine(), jjtGetChild(i + 1).getColumn()); } String msg = "reference set is not a valid reference at " + Log.formatFileString(uberInfo); log.error(msg); 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 " + Log.formatFileString(astIndex)); } 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(), 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, 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(), 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 " + Log.formatFileString(uberInfo); log.error(msg, e); throw new VelocityException(msg, e); } return true; }
/** * gets an Object that 'is' the value of the reference * * @param o unused Object parameter * @param context context used to generate value * @return The execution result. * @throws MethodInvocationException */ public Object execute(Object o, InternalContextAdapter context) throws MethodInvocationException { if (referenceType == RUNT) return null; /* * get the root object from the context */ Object result = getVariableValue(context, rootString); if (result == null && !strictRef) { return EventHandlerUtil.invalidGetMethod( rsvc, context, getDollarBang() + rootString, null, null, uberInfo); } /* * 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).getFirstToken().image; throw new VelocityException( "Attempted to access '" + name + "' on a null value at " + Log.formatFileString( uberInfo.getTemplateName(), +jjtGetChild(i).getLine(), jjtGetChild(i).getColumn())); } 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) { result = EventHandlerUtil.invalidGetMethod( rsvc, context, getDollarBang() + rootString, previousResult, null, uberInfo); } else { StringBuffer name = new StringBuffer(getDollarBang()).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.getFirstToken().image); } } if (jjtGetChild(failedChild) instanceof ASTMethod) { String methodName = ((ASTMethod) jjtGetChild(failedChild)).getMethodName(); result = EventHandlerUtil.invalidMethod( rsvc, context, name.toString(), previousResult, methodName, uberInfo); } else { String property = jjtGetChild(failedChild).getFirstToken().image; result = EventHandlerUtil.invalidGetMethod( rsvc, context, name.toString(), previousResult, property, uberInfo); } } } return result; } catch (MethodInvocationException mie) { mie.setReferenceName(rootString); throw mie; } }