个人博客 地址: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. Windows10访问Ubuntu子系统(WSL)的桌面环境

    原文地址:https://blog.csdn.net/xmh19936688/article/details/90212960 Windows10访问Ubuntu子系统(WSL)的桌面环境文章目录Wi ...

  2. ArcGIS Runtime SDK for Android 加载shp数据,中文乱码问题

    针对ArcGIS10.2版本的解决办法(默认中文编码为OEM): 现有一个图层名称为“图层.shp”,以此为例: 1.拷贝一个cpg文件,修改名称为“图层.cpg”,并用文本打开cpg文件修改编码为“ ...

  3. Docker Compose 启动mysql,redis,rabbitmq

    这里使用的centos7,首先切换到root. sudo -s 首先去设置下载镜像,否则下载这三个东西要很久,而且可能失败. vim /etc/docker/daemon.json 内容如下: { & ...

  4. Selenium实战(五)——HTML测试报告

    一.概览下载与安装 HTMLTestRunner是unittest的一个扩展,可以生成易于使用的HTML测试报告.HTMLTestRunner是在BSD许可证下发布的. 下载地址:http://tun ...

  5. D. Domino for Young

    基本思想是利用涂色的方法,用黑白两种颜色把方格全部涂色,相邻方格不同色. 方法1:基于二分图匹配的思想 一开始也想过二分图匹配,但数据量太大,就放弃了这种想法.其实根据增广路的定义.如果白色的方格的数 ...

  6. 一个margin就可以让块状元素响应居中,很实用

    之前总结过水平居中的很多方法,但今天在<css世界>这本书里看到margin的一个特性,一行代码就搞定很实用,分享一下 margin: auto能在块级元素设定宽高之后自动填充剩余宽高.m ...

  7. WebGL_0004:带贴图的材质透明效果

    在材质中: "blendType": 2,

  8. Python函数进阶:闭包、装饰器、生成器、协程

    返回目录 本篇索引 (1)闭包 (2)装饰器 (3)生成器 (4)协程 (1)闭包 闭包(closure)是很多现代编程语言都有的特点,像C++.Java.JavaScript等都实现或部分实现了闭包 ...

  9. 移动端display:flex

    移动端display:flex布局时候,子元素有背景颜色时候,背景颜色不能铺满,有缝隙, // less .t-flex { background: blue; display: flex; > ...

  10. 2020牛客寒假算法基础集训营4-I 匹配星星【贪心】

    链接:https://ac.nowcoder.com/acm/contest/3005/I来源:牛客网 示例1 输入 复制 2 1 1 0 2 2 1 2 1 1 0 2 2 1 输出 复制 1 1 ...