个人博客 地址:http://www.wenhaofan.com/article/20190304102258

平时在项目中使用短信模板 邮件模板以及 站内消息通知等文本模板一般都是通过手动的字符串拼接来完成,例如:"欢迎"+user.getName()+"加入俱乐部。"

然而这种方法不仅代码难看而且还不方便管理,因此为了更方便的在项目中管理使用这类文本模板,参考JFinal源码中的activerecord管理sql的代码来扩展了Enjoy的指令。

1.扩展代码

package com.autu.common.keys;

import com.jfinal.kit.StrKit;
import com.jfinal.template.Directive;
import com.jfinal.template.Env;
import com.jfinal.template.expr.ast.Const;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope; /**
* KeysDirective
*/
public class KeysDirective extends Directive { static final String KEYS_DIRECTIVE="_KEYS_DIRECTIVE"; private String id; public void setExprList(ExprList exprList) {
if (exprList.length() == 0) {
throw new ParseException("The parameter of #keys directive can not be blank", location);
}
if (exprList.length() > 1) {
throw new ParseException("Only one parameter allowed for #keys directive", location);
}
Expr expr = exprList.getExpr(0);
if (expr instanceof Const && ((Const)expr).isStr()) {
} else {
throw new ParseException("The parameter of #keys directive must be String", location);
} this.id = ((Const)expr).getStr();
} public void exec(Env env, Scope scope, Writer writer) {
String beforeKey=(String)scope.get(KeysDirective.KEYS_DIRECTIVE); String key = StrKit.isBlank(beforeKey) ? id : beforeKey + "." + id;
scope.set(KEYS_DIRECTIVE, key); stat.exec(env, scope, writer); } public boolean hasEnd() {
return true;
}
}
package com.autu.common.keys;

import java.util.Map;

import com.jfinal.kit.StrKit;
import com.jfinal.template.Directive;
import com.jfinal.template.Env;
import com.jfinal.template.Template;
import com.jfinal.template.expr.ast.Const;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope; /**
* KeyDirective
*/
public class KeyDirective extends Directive { static final String KEY_DIRECTIVE="_KEY_DIRECTIVE"; private String id; public void setExprList(ExprList exprList) {
if (exprList.length() == 0) {
throw new ParseException("The parameter of #key directive can not be blank", location);
}
if (exprList.length() > 1) {
throw new ParseException("Only one parameter allowed for #key directive", location);
}
Expr expr = exprList.getExpr(0);
if (expr instanceof Const && ((Const)expr).isStr()) {
} else {
throw new ParseException("The parameter of #key directive must be String", location);
} this.id = ((Const)expr).getStr();
} @SuppressWarnings("unchecked")
public void exec(Env env, Scope scope, Writer writer) {
String nameSpace = (String)scope.get(KeysDirective.KEYS_DIRECTIVE);
String key = StrKit.isBlank(nameSpace) ? id : nameSpace + "." + id;
Map<String, Template> keyTemplateMap = (Map<String, Template>)scope.get(KeyKit.KEY_TEMPLATE_MAP_KEY);
if (keyTemplateMap.containsKey(key)) {
throw new ParseException("Key already exists with key : " + key, location);
} keyTemplateMap.put(key, new Template(env, stat));
} public boolean hasEnd() {
return true;
}
}
package com.autu.common.keys;

import com.jfinal.template.source.ISource;

