天天看点

TinyTemplate(Velocity Plus版)即将火热推出~~~

本来是没有自己写一个模板引擎的计划的,因为按我的理解,一直认识这种“语言”级的引擎,难度是非常大的。总感觉自己的水平不够,因此不敢有这个念头。直到大量使用velocty的时候,碰到velocty诸多尽如人意的地方,但是又无能为力,退回到jsp吧,又心不有甘。于是就期望着寻找一种语法结构接近velocty,但是又没有velocity这些不方便之处的模板语言。于是进到一个模板语言群,一群大佬们个个至少是一个模板语言的作者,于是作者在里面表达了自己的期望,大佬们都介绍了自己的模板引擎,于是作者一个接一个的看源码,看文档。说实际,看文档,感觉都非常不错,都有自己的特色,看语法也都不错,除了一部分自己特别关注的点没有之外,其部分都非常不错了。但是距离自己的诉求还是有差距,怎么办呢?于是就准备找一个最接近的模板引擎来进行一定的扩展,挑来挑去就挑中了jetbrick这个模板语言(在此对jetbrick致以强烈的衷心的感谢!!)。

之所以挑这个呢,是因为以下几个原因:

antlr语言文件编写非常清晰,对于我这种antlr盲来说,也可以看得懂,甚至可以照葫芦画瓢修改修改,这个非常重要,在后期作者进行了相当的语法改进,这个方面有极度体现

代码质量较好,使用sonar进行进行分析,给的结果都还是相当不错的,在作者看过的所有的模板语言中,算上成之选

语法结构与velocity的非常接近,这点对我也非常重要,因为我的想法就是velocity语法有相当的接受度,与velocity语法接近,velocity的一些使用者可以方便的进行切换

测试用例比较完善,当时也就是这么一看,但是最后tiny模板引擎完成之后,利用其测试用例进行测试发现了好几个bug,说明还是非常有效果的

于是下载了源码,初端详还是不错的,但是在实现在几点和笔记诉求有差距:

第一我希望是弱类型,jetbrick是强类型,性能是有提高但是开发过程会比较不方便。

另外由于jetbrick作者期望在编译器进行强类型推测,因此导致运行期的内容与编译期的内容有比较强的耦合。

另外有一些语言特性,属于个人爱好上的原因,也有一些差异,因此就决定自己编写一个。

为了更好的说明tiny的语法规范,因此全程对比velocity

${表达式}

表达式是指最后的运算结果是一个值,表达式中可以使用变量

=>与velocity区别,这里大括号必须有,不能省略,“-”号不允许出现

${a+b-c+d}

${"abc"+1}

${user.name}

${user.items[1].count+3}

${user.func(1,2,3,4)+map.def+map["aa"]}

<a->[_a-za-z0-9]

=>与velocity区别,“-”号不允许出现

示例:

合法

abc

ab_c

ab9_

非法

9bc

_bc

a-b

属性语法与变量名一样

区别大括号必须有,“-”号不可以有

属性值实际上也是个表达式,因此这么写也是可以的

${a.("aa"+bb)},如果bb的值为3,则等同于${a.aa3},等同于a."aa3"

format:

#set ( ref = [ ", ' ]arg[ ", ' ] )

示例

#{set}不被支持

变量名前面不可以加$

#set(aa=1)

#set(aa=2,b=2,c=aa+2,d=func("info"))

#set(aa=[aa,"bb",2,3,4]

#set(aa={"aa":1,bb:2,aa.bb.func()+3:5}

格式:

#if( [condition] ) [output] [ #elseif( [condition] ) [output] ]* [ # [ { ] else [ } ] [output] ] # [ { ] end [ } ]

用法:

#if(aa)

#end

#if(aa||bb)

这里即可以是逻辑表达式,也可以是非逻辑表达式

情况如下:

如果是null,则false

如果是boolean值,看true/false

如果是string,看长度>0

如果是collection,看size>0

如果是iterator看hasnext

如果是数组,看长度>0

如果是enumerator,看hasmoreelements

如果是map看size

其它情况,就返回true

下面所有的逻辑表达式都支持

operator name   

symbol    

alternative symbol    

example

equals number   

==    

eq    

#if( $foo == 42 )

equals string   

#if( $foo == "bar" )

object equivalence   

#if( $foo == $bar )

not equals   

!=    

ne    

#if( $foo != $bar )

greater than   

>    

gt    

#if( $foo > 42 )

less than   

<    

lt    

#if( $foo < 42 )

greater than or equal to   

>=    

ge    

#if( $foo >= 42 )

less than or equal to   

<=    

le    

#if( $foo <= 42 )

boolean not   

!    

not    

#if( !$foo )

notes:

==用的是equals

可以用下面的方式来避免与显示内容分不开

注意:与velocity区别:变量名前面不要加“$”符号

# for(varname : collection) 显示内容1 [#[{]else[}] ]  显示内容2 # [ { ] end [ } ]

