freemarker 自定义布局指令

 
 
在项目中使用freemarker模板引擎时,经常感觉布局上有些功能没法实现,如下:
<html>
     <head>
          <title>xxxx</title>
          <#inclue “url1”>
     </head>
     <body>
          <div><#include “header.ftl”></div>
          <div>
               <div><#inclued “left.ftl”></div>
               <div>
                   part1
               </div>
               <div>
                    part2
               </div>
          </div>
          <div>
               part3 
          </div>
          <div><#include “footer.ftl”></div>
     </body>
</html>

 

   
其实大部分页面都是这种模板,只是改动某一块或者几块,但是每个ftl文件,都得包含这些内容,万一有改动,得全部ftl做修改,比较麻烦。
 
假设自定义三个指令parent, layout, content ,可能会方便很多。
 
layout.ftl
<html>
     <head>
          <@layout name=“title">
               <title>default title</title>
          </@layout>
          <#inclue “url1”>
     </head>
     <body>
          <div><#include “header.ftl”></div>
          <div>
               <div><#inclued “left.ftl”></div>
               <div>
                   <@layout name=“part1">
                         default part1
                    </@layout>
               </div>
               <div>
                    <@layout name=“part2">
                         default part2
                    </@layout>
               </div>
          </div>
          <div>
               <@layout name=“part3">
                    default part3 
                </@layout>
          </div>
          <div><#include “footer.ftl”></div>
     </body>
</html>

 

 
part1.ftl 
<@content name=“title”>
     <title>part1</title>
<@content>
<@content name=“part1”>
       part1 …..
</@content>
<@parent name=“layout.ftl”/>

 

 
part2.ftl 
<@content name=“title”>
     <title>part2</title>
<@content>
<@content name=“part1”>
       part2 …..
</@content>
<@parent name=“layout.ftl”/>

 

 
part3.ftl 
<@content name=“title”>
     <title>part3</title>
<@content>
<@content name=“part1”>
       part3 …..
</@content>
<@parent name=“layout.ftl”/>

 

 
part1.ftl解析完全部的文本格式为:
 
<html>
     <head>
               <title>part1</title>
          <#inclue “url1”>
     </head>
     <body>
          <div><#include “header.ftl”></div>
          <div>
               <div><#inclued “left.ftl”></div>
               <div>
                        part1 …..
               </div>
               <div>
                      default part2
               </div>
          </div>
          <div>
               default part3 
          </div>
          <div><#include “footer.ftl”></div>
     </body>
</html>

 

 
part2.ftl,part3.ftl 可脑补出来了。
 
这三个标签怎么定义呢?
 
public class ContentDirective implements TemplateDirectiveModel {
 
  public void execute(Environment env,
            @SuppressWarnings("rawtypes") Map params, TemplateModel[] loopVars,
            TemplateDirectiveBody body) throws TemplateException, IOException {
    // 工具方法,获取指令名
    String name = ParamUtils.getDirectiveName(params) ;
    // body类的代理类,主要是由于env存放时现在对象为TemplateModel的子类或者本身
    TemplateDirectiveBodyOverrideWraper current = new TemplateDirectiveBodyOverrideWraper(body);
    // 把content的内容,先保存着
    env.setVariable(name, current);
  }
}
 
public class LayoutDirective  implements TemplateDirectiveModel {
 
  public void execute(Environment env,
            @SuppressWarnings("rawtypes") Map params, TemplateModel[] loopVars,
            TemplateDirectiveBody body) throws TemplateException, IOException {
    // 工具方法,获取指令名
    String name = ParamUtils.getDirectiveName(params) ;
    // 找寻之前是否有保存
    TemplateDirectiveBodyOverrideWraper overrideBody = (TemplateDirectiveBodyOverrideWraper) env.getVariable(name) ;
    if(overrideBody == null) {
      // 没有保存,是否有默认
      if(body != null) {
        // 输出默认的
        body.render(env.getOut());
      }
    }else {
      // 输出保存的
      overrideBody.render(env.getOut());
    }
  }  
}
 
public class ParentDirective  implements TemplateDirectiveModel {
 
  public void execute(Environment env,
            @SuppressWarnings("rawtypes") Map params, TemplateModel[] loopVars,
            TemplateDirectiveBody body) throws TemplateException, IOException {
 
    String name = ParamUtils.getParamValue(params,"name");
    String encoding = ParamUtils.getParamValue(params, "encoding",null);
    // 引入文件
    env.include(name, encoding, true);
  }
}

 

 
当然,freemarker模板引擎解析ftl文件时,是从上往下循着标签解析,在回到之前的样例part1.ftl结构,大概就明白了。
 
为方便理解,在补上其他两个类的源码:
public class ParamUtils {
 
    private static String PREFIX = "_freemarker_directive_prefix_";
 
    public static String getDirectiveName(@SuppressWarnings("rawtypes") Map params) throws TemplateModelException {
        return PREFIX + getParamValue(params, "name") ;
    }
 
    public static String getParamValue(@SuppressWarnings("rawtypes") Map params, String key) throws TemplateModelException {
        Object value = params.get(key);
        if(value == null || "".equals(value.toString())) {
            throw new TemplateModelException("not found directive property : name");
        }
 
        return value.toString() ;
    }
 
    public static String getParamValue(@SuppressWarnings("rawtypes") Map params, String key, String defaultValue) throws TemplateException {
        Object value = params.get(key);
        return value == null ? defaultValue : value.toString();
    }
 
}
 
public class TemplateDirectiveBodyOverrideWraper implements TemplateDirectiveBody,TemplateModel {
 
    private TemplateDirectiveBody body;
 
    public TemplateDirectiveBodyOverrideWraper(TemplateDirectiveBody body) {
        super();
        this.body = body;
    }
 
    public void render(Writer out) throws TemplateException, IOException {
        if(body == null) return;
        body.render(out);
    }
}
View Code

 

 
 
 
 
 
 
 
 
 
 
posted @ 2016-08-18 21:30  li.zhiliang  阅读(726)  评论(0)    收藏  举报