Java 模板引擎:webit-script
jopen
11年前
Java 模板引擎,基于java 5 开发,不依赖其他第三方库,弱类型,语法类似于Javascript。
How to use(如何使用)
Maven
<repositories> <repository> <id>webit-script</id> <name>Webit script</name> <url>http://zqq90.github.io/maven/</url> </repository> </repositories> <dependencies> ... <dependency> <groupId>com.github.zqq90.webit-script</groupId> <artifactId>webit-script</artifactId> <version>1.2.1</version> </dependency> ... </dependencies>
Or Add jars
webit-script-1.2.1.jar
Code in Java like this
// !! engine 并不会被缓存, 请根据需要自行实现 Engine的单例模式 Engine engine = Engine.createEngine("/webit-script-config.props", extraSettingsMap); ... // template 已缓存, 线程安全, 并自动检测模板源是否被更新 // 当然您也可以缓存 Template 实例,模板更新时更新实例内部AST, 其实例不会变化 Template template = engine.getTemplate("/your/template/path/filename.ext"); ... template.merge(parametersMap, outputStream); //template.merge(parametersMap, writer);
Config(配置)
- 配置文件格式: Use Jodd-props, see:Jodd Props doc
Tips: Java-Properties also works
- 多文件支持 "/webit-script-config1.props,/webit-script-config2.props"
- 可选额外参数: extraSettingsMap 类型为Map, 支持props 宏
- 默认配置: webit-script-default.props
Grammar(语法)
Hello word
Hello Webit Script! <% var books; { for(book : books){ %> ${for.iter.index}.《${book.name}》 ¥${book.price} <% } } { //this is a function var func = function(a, b){ return a + b + arguments[3]; }; echo func("a", "b", "c"); echo '\n'; } { var map = { 1: 1, "key2": "value2", 3: 2 + 1 }; map[5] = 2 + 3; for(key, value : map){ echo key + ":" +value + "\n"; } } %>
更多实例可见:测试模板
结构
- 脚本必须都放在
<% %>
内.<% .code inside.. %> plain text outside
- 替换占位符
${}
只能允许带返回值的单个表达式,只能在脚本块<% %>
以外使用.${ .. expression .. }
- 转义字符
\\
to\
,\<%
to<%
,\${
to${
- Node: 只有紧挨
<%
${
的\
才需要转义 , - 转义窍门: 偶数个
\
会 打印 一半数量的\
并进入代码段, 奇数 会 打印 (count-1)/2 个\
然后打印被转移的符号。
注释
- 单行注释
//
- 块注释
/* */
关键字
var super this if else switch case default for do while break continue function return import include echo native new @import
保留的关键字
static instanceof class const final throw try catch finally
操作符
与Java 保持一致,顺序按优先级从高到低
[] . () @ => ! ~ ++ -- – (取负) * / % + - << >> >>> < <= > >= ^ | && || ?: .. = += -= *= /= %= ^= <<= >>= >>>=
语句
- 结尾分号不能省略
作用域(代码段) { }
- 作用域引用上层变量
- 本层作用域变量不会影响上层
- 同一作用域不能重复声明变量
- 模板传入的变量仅在该作用域查找同名变量并赋值 1. 调用模板传入的变量; 2.import 返回的变量
变量
变量声明 var
var a; var a, b, c=0, d="d";
变量名规则
- 先声明 后使用,所有变量 必须全部声明
- 可开启 弱声明模式,所有变量不需要 事先声明,解析时自动声明
- 对大小写敏感
- 不能使用关键字
- 仅能包含 0-9a-zA-Z_$
- 特殊变量名: ++
super.
用于 取得指定上层且仅该层作用域的变量, 可嵌套super.super.a
++this.
用于 取得本层且仅本层作用域的变量, 可嵌套this.super.a
++for.iter
用于 最近一层for循环的 迭代状态对象, 可使用super
this
限定作用域super.for.iter
数据结构
拥有动态类
var x // null var x2 = 6; // 数字 var x3 = "Bill"; // 字符串 var x4 = 'a'; // 字符 var x5 = [1, "string"]; // 数组 var x6 = {}; // Map
字符串
- 转义,
\\
\"
\'
\n
\r
\t
\f
\b
- 允许换行,行后转义字符 可屏蔽该换行符
var string = "第一行 \ 这还是在第一行 这是第二行\n第三行\n 这是第五行,第四行是空行";
数字
var x1=34; //Integer var x2=34L; //Long var x3=34.00; //Double var x4=34.00D; //Double var x5=34.00F; //Float var x6 = 0b10101; //二进制 var x7 = 0123; //八进制 var x8 = 0x1A; //十六进制
布尔
var x = true; var y = false;
数组
带初始值的数组
var array = [1, "a string", book]; var item; item = array[0]; item = array[1]; item = array[2]; array[0] = "a new value";
Native 方法声明定长数组,
// 引入生成数组的 native 方法 var new_int_array = native int []; var new_Object_array = native Object []; var new_DateTime_array = native java.util.DateTime []; //得到定长数组 var int_array = new_int_array(5); //长度为5 的int数组 var objects = new_Object_array(5);//长度为5 的Object数组 var a; a = objects[4]; objects[4]=4; // 自动 装箱为Integer 然后放入数组 var len = objects.length; //数组长度 len = objects.size; //数组长度 //不定长数组 可使用Java提供的List实现 var new_list = native new java.util.ArrayList(); var list_add = native java.util.List.add(Object); var list = new_list(); list@list_add(0); list@list_add(1); var a = list[0]; list[0] = "zero"; list[1] = "a string";
Map
var map = {}; var map2 = {1:1,"2","2"}; map["key"] = "a string value"; var value = map[1]; value = map["2"]; value = map["key"];
Java对象
声明
var new_list = native new java.util.ArrayList(); var list = new_list(); var list2 = new_list();
访问属性
var book; var name = book.name; // book.getName(); book.name = "new name"; //book.setName("new name");
访问方法
访问方法必须事先native导入成本地函数
var list_add = native java.util.List.add(Object); list@list_add(0); list_add(list, 1);
访问静态方法
var now = native java.lang.System.currentTimeMillis(); echo now();
函数
声明
- 格式同java
- 可变参数,
- 可通过 arguments 获得所有传入的参数, java类型为 Object[]
- 可访问父层作用域
- 函数内部可嵌套函数
var outSideVar; var a; var myFunc = function(arg1, arg2){ var arg3 = arguments[3]; // 如果没有将抛出异常,最好通过 arguments.size确认 outSideVar = "a new "; //可访问 var a = 0; //内部变量 super.a ++ ; //访问上层变量 var func = function(){ ... }; //内部嵌套函数 }; //不要忘了分号!!!
导入Java内的 方法
- 仅可导入公共类的公共方法, 包括静态方法 和 成员方法
- 可使用
@import
导入类名 或者包名 用法同Java里的import
, 以简化类名输入 @import java.util.*;v1.2.0+ 不再支持导入包
@import java.lang.System; //实际上默认已经导入 java.lang.* 只是演示使用方法 @import java.util.List; @import java.util.ArrayList; var now = native java.lang.System.currentTimeMillis(); var list_add = native List.add(Object); var new_list = native new ArrayList(); // 导入 构造函数 var new_list2 = native new ArrayList(int); // 导入 构造函数
调用
- 可变参数个数, 多余函数同样会被传入, 多余函数是否被使用 取决于函数本身
- 缺少的参数 自动 null 填充, 为了良好的设计 不建议使用缺少函数自动填充
- 可使用@ 将第一个参数 外置
func(arg1, arg2); //等同于 arg1@func(arg2); list_add(list, item); //等同于 list@list_add(item);
重定向输出符 =>
- 作用: 将指定 范围 产生的输出流 重定向到 指定变量
- 意义: 可以延后输出
- 使用对象: 1. 代码段; 2. 函数调用
- 数据格式: 使用OutputStream 时, 为 byte[] ; 使用 Writer 时, 为String.
var out; var book; //代码段 输出重定向 { echo "a String"; >${book.name} < } => out; //不要忘了分号!!! // "a String" 以及 book.name 都将输出到 out var out; // 函数 输出重定向 func() => out; //由于 `=>` 具有较高的优先级,也可以这么些 var a = arg1@func() => out +1; //此时 a为 func()+1 , func() 本次执行的输出内容赋值给out
import & include
- 区别: import 将把调用模板的 上层变量 推入调用层的当前已存在变量
- 共同点: 都会在调用位置产生 输出
- 将使用默认的Loader 加载模板,可使用相对路径或绝对路径
- 可跟随 一个 map 格式的传参
- 模板名可以动态生成
- import 可支持指定需要导出的变量, 否则只导出本层作用域内的同名变量
//相对路径 include "./book-head.wtl"; //等同于 include "book-head.wtl"; //绝对路径 include "/copyright.wtl"; //动态模板名 var style = ""; import "book-list-"+ style +".wtl"; //可传入参数 函数同样也可以作为变量传输 var func = function(){}; var book; import "book-head.wtl" {"book": book, "func":func}; //传入Map 变量作为参数 var map = {"book": book, "func":func}; map["one"] = 1; import "book-head.wtl" {map}; //导出指定变量 var a; var b; //导出 : a 到a ,c 到 b import "book-head.wtl" {"param1":1} a,b=c;
关于条件判断的三种情况
- 如果是boolean(Boolean)值 会原封返回
- 如果 ==null 返回 false
- **如果 是空集合 或者 空数组 (.size==0) 返回 false **
- 否则 返回 true
三元条件运算符 & 其简写
- 操作符按 自右向左 结合 [不是执行顺序], 详解看下面例子
- 简写时
?:
之间不能有空白
var a1 = isTrue ? "Yes" : "No"; //简写 var a2 = value ?: defaultValue; //取默认值 //自右向左 结合 var x = expr1 ? expr3 : expr2 ? expr4 : expr5; //这个等同于 var x = expr1 ? expr3 : (expr2 ? expr4 : expr5); // 如果 是 自左向右 就会变成这样 var x = (expr1 ? expr3 : expr2) ? expr4 : expr5; //简写 就按 从左向右 “执行” var a4 = list1 ?: list2 ?: list3;
判断语句
判断表达式 ?:
判断控制语句 if - else if - else
- 不能省略
{ }
if( ... ){ ...; }else if(...){ ...; }else{ ...; }
循环控制语句
- 支持 数组, java.util.Collection, java.util.Iterator, java.util.Enumeration, CharSequence, java.util.Map, 整型递增/递减
- 当集合为null 或者为空时将不会执行循环
- 支持 else , 可选, 当不符合执行循环体的条件时执行else体.
for-in
//集合 数组 等 for(item : list){ echo item; //echo for.iter.index; // .isFirst .hasNext .isOdd .isEven } else{ echo "list is empty"; } //递增 for(i: 3..6){ echo i; } //递减 for(i: 6..3){ echo i; //支持 for.iter.* }
for-in Map version
for(key, value : map){ echo key + " = " value; echo "\n"; //同样支持 for.iter.* }
while do-while
- 不支持 for.iter 特殊变量
// var iter; ... ; while(iter.hasNext){ var item = iter.next; ....; } // do{ ....; }while( ... );
Switch-Case
- 支持普通 Object, 包括 String
- 使用 Object.equls() 判断是否相等
- 需要 break, 否则无条件继续执行下一个标签的句柄
- 每个case 命名空间独立
switch(a){ case 1: ....; break; case "c": //String ....; break; case 'c': //Char ....; break; default: ....; }
break continue
- 支持 label, 直接操作该循环体 或 switch
//break continue outter: for(i: 6..3){ echo i; //支持 for.iter.* inner: for(item : list){ if( ..... ){ break outter; } .....; break; // break inner; } // switch(a){ ...; case 'x': break outter; ...; } }
正在完善。。。
其他
Performance(性能)
- 缺省开启ASM构建Native 调用减少反射, 不同于将整个模板编译成Java字节码,该方法不会造成无限制的perm溢出;
- 解析之后的Template AST会放入缓存, 检测到模板源文件改变时将重新加载资源并解析;
- 性能测试结果比较理想, 待比较权威的模版测试程序;
- 使用OutputStream 输出时, 选择 SimpleTextStatmentFactory 将会预先将纯文本根据缺省编码编码成字节流.
- boilit/ebm 测试结果 by:boilit/ebm or see
Engine Time Size BSL-2.0.0 559 68118050 webit-script-1.1.4 590 68318250 HTTL-1.0.11 958 68118050 BeeTL-1.25.01 958 68138070 Rythm-1.0.0-b10-SNAPSHOT 1624 48728680 Velocity-1.7 1834 75046912 FreeMarker-2.3.19 2369 68157440 JdkStringBuffer-1.7.0_40 606 67395584 JdkStringBuilder-1.7.0_40 735 67395584
SPI
- TextStatmentFactory 对模板纯文本的存贮以及输出形式进行管理
- Filter 输出过滤
- CoderFactory 编码/解码
- Loader 模板资源加载(原ResourceLoader)
- Logger 日志
- GetResolver, SetResolver, OutResolver Bean属性解释器
- NativeSecurityManager Native调用安全管理器
项目主页:http://www.open-open.com/lib/view/home/1385607413718