public boolean setValue()

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();
        }
    }