官网
引用:https://www.dazhuanlan.com/meaninglessbuzz/topics/1450555
ST (StringTemplate) 是一个 java 模板引擎,用于生成源代码、网页、电子邮件或任何其他格式化的文本输出
。 相比较与常见的模板,如FreeMarker,Velocity。ST 更加强大,也更加复杂。
ANTLR (ANother Tool for Language Recognition)(另一种语言识别工具)是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件。
引入ST依赖
<dependency>
<groupId>org.antlr</groupId>
<artifactId>ST4</artifactId>
<version>4.3</version>
<scope>compile</scope>
</dependency>
本质上,ST 的语法很简单,分为两个部分:文本和属性表达式 (attribute expressions)。
@Test
public void test01(){
ST hello = new ST("Hello <name>");
hello.add("name","World");
System.out.println(hello.render()); // Hello World
}
<name>
就被替换为了World
,所以合起来的输出是Hello World
。
分隔符
默认属性表达式使用尖括号
包围,当然这个是可以自定义的,比如如果你要生成 HTML 代码,用尖括号就非常麻烦了,可以改用%。比如上面的例子中的模板:
@Test
public void test01_delimiter(){
ST hello = new ST("Hello %name%",'%','%');
hello.add("name","World");
System.out.println(hello.render()); // Hello World
}
代码生成是具有复杂逻辑的,一般不会在一个模板中搞定,而是分解为多个小的模板,然后拼装起来
。
ST 的模板更加强大,可以有输入参数,写起来和编程语言很类似。一个模板组里可以定义多个模板,只有在同一个模板组里的模板才可以互相调用。
@Test
public void test_group1(){
STGroup stGroup = new STGroup();
//defineTemplate 需要三个参数,分别是模板名称,模板参数,模板内容
stGroup.defineTemplate("template1","input1","【 <input1> 】");
//调用 template1 内容
stGroup.defineTemplate("template2","input2","Hello <template1(input2)>");
ST st = stGroup.getInstanceOf("template2");
st.add("input2","World");
System.out.println(st.render()); // Hello 【 World 】
}
文件
在resources目录下,创建模板文件:/tmp/test.stg
,内容如下
/**
* <type> : 使用入参
* <init(value)>: 调用方法
*/
decl(type, name, value) ::= "<type> <name><init(value)>;"
init(v) ::= "<if(v)> = <v><endif>"
执行
@Test
public void test03_template_file(){
STGroup group = new STGroupFile("tmp\\test.stg");
ST st = group.getInstanceOf("decl");
st.add("type", "int");
st.add("name", "x");
st.add("value", 0);
System.out.println(st.render()); // int x = 0;
}
@Test
public void test04_template_String(){
String s = "say(name) ::= \"hello, <name>.\"";
STGroup group = new STGroupString(s);
ST st = group.getInstanceOf("say");
st.add("name", "zhangsan");
System.out.println(st.render()); //hello, zhangsan.
}
如果多次 add 同一个参数的值,是不会覆盖的,而是追加,也就是每个参数其实是一个数组
:
@Test
public void test04_template_multi_String(){
String s = "say(name) ::= \"hello, <name>.\"";
STGroup group = new STGroupString(s);
ST st = group.getInstanceOf("say");
st.add("name", "zhangsan");
st.add("name", "lisi");
System.out.println(st.render()); //hello, zhangsanlisi.
}
控制多个值的输出格式
@Test
public void test04_template_array1_String(){
// say(name) ::= "hello, <name;separator=",">" //设置分隔符
String s = "say(name) ::= \"hello, <name;separator=\\\",\\\">.\"";
STGroup group = new STGroupString(s);
ST st = group.getInstanceOf("say");
st.add("name", "zhangsan");
st.add("name", "lisi");
System.out.println(st.render()); //hello, zhangsan,lisi.
}
模板内容
say(name) ::= "hello, <name:bracket();separator=",">"
bracket(x) ::= "(<x>)"
执行程序
st.add("name", "mushan");
st.add("name","willing");
// 输出: hello, (mushan),(willing)
模板
say(name) ::= "hello, <name:{x|[<x>]};separator=",">"
执行程序
st.add("name", "mushan");
st.add("name","willing");
// 输出: hello, [mushan],[willing]
定义对象
public static class User {
public int id; // template can directly access via u.id
private String name; // template can't access this -- 私有属性无法访问
public User(int id, String name) { this.id = id; this.name = name; }
public boolean isManager() { return true; } // u.manager
public boolean hasParkingSpot() { return true; } // u.parkingSpot
public String getName() { return name; } // u.name
public String toString() { return id+":"+name; } // u
}
属性的范围是比较简单的,仅可以访问
public
属性
方法的访问有一套映射关系,比如对于o.p
,st 会在类上尝试访问getP(), isP(), hasP()
,如果都不存在,则会报错。
执行程序
ST st = new ST("<b>$u.id$</b>: $u.name$", '$', '$');
st.add("u", new User(999, "parrt"));
String result = st.render(); // "<b>999</b>: parrt"
可以看到在 ST 模板中使用 Java 类是非常简单的。但是和 js 中直接使用 json 相比,Java 中定义一个类,只是为了传递数据就定义一个类是非常繁琐的,
还好,ST 为我们提供了一种简便的方法来传递对象性质的信息:
//模板
//introduction(person) ::= "I'm <person.name>, I'm <person.age> years old."
st = stGroupFile.getInstanceOf("introduction");
st.addAggr("person.{name,age}","mushan",18); // addAggr
System.out.println(st.render());
// 输出: I'm mushan, I'm 18 years old.
ST 模板的语法很丰富,可以实现复杂逻辑
,以处理复杂的代码生成需求。上面我提到的语法是最常用的语法,全面的模板语法可以参考stringtemplate4/cheatsheet.md