Exemplo n.º 1
0
  /**
   * gets the value of the reference and outputs it to the writer.
   *
   * @param context context of data to use in getting value
   * @param writer writer to render to
   * @return True if rendering was successful.
   * @throws IOException
   * @throws MethodInvocationException
   */
  public boolean render(InternalContextAdapter context, Writer writer)
      throws IOException, MethodInvocationException {
    if (referenceType == RUNT) {
      writer.write(rootString);
      return true;
    }

    Object value = null;
    if (escaped && strictEscape) {
      /**
       * If we are in strict mode and the variable is escaped, then don't bother to retreive the
       * value since we won't use it. And if the var is not defined it will throw an exception. Set
       * value to TRUE to fall through below with simply printing $foo, and not \$foo
       */
      value = Boolean.TRUE;
    } else {
      value = execute(null, context);
    }

    String localNullString = null;

    /*
     * if this reference is escaped (\$foo) then we want to do one of two things : 1) if this is
     * a reference in the context, then we want to print $foo 2) if not, then \$foo (its
     * considered schmoo, not VTL)
     */

    if (escaped) {
      localNullString = getNullString(context);

      if (value == null) {
        writer.write(escPrefix);
        writer.write("\\");
        writer.write(localNullString);
      } else {
        writer.write(escPrefix);
        writer.write(localNullString);
      }
      return true;
    }

    /*
     * the normal processing
     *
     * if we have an event cartridge, get a new value object
     */

    value = EventHandlerUtil.referenceInsert(rsvc, context, literal, value);

    String toString = null;
    if (value != null) {
      if (value instanceof Renderable) {
        Renderable renderable = (Renderable) value;
        try {
          if (renderable.render(context, writer)) return true;
        } catch (RuntimeException e) {
          // We commonly get here when an error occurs within a block reference.
          // We want to log where the reference is at so that a developer can easily
          // know where the offending call is located.  This can be seen
          // as another element of the error stack we report to log.
          log.error(
              "Exception rendering "
                  + ((renderable instanceof Reference) ? "block " : "Renderable ")
                  + rootString
                  + " at "
                  + Log.formatFileString(this));
          throw e;
        }
      }

      toString = value.toString();
    }

    if (value == null || toString == null) {
      if (strictRef) {
        if (referenceType != QUIET_REFERENCE) {
          log.error(
              "Prepend the reference with '$!' e.g., $!"
                  + literal().substring(1)
                  + " if you want Velocity to ignore the reference when it evaluates to null");
          if (value == null) {
            throw new VelocityException(
                "Reference "
                    + literal()
                    + " evaluated to null when attempting to render at "
                    + Log.formatFileString(this));
          } else // toString == null
          {
            // This will probably rarely happen, but when it does we want to
            // inform the user that toString == null so they don't pull there
            // hair out wondering why Velocity thinks the value is null.
            throw new VelocityException(
                "Reference "
                    + literal()
                    + " evaluated to object "
                    + value.getClass().getName()
                    + " whose toString() method returned null at "
                    + Log.formatFileString(this));
          }
        }
        return true;
      }

      /*
       * write prefix twice, because it's schmoo, so the \ don't escape each
       * other...
       */
      localNullString = getNullString(context);
      if (!strictEscape) {
        // If in strict escape mode then we only print escape once.
        // Yea, I know.. brittle stuff
        writer.write(escPrefix);
      }
      writer.write(escPrefix);
      writer.write(morePrefix);
      writer.write(localNullString);

      if (logOnNull && referenceType != QUIET_REFERENCE && log.isDebugEnabled()) {
        log.debug(
            "Null reference [template '"
                + getTemplateName()
                + "', line "
                + this.getLine()
                + ", column "
                + this.getColumn()
                + "] : "
                + this.literal()
                + " cannot be resolved.");
      }
      return true;
    } else {
      /*
       * non-null processing
       */
      writer.write(escPrefix);
      writer.write(morePrefix);
      writer.write(toString);

      return true;
    }
  }
Exemplo n.º 2
0
  /**
   * 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;
    }
  }