/**
* 封装 key 模板源
*/
class KeySource { String file;
ISource source; KeySource(String file) {
this.file = file;
this.source = null;
} KeySource(ISource source) {
this.file = null;
this.source = source;
} boolean isFile() {
return file != null;
}
}
package com.autu.common.keys;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import com.jfinal.kit.Kv;
import com.jfinal.kit.StrKit;
import com.jfinal.template.Engine;
import com.jfinal.template.Template;
import com.jfinal.template.source.ISource;
import com.jfinal.template.stat.ParseException; /**
* Email Kit
*/
public class KeyKit { static final String KEY_TEMPLATE_MAP_KEY = "_KEY_TEMPLATE_MAP_";
static final boolean DEFAULT_DEV_MODE=false;
public static final String MAIN_CONFIG_NAME = "keys";
public String configName;
private boolean devMode=false;
private Engine engine;
private List<KeySource> keySourceList = new ArrayList<KeySource>();
public Map<String, Template> keyTemplateMap; private static final Map<String,KeyKit> keyKitMap=new HashMap<>(); public static KeyKit use(String configName) {
return keyKitMap.get(configName);
} public static KeyKit use() {
return use(MAIN_CONFIG_NAME);
} private KeyKit(boolean devMode) {
this(MAIN_CONFIG_NAME, devMode);
} public static KeyKit load(String configName) { if(keyKitMap.containsKey(configName)) {
return keyKitMap.get(configName);
} return new KeyKit(configName,DEFAULT_DEV_MODE);
} public static KeyKit load(boolean devMode) {
if(keyKitMap.containsKey(MAIN_CONFIG_NAME)) {
return keyKitMap.get(MAIN_CONFIG_NAME);
}
return new KeyKit(devMode);
} public static KeyKit load(String configName, boolean devMode) {
if(keyKitMap.containsKey(configName)) {
return keyKitMap.get(configName);
}
return new KeyKit(configName, devMode);
} private KeyKit(String configName, boolean devMode) {
this.configName = configName;
this.devMode = devMode; if(keyKitMap.containsKey(configName)) {
throw new ParseException("Key already exists", null );
} engine = new Engine(configName);
engine.setDevMode(devMode);
engine.addDirective("key", KeyDirective.class);
engine.addDirective("keys", KeysDirective.class);
engine.addSharedMethod(new StrKit()); keyKitMap.put(configName, this);
} public KeyKit(String configName) {
this(configName, false);
} public Engine getEngine() {
return engine;
} public void setDevMode(boolean devMode) {
this.devMode = devMode;
engine.setDevMode(devMode);
} public KeyKit setBaseKeyTemplatePath(String baseKeyTemplatePath) {
engine.setBaseTemplatePath(baseKeyTemplatePath);
return this;
} public KeyKit addTemplate(String KeyTemplate) {
if (StrKit.isBlank(KeyTemplate)) {
throw new IllegalArgumentException("keyTemplate can not be blank");
}
keySourceList.add(new KeySource(KeyTemplate));
return this;
} public void addTemplate(ISource keyTemplate) {
if (keyTemplate == null) {
throw new IllegalArgumentException("keyTemplate can not be null");
}
keySourceList.add(new KeySource(keyTemplate));
} public synchronized KeyKit parseKeysTemplate() {
Map<String, Template> keyTemplateMap = new HashMap<String, Template>(512, 0.5F);
for (KeySource ss : keySourceList) {
Template template = ss.isFile() ? engine.getTemplate(ss.file) : engine.getTemplate(ss.source);
Map<Object, Object> data = new HashMap<Object, Object>();
data.put(KEY_TEMPLATE_MAP_KEY, keyTemplateMap);
template.renderToString(data);
}
this.keyTemplateMap = keyTemplateMap;
return this;
} private void reloadModifiedKeyTemplate() {
engine.removeAllTemplateCache(); // 去除 Engine 中的缓存,以免 get 出来后重新判断 isModified
parseKeysTemplate();
} private boolean isKeyTemplateModified() {
for (Template template : keyTemplateMap.values()) {
if (template.isModified()) {
return true;
}
}
return false;
} private Template getKeyTemplate(String key) {
Template template = keyTemplateMap.get(key);
if (template == null) { // 此 if 分支,处理起初没有定义,但后续不断追加 key 的情况
if (!devMode) {
return null;
}
if (isKeyTemplateModified()) {
synchronized (this) {
if (isKeyTemplateModified()) {
reloadModifiedKeyTemplate();
template = keyTemplateMap.get(key);
}
}
}
return template;
} if (devMode && template.isModified()) {
synchronized (this) {
template = keyTemplateMap.get(key);
if (template.isModified()) {
reloadModifiedKeyTemplate();
template = keyTemplateMap.get(key);
}
}
}
return template;
} /**
* 示例: 1:模板 定义 #key("key")
*
* #end
*
* 2:java 代码 getContent("key", Kv);
*/
public String getContent(String key, Kv kv) {
Template template = getKeyTemplate(key);
if (template == null) {
return null;
}
return template.renderToString(kv);
} public java.util.Set<java.util.Map.Entry<String, Template>> getKeyMapEntrySet() {
return keyTemplateMap.entrySet();
} public String toString() {
return "KeyTplKit for config : " + configName;
}
}

2.模板文件

3.1 all_emails.tpl

#keys("comment")
#include("innerKeys.tpl")
#end

3.2 comment.tpl

