예제 #1
0
  /**
   * Intialize the Runtime macro. At the init time no implementation so we just save the values to
   * use at the render time.
   *
   * @param rs runtime services
   * @param context InternalContextAdapter
   * @param node node containing the macro call
   */
  public void init(RuntimeServices rs, InternalContextAdapter context, Node node) {
    super.init(rs, context, node);
    rsvc = rs;
    this.node = node;

    /**
     * Apply strictRef setting only if this really looks like a macro, so strict mode doesn't balk
     * at things like #E0E0E0 in a template. compare with ")" is a simple #foo() style macro,
     * comparing to "#end" is a block style macro. We use starts with because the token may end with
     * '\n'
     */
    Token t = node.getLastToken();
    if (t.image.startsWith(")") || t.image.startsWith("#end")) {
      strictRef = rsvc.getBoolean(RuntimeConstants.RUNTIME_REFERENCES_STRICT, false);
    }

    // Validate that none of the arguments are plain words, (VELOCITY-614)
    // they should be string literals, references, inline maps, or inline lists
    for (int n = 0; n < node.jjtGetNumChildren(); n++) {
      Node child = node.jjtGetChild(n);
      if (child.getType() == ParserTreeConstants.JJTWORD) {
        badArgsErrorMsg =
            "Invalid arg '"
                + child.getFirstToken().image
                + "' in macro #"
                + macroName
                + " at "
                + Log.formatFileString(child);

        if (strictRef) // If strict, throw now
        {
          /* indicate col/line assuming it starts at 0
           * this will be corrected one call up  */
          throw new TemplateInitException(badArgsErrorMsg, context.getCurrentTemplateName(), 0, 0);
        }
      }
    }
  }
예제 #2
0
  /**
   * This method is used with BlockMacro when we want to render a macro with a body AST.
   *
   * @param context
   * @param writer
   * @param node
   * @param body AST block that was enclosed in the macro body.
   * @return true if the rendering is successful
   * @throws IOException
   * @throws ResourceNotFoundException
   * @throws ParseErrorException
   * @throws MethodInvocationException
   */
  public boolean render(InternalContextAdapter context, Writer writer, Node node, Renderable body)
      throws IOException, ResourceNotFoundException, ParseErrorException,
          MethodInvocationException {
    VelocimacroProxy vmProxy = null;
    String renderingTemplate = context.getCurrentTemplateName();

    /** first look in the source template */
    Object o = rsvc.getVelocimacro(macroName, getTemplateName(), renderingTemplate);

    if (o != null) {
      // getVelocimacro can only return a VelocimacroProxy so we don't need the
      // costly instanceof check
      vmProxy = (VelocimacroProxy) o;
    }

    /** if not found, look in the macro libraries. */
    if (vmProxy == null) {
      List macroLibraries = context.getMacroLibraries();
      if (macroLibraries != null) {
        for (int i = macroLibraries.size() - 1; i >= 0; i--) {
          o = rsvc.getVelocimacro(macroName, (String) macroLibraries.get(i), renderingTemplate);

          // get the first matching macro
          if (o != null) {
            vmProxy = (VelocimacroProxy) o;
            break;
          }
        }
      }
    }

    if (vmProxy != null) {
      try {
        // mainly check the number of arguments
        vmProxy.checkArgs(context, node, body != null);
      } catch (TemplateInitException die) {
        throw new ParseErrorException(
            die.getMessage() + " at " + Log.formatFileString(node), new Info(node));
      }

      if (badArgsErrorMsg != null) {
        throw new TemplateInitException(
            badArgsErrorMsg, context.getCurrentTemplateName(), node.getColumn(), node.getLine());
      }

      try {
        preRender(context);
        return vmProxy.render(context, writer, node, body);
      } catch (StopCommand stop) {
        if (!stop.isFor(this)) {
          throw stop;
        }
        return true;
      } catch (RuntimeException e) {
        /**
         * We catch, the exception here so that we can record in the logs the template and line
         * number of the macro call which generate the exception. This information is especially
         * important for multiple macro call levels. this is also true for the following catch
         * blocks.
         */
        rsvc.getLog()
            .error("Exception in macro #" + macroName + " called at " + Log.formatFileString(node));
        throw e;
      } catch (IOException e) {
        rsvc.getLog()
            .error("Exception in macro #" + macroName + " called at " + Log.formatFileString(node));
        throw e;
      } finally {
        postRender(context);
      }
    } else if (strictRef) {
      throw new VelocityException(
          "Macro '#" + macroName + "' is not defined at " + Log.formatFileString(node));
    }

    /** If we cannot find an implementation write the literal text */
    writer.write(getLiteral());
    return true;
  }