/**
   * 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);
      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).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;
    }
  }
  /**
   * @see
   *     org.apache.velocity.runtime.parser.node.SimpleNode#init(org.apache.velocity.context.InternalContextAdapter,
   *     java.lang.Object)
   */
  public Object init(InternalContextAdapter context, Object data) throws TemplateInitException {
    super.init(context, data);

    strictEscape = rsvc.getBoolean(RuntimeConstants.RUNTIME_REFERENCES_STRICT_ESCAPE, false);
    strictRef = rsvc.getBoolean(RuntimeConstants.RUNTIME_REFERENCES_STRICT, false);
    toStringNullCheck = rsvc.getBoolean(RuntimeConstants.DIRECTIVE_IF_TOSTRING_NULLCHECK, true);

    /*
     *  the only thing we can do in init() is getRoot()
     *  as that is template based, not context based,
     *  so it's thread- and context-safe
     */

    rootString = getRoot().intern();

    numChildren = jjtGetNumChildren();

    // This is an expensive call, so get it now.
    literal = literal();

    /*
     * and if appropriate...
     */

    if (numChildren > 0) {
      Node lastNode = jjtGetChild(numChildren - 1);
      if (lastNode instanceof ASTIndex) astIndex = (ASTIndex) lastNode;
      else identifier = lastNode.getFirstToken().image;
    }

    /*
     * make an uberinfo - saves new's later on
     */
    uberInfo = new Info(getTemplateName(), getLine(), getColumn());

    /*
     * track whether we log invalid references
     */
    logOnNull = rsvc.getBoolean(RuntimeConstants.RUNTIME_LOG_REFERENCE_LOG_INVALID, true);

    /**
     * In the case we are referencing a variable with #if($foo) or #if( ! $foo) then we allow
     * variables to be undefined and we set strictRef to false so that if the variable is undefined
     * an exception is not thrown.
     */
    if (strictRef && numChildren == 0) {
      logOnNull = false; // Strict mode allows nulls

      Node node = this.jjtGetParent();
      if (node instanceof ASTNotNode // #if( ! $foo)
          || node instanceof ASTExpression // #if( $foo )
          || node instanceof ASTOrNode // #if( $foo || ...
          || node instanceof ASTAndNode) // #if( $foo && ...
      {
        // Now scan up tree to see if we are in an If statement
        while (node != null) {
          if (node instanceof ASTIfStatement) {
            strictRef = false;
            break;
          }
          node = node.jjtGetParent();
        }
      }
    }

    return data;
  }