表示对集合进行循环,执行显示内容1,如果集合为空,则执行显示内容2

注意:与velocity区别:增了了#else指令

可以是collection的内容:

数组、集合、iterator,enumeration,map,对象,甚至null

如果不是集合对象,只是一个普通对象,就只循环次,这比较适合于有时候会返回列表,有时候会返回一个对象的情况,就避免了增加复杂的判断。

#include(expression[,{aa:1,bb:2}])

#include("/aa/bb/cc.vm")

与velocity的区别:用于把另外的页面在当前位置进行渲染,后面只能加一个map用来传递参数

#stop[(expresson)]

usage:

#if(aa==bb)

#stop

等价于

#stop(aa==bb)

#break[(expresson)]

#break

#break(aa==bb)

#continue[(expresson)]

#continue

#continue(aa==bb)

增加指令#[@]call

#call("aa"+"bb",1,2)

等同于

#aabb(1,2)

#call(format("sys%smdl%s",1,2),1,2)

#sys1mdl2(1,2)

#@call("aa"+"bb",1,2)

...

#@aabb(1,2)

#@call(format("sys%smdl%s",1,2),1,2)

#@sys1mdl2(1,2

另外支持命名传递,详见宏调用

# macro macroname( arg1 [, arg2,arg3 ... argn ] ) [ vm vtl code... ] # [ { ] end [ } ]

与 velocity不同:macroname由括号里放在了括号外面,避免与参数混一起,参数之间必须用逗号隔开

....

与velocity不同:支持命名调用:

比如下面的方式; #vmname(arg2=3),也可以混用:#vmname(1,2,arg5=3,arg4=4)

注释:

##单行注释

#-- ... --#  #* ... *#,两种格式支持,是考虑到在<!-- -->的时候,改成#-- --#更方便

非解析标记,下面的内容会原样输出

#[[

this has invalid syntax that would normally need "poor man's escaping" like:

#define()

${blah

]]#

新增加内容:i18n支持:

$${aaa.bbb.ccc}

表示显示aaa.bbb.ccc对应的国际化内容

当然,还有强大的布局(页面继承),容易的使用(会vm的到现在已经会用),方便的扩展(非常容易扩展),微小的体量(引擎只有2000行代码),还想要什么,可以尽情提出。

新增内容:java对象方法扩展,即在不修改原类的情况下,对java类添加

比如可以为string增加bold函数,通过下面的方式来进行加粗

${“悠然是个好同志”.bold()}

也可以给 object增加tojson,toxml等方法,从而直接用下面的方式输出json或xml:

${user.tojson()},${user.toxml()}

当然,现在还有一点计划中的特性没有实现,那就是装饰方式的布局方式,可能有些同学不了解,那就先留点悬念吧。

下面看看实际演练:

<a href="http://my.oschina.net/tinyframework/blog/278502#">?</a>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

<code>&lt;!</code><code>doctype</code> <code>html&gt;</code>

<code>&lt;</code><code>html</code><code>&gt;</code>

<code>&lt;</code><code>head</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>title</code><code>&gt;stockmodel - httl&lt;/</code><code>title</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>meta</code> <code>http-equiv</code><code>=</code><code>"content-type"</code> <code>content</code><code>=</code><code>"text/html; charset=${outputencoding}"</code><code>/&gt;</code>

<code>    </code><code>&lt;</code><code>style</code> <code>type</code><code>=</code><code>"text/css"</code><code>&gt;</code>

<code>        </code><code>body {</code>

<code>            </code><code>color: #333333;</code>

<code>            </code><code>line-height: 150%;</code>

<code>        </code><code>}</code>

