PebbleTemplates 模版解析处理简单说明

以下是一个简单的说明如何集成起来的,详细的可以阅读完整源码

调用

在getPebbleTemplate 部分,参考处理

private PebbleTemplate getPebbleTemplate(String templateName, Loader loader, Object cacheKey) {
    // 获取reader
    Reader templateReader = loader.getReader(cacheKey);
 
    try {
      this.logger.trace("Tokenizing template named {}", templateName);
      // 词法解析
      LexerImpl lexer = new LexerImpl(this.syntax,
          this.extensionRegistry.getUnaryOperators().values(),
          this.extensionRegistry.getBinaryOperators().values());
      // 转换为TokenStream
      TokenStream tokenStream = lexer.tokenize(templateReader, templateName);
      this.logger.trace("TokenStream: {}", tokenStream);
      // 解析器
      Parser parser = new ParserImpl(this.extensionRegistry.getUnaryOperators(),
          this.extensionRegistry.getBinaryOperators(), this.extensionRegistry.getTokenParsers(),
              this.parserOptions);
      RootNode root = parser.parse(tokenStream);
 
      PebbleTemplateImpl instance = new PebbleTemplateImpl(this, root, templateName);
 
      for (NodeVisitorFactory visitorFactory : this.extensionRegistry.getNodeVisitors()) {
        visitorFactory.createVisitor(instance).visit(root);
      }
 
      return instance;
 
    } finally {
      try {
        templateReader.close();
      } catch (IOException e) {
        // can't do much about it
      }
    }
  }

解析

public BodyNode subparse(StoppingCondition stopCondition) {
     // 通过rootnode ,进行遍历解析转换为RenderableNode(这个也是我们进行扩展开发的处理,对于自己开发的TokenParser 需要返回一个RenderableNode)
    // these nodes will be the children of the root node
    List<RenderableNode> nodes = new ArrayList<>();
 
    Token token;
    while (!this.stream.isEOF()) {
 
      switch (this.stream.current().getType()) {
        case TEXT:
 
          /*
           * The current token is a text token. Not much to do here other
           * than convert it to a text Node.
           */
          token = this.stream.current();
          nodes.add(new TextNode(token.getValue(), token.getLineNumber()));
          this.stream.next();
          break;
 
        case PRINT_START:
 
          /*
           * We are entering a print delimited region at this point. These
           * regions will contain some sort of expression so let's pass
           * control to our expression parser.
           */
 
          // go to the next token because the current one is just the
          // opening delimiter
          token = this.stream.next();
 
          Expression<?> expression = this.expressionParser.parseExpression();
          nodes.add(new PrintNode(expression, token.getLineNumber()));
 
          // we expect to see a print closing delimiter
          this.stream.expect(Token.Type.PRINT_END);
 
          break;
 
        case EXECUTE_START:
 
          // go to the next token because the current one is just the
          // opening delimiter
          this.stream.next();
 
          token = this.stream.current();
 
          /*
           * We expect a name token at the beginning of every block.
           *
           * We do not use stream.expect() because it consumes the current
           * token. The current token may be needed by a token parser
           * which has provided a stopping condition. Ex. the 'if' token
           * parser may need to check if the current token is either
           * 'endif' or 'else' and act accordingly, thus we should not
           * consume it.
           */
          if (!Token.Type.NAME.equals(token.getType())) {
            throw new ParserException(null, "A block must start with a tag name.",
                token.getLineNumber(),
                this.stream.getFilename());
          }
 
          // If this method was executed using a TokenParser and
          // that parser provided a stopping condition (ex. checking
          // for the 'endif' token) let's check for that condition
          // now.
          if (stopCondition != null && stopCondition.evaluate(token)) {
            return new BodyNode(token.getLineNumber(), nodes);
          }
 
          // find an appropriate parser for this name
          TokenParser tokenParser = this.tokenParsers.get(token.getValue());
 
          if (tokenParser == null) {
            throw new ParserException(null,
                String.format("Unexpected tag name \"%s\"", token.getValue()),
                token.getLineNumber(), this.stream.getFilename());
          }
 
          RenderableNode node = tokenParser.parse(token, this);
 
          // node might be null (ex. "extend" token parser)
          if (node != null) {
            nodes.add(node);
          }
 
          break;
 
        default:
          throw new ParserException(null, "Parser ended in undefined state.",
              this.stream.current().getLineNumber(),
              this.stream.getFilename());
      }
    }
 
    // create the root node with the children that we have found
    return new BodyNode(this.stream.current().getLineNumber(), nodes);
  }

渲染处理

// 使用调用了rootNode 的render方法,当然还包含了对于层级关系的处理
// 每个node 的实现都会包含一个render 方法,内部会调用下PebbleTemplateImpl 提供的一些替换方法
private void evaluate(Writer writer, EvaluationContextImpl context) throws IOException {
if (context.getExecutorService() != null) {
  writer = new FutureWriter(writer);
}
writer = LimitedSizeWriter.from(writer, context);
this.rootNode.render(this, writer, context);
 
/*
 * If the current template has a parent then we know the current template
 * was only used to evaluate a very small subset of tags such as "set" and "import".
 * We now evaluate the parent template as to evaluate all of the actual content.
 * When evaluating the parent template, it will check the child template for overridden blocks.
 */
if (context.getHierarchy().getParent() != null) {
  PebbleTemplateImpl parent = context.getHierarchy().getParent();
  context.getHierarchy().ascend();
  parent.evaluate(writer, context);
}
writer.flush();
}

说明

PebbleTemplates 的灵活性以及代码还是比较简洁的,简单了解一些内部模版解析处理,对于开发还是比较有用的

参考资料

https://github.com/PebbleTemplates/pebble
https://pebbletemplates.io/
https://github.com/rongfengliang/pebbletemplates-learning.git

posted on 2023-03-17 12:11  荣锋亮  阅读(70)  评论(0编辑  收藏  举报

导航