#key("comment_title")
[#(config.title)评论通知] Re:#(title)
#end

3.3 inner.tpl

#keys("inner")
#include("comment.tpl")
#end

3.创建管理工具

KeyKit.load(p.getBoolean("devMode", false))
.setBaseKeyTemplatePath(PathKit.getRootClassPath() + "/email")
.addTemplate("all_emails.tpl")
.parseKeysTemplate();

4.使用

   String keyContent=	KeyKit.use().getContent("comment.inner.comment_title",
Kv.by("config",new Config().setTitle("test")).set("title", "title1"));
System.out.println(keyContent);

输出结果

  [test评论通知] Re:title1

5.说明

此处演示的为多层嵌套keys的使用,单层和jfinal的sql管理一样使用

JFinal Enjoy指令扩展管理常用文本模板的更多相关文章

  1. T4文本模板转换过程

    T4文本模板转换过程将文本模板文件作为输入,生成一个新的文本文件作为输出. 例如,可以使用文本模板生成 Visual Basic 或 C# 代码,还可以生成 HTML 报告. 有三个组件参与这一过程: ...

  2. T4 文本模板编写准则

    如果要在 Visual Studio 中生成程序代码或其他应用程序资源,遵守以下一般准则可能非常有帮助. 它们并不是一成不变的规则. 设计时 T4 模板准则 设计时 T4 模板是在设计时在 Visua ...

  3. 编写 T4 文本模板

    文本模板由以下部件组成: 1)指令 - 控制模板处理方式的元素. 2)文本块 - 直接复制到输出的内容. 3)控制块 - 向文本插入可变值并控制文本的条件或重复部件的程序代码. 指令: 指令是控制模板 ...

  4. 使用 T4 文本模板生成设计时代码

      使用设计时 T4 文本模板,您可以在 Visual Studio 项目中生成程序代码和其他文件. 通常,您编写一些模板,以便它们根据来自模型的数据来改变所生成的代码. 模型是包含有关应用程序要求的 ...

  5. MVC开发T4代码生成之一----文本模板基础

    T4文本模板 T4全写为Text Template Transformation Toolkit,是一种编程辅助工具,用来使程序代码自(懒)动(猿)生(福)成(利)的工具.MVC开发中大量使用了T4模 ...

  6. T4文本模板

    <#...#> 可以包含语句 <#=...#>  用于表达式,提供“输出”操作 <#+ ...> 使用类功能控制块向文本模板添加方法.属性.字段,必须作为文件中最后 ...

  7. 第3章-Vue.js 指令扩展 和 todoList练习

    一.学习目标 了解Vue.js指令的实现原理 理解v-model指令的高级用法 能够使用Vue.js 指令完成 todoList 练习(重点+难点) 二.todoList练习效果展示 2.1.效果图展 ...

  8. T4模板之文本模板

    网址:https://docs.microsoft.com/en-us/visualstudio/modeling/design-time-code-generation-by-using-t4-te ...

  9. 【翻译】Scriban README 文本模板语言和.NET引擎

    scriban Scriban是一种快速.强大.安全和轻量级的文本模板语言和.NET引擎,具有解析liquid模板的兼容模式 Github https://github.com/lunet-io/sc ...

随机推荐

  1. yamlpy接口测试框架

    1.思路: yamlpy即为yaml文件+pytest单元测试框架的缩写, 可以看作是一个脚手架工具, 可以快速生成项目的各个目录与文件, 只需维护一份或者多份yaml文件即可, 不需要大量写代码. ...

  2. Xilinx FPGA控制器的Everspin STT-DDR4设计指南

    自旋转移扭矩磁阻随机存取存储器(STT-MRAM)是一种持久性存储技术,可利用各种工业标准接口提供性能,持久性和耐用性. Everspin推出了STT-MRAM产品,该产品利用称为JE-DDR4的JE ...

  3. cf1280B

    题意:给出一个n*m的矩阵,矩阵中的元素要么P要么是A 每次可以选择一个的子矩形,然后将矩阵中每一行都变为第一行,或者将矩阵中每一列都变为第一列 要求用最少的次数将矩阵中所有元素都变成A 题解:分类讨 ...

  4. C#MVC实现为雇员配置角色(完整详细+数据库)

    数据库创建“用户表”“角色表”“用户角色关系表” create table roles ( RId int identity, RName varchar(), Remark varchar() ) ...

  5. windows2016_x64搭建ELK(datasource->filebeat->logstash->elasticsearch->kibana)

    windows2016_x64搭建ELK(datasource->filebeat->logstash->elasticsearch->kibana) 本文示例日志程序基于as ...

  6. 安装canvas

    本方法仅适用用于window系统 安装canvas需要当前工作环境拥有python环境,且只能适用python2.7版本,v3.x.x版本会造成系统报错 1.在管理员权限下 使用choco insta ...

  7. Redis入门-02-CentOS7环境搭建

    CentOS7下redis安装过程,安装后需要开启端口号6379 #下载 wget http://download.redis.io/releases/redis-3.2.4.tar.gz #解压 t ...

  8. List和DataTable互转

    /// <summary> /// List和DataTable互转 /// </summary> static class ListUtility { /// <sum ...

  9. JS变量+作用域

    基本类型-栈内存 保存基本类型的变量保存的是值本身 引用类型-堆内存 保存引用类型的变量保存的并不是对象本身,而是一个指向该对象的引用地址 引用类型判断对象是否相等 function equalObj ...

  10. js 时间格式转换

    js时间格式转换 格式化时间转成时间戳 //格式化转时间戳(单位秒) function strtotime(strtime) { strtime = strtime.substring(0, 19); ...