<code>        </code><code>td {</code>

<code>            </code><code>text-align: center;</code>

<code>        </code><code>thead {</code>

<code>            </code><code>font-weight: bold;</code>

<code>            </code><code>background-color: #c8fbaf;</code>

<code>        </code><code>.odd {</code>

<code>            </code><code>background-color: #f3defb;</code>

<code>        </code><code>.even {</code>

<code>            </code><code>background-color: #effff8;</code>

<code>    </code><code>&lt;/</code><code>style</code><code>&gt;</code>

<code>&lt;/</code><code>head</code><code>&gt;</code>

<code>&lt;</code><code>body</code><code>&gt;</code>

<code>&lt;</code><code>h1</code><code>&gt;stockmodel - jetbrick-template&lt;/</code><code>h1</code><code>&gt;</code>

<code>&lt;</code><code>table</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>thead</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>tr</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>th</code><code>&gt;#&lt;/</code><code>th</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>th</code><code>&gt;id&lt;/</code><code>th</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>th</code><code>&gt;code&lt;/</code><code>th</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>th</code><code>&gt;name&lt;/</code><code>th</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>th</code><code>&gt;price&lt;/</code><code>th</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>th</code><code>&gt;range&lt;/</code><code>th</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>th</code><code>&gt;amount&lt;/</code><code>th</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>th</code><code>&gt;gravity&lt;/</code><code>th</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>tr</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>thead</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>tbody</code><code>&gt;</code>

<code>    </code><code>#for(item : items)</code>

<code>    </code><code>&lt;</code><code>tr</code> <code>class</code><code>=</code><code>"${itemfor.index % 2 == 1 ? "</code><code>even" : "odd"}"&gt;</code>

<code>        </code><code>&lt;</code><code>td</code><code>&gt;${itemfor.index}&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code><code>&gt;${item.id}&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code><code>&gt;${item.code}&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code> <code>style</code><code>=</code><code>"text-align: left;"</code><code>&gt;${item.name}&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code><code>&gt;${item.price}&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code> <code>style="color: ${item.range&gt;=10?'red':'blue'};"&gt;${item.range}%&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code><code>&gt;${item.amount}&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code> <code>style="color: ${item.gravity&gt;=20?'red':'blue'};"&gt;${item.gravity}%&lt;/</code><code>td</code><code>&gt;</code>

<code>    </code><code>#end</code>

<code>    </code><code>&lt;/</code><code>tbody</code><code>&gt;</code>

<code>&lt;/</code><code>table</code><code>&gt;</code>

<code>&lt;/</code><code>body</code><code>&gt;</code>

<code>&lt;/</code><code>html</code><code>&gt;</code>

下面是渲染结果:

63

64

65

66

67

68

69

70

71

<code>    </code><code>&lt;</code><code>meta</code> <code>http-equiv</code><code>=</code><code>"content-type"</code> <code>content</code><code>=</code><code>"text/html; charset=gbk"</code><code>/&gt;</code>

<code>    </code> 

<code>    </code><code>&lt;</code><code>tr</code> <code>class</code><code>=</code><code>"even"</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code><code>&gt;1&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code><code>&gt;600663&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code> <code>style</code><code>=</code><code>"text-align: left;"</code><code>&gt;company 01&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code><code>&gt;20.55&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code> <code>style</code><code>=</code><code>"color: red;"</code><code>&gt;10.01%&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code><code>&gt;2.13 hm&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code> <code>style</code><code>=</code><code>"color: red;"</code><code>&gt;24.29%&lt;/</code><code>td</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>tr</code> <code>class</code><code>=</code><code>"odd"</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code><code>&gt;2&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code><code>&gt;600822&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code> <code>style</code><code>=</code><code>"text-align: left;"</code><code>&gt;company 02&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code><code>&gt;14.69&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code> <code>style</code><code>=</code><code>"color: red;"</code><code>&gt;10.04%&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code><code>&gt;1.56 hm&lt;/</code><code>td</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>td</code> <code>style</code><code>=</code><code>"color: red;"</code><code>&gt;36.79%&lt;/</code><code>td</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>tr</code><code>&gt;    </code>

补充一下,上面本来是有20行数据的,为了节省空间,给删除了18行。

下面是程序代码:

<code>    </code><code>public</code> <code>static</code> <code>void</code> <code>main(string[] args) </code><code>throws</code> <code>templateexception {</code>

<code>        </code><code>templateengine engine = </code><code>new</code> <code>templateenginedefault();</code>

<code>        </code><code>templatecontext context=</code><code>new</code> <code>templatecontextdefault();</code>

<code>        </code><code>context.put(</code><code>"outputencoding"</code><code>,</code><code>"gbk"</code><code>);</code>

<code>        </code><code>context.put(</code><code>"items"</code><code>, stockmodel.dummyitems());</code>

<code>        </code><code>engine.addtemplateloader(</code><code>new</code> <code>fileobjecttemplateloader(</code><code>"jetsample"</code><code>, </code><code>"d:\\git\\ebm\\src\\main\\resources\\templates"</code><code>));</code>

<code>        </code><code>engine.rendertemplate(</code><code>"/tiny.html"</code><code>,context,</code><code>new</code> <code>outputstreamwriter(system.out));</code>

<code>    </code><code>}</code>

当然了,可能大家对性能非常感兴趣。

TinyTemplate(Velocity Plus版)即将火热推出~~~

tiny框架的性能比beetl-1.26稍微落后一点点,但是明显比velocity和freemarker是快多了。

与前面几个引擎比较,性能差异主要在:

1.强类型与弱类型方面的差异,tinytemplate采用的是弱类型,而一些模板引擎采用的是强类型。

2.其它引擎都已经经过了长时间的优化,而 tinytemplate只是刚二经过不到一周的初始期。通过后面的一些优化,他将有一定的提升(但是达不到jetbrick这个程度)

这天这个只是开胃菜,亲们耐心等待我的正式发布吧。

附录:

tinytemplate历史三天编写,代码行数量截止发稿为在3410,预计完稿后,在3700行左右。

jetbrick为1.3万+

beetl为1.7万+

对于velocity用户来说,迁移非常容易。

本来希望velocity升级2.0的但是实在等不到,因此只好自己动手升级了。

凡是有好的意见、建议、需求的,全部采纳并快速实现,在此提前致以感谢!