|
|
Common Template Language(CTL)模板指南 |
|
一. 语法规则:
整个模板语言只有一个语法规则:
$指令名{参数表达式}
(注:指令名只能包含字母,下划线,数字,点号)
(注:指令名或参数表达式均可以为空,参数表达式为空时,大括号可省)
(注:如果指令名称与字母相连,大括号不可省,如:$else{}xxxx,而不能为$elsexxxx,否则引起歧义)
二. 特殊符转义:
(1) \ 为转义符,\\ 取消转义(或自转义)
(注:未处于转义位置的斜线不作任何处理,以避免模板中到处使用双斜线)
(2) 使用 \$ 转义指令前导符
如:“\${name}”输出“${name}”
如:“\\${name}”输出“\value” (注:前导符未被转义)
如:“\\\${name}”输出“\${name}”
(3) 使用 \!$ 转义不解释块结束符
如:“$!aaa\!$bbb!$”输出“aaa!$bbb”
如:“$!aaa\\!$”输出“aaa\” (注:结束符未被转义)
如:“$!aaa\\\!$bbb!$”输出“aaa\!$bbb”
(4) 使用 \*$ 转义注释块结束符
如:“$*aaa\*$bbb*$” 注释内容为“aaa*$bbb”
如:“$*aaa\\*$” 注释内容为“aaa\” (注:结束符未被转义)
如:“$*aaa\\\*$bbb*$” 注释内容为“aaa\*$bbb”
三. 编译指令: 配置...
(1) 行注释: 忽略其后同一行的内容 (注: 可注释不合法的指令内容,注释内容不包含最后的换行符)
运行期不保留: (注: 编译后即抛弃)
$# line comment ...
运行期保留: (注: 可通过TemplateVisitor访问到)
$## line comment ...
(2) 多行块注释: 忽略其包含的内容 (注: 可注释不合法的指令内容,自身不可嵌套,但可嵌套行注释和不解释块等)
运行期不保留: (注: 编译后即抛弃)
$*
block comment ...
*$
运行期保留: (注: 可通过TemplateVisitor访问到)
$**
block comment ...
*$
(3) 不解释块: 将其包含的内容作为纯文本输出 (注: 可包含不合法的指令内容,自身不可嵌套,但可嵌套行注释和块注释等)
$!
no parse, eg: $if ...
!$
(4) 通用结束指令:
$end 或者 $end{if}
(注: 参数表示被结束块指令的名称, 编译时将进行检查(不匹配将抛出异常), 没有参数表示自动匹配)
(注: $end总是与最近的块指令匹配,如:$if...$end{if} 或 $if...$else...$end{else})
四. 标准指令: (注:全部采用全小写命名) 扩展...
(1) 输出指令:
表达式结果输出: (注:指令名称为空的指令)
${user.name}
${user.coins + 100}
$out{user.name} 等价于 ${user.name} (注:用于指令名不能为空的语法外套中)
表达式结果输出块指令:忽略指令内部块内容 (注:用于语法外套)
$output{user.name} James $end
<!--$output{user.name}--> James <!--$end-->
<span ct:output="user.name"> James </span>
国际化信息输出:
$msg{"home.title"}
$msg{"home.title", arg0, arg1}
国际化信息输出块指令:将内部块内容作为默认值 (注:用于语法外套)
$message{"key", arg0, arg1} default value $end
$message{"home.title"} welcome $end
<!--$message{"home.title"}--> welcome <!--$end-->
<span ct:message="'home.title'"> welcome </span>
(2) 条件指令:
如果参数条件为真则执行其内部块:
$if{user.name == "james"}
...
$elseif{user.name == "kent"}
...
$else
...
$end
其中,if和elseif指令均可以直接判断null和empty对象,如:
$if{user} 等价于 $if{user != null}
$if{list} 等价于 $if{list != null && list.size > 0}
$if{string} 等价于 $if{string != null && string.length > 0}
(3) 迭代指令: 扩展...
定义循环变量:
$cycle{color = ("red", "blue", "green")}
$cycle{global -> color = ("red", "blue", "green")}
迭代集合或数组:
$for{user : users}
从循环显示项中取值 (注:可用于交替颜色的表格行)
${color.next} (注:取值并向后滚动,cycle变量每次next取值时向后滚动,到最后一个值时将循环到第一个值)
${color.value} ${color.index} (注:取值但不滚动)
${color.values} (注:取原始定义数据集合)
中断循环
$break{for.count > 5} (注:条件判定与指令合并,以避免冗长的语句:$if {for.count > 5} $break $end)
继续循环
$continue{user.name == null}
状态
(注:for变量(非关键字)持有循环过程的状态,多级嵌套循环时,可以用super.for.index(或super.super.for.index)获取上级循环的状态)
(注:index从0开始,count从1开始,也就是count = index + 1)
${for.index} ${for.count} ${for.size} ${for.first} ${for.middle} ${for.last} ${for.odd} ${for.even}
递归级别状态
(注:非递归迭代时level总是为0,递归迭代如:$for{menu -> children : menus})
${for.level}
取值
${user.name}
$else (注:类似$if ... $else ...语法,以避免多重判断语句,当循环集合为空时执行)
have no users...
$end
对于一个List<Map<Object, Object>>的嵌套集合, 迭代方式如:
$for{map: list} (注:先取出List中的Map项)
$for{entry: map} (注:再取出Map中Entry键值对)
${entry.key} 或者 ${entry.value}
$end
$end
特殊迭代举例:
a. 简单次数迭代:$for{10},迭代10次,不产生迭代项数据,但可以用状态信息${for.index}等,当次数小于或等于零时不迭代
b. 递归迭代:$for{menu -> children : menus},递归迭代menu的children属性,直到为空,通常用于输出树结构。
c. 并行迭代:$for{item1 : list1, item2 : list2},$for{i : (1..10), user : users},多个集合并行取next值,以最长的集合作为结束,短集合自动补null值
d. 数字序列迭代:$for{num : (1..9)},$for{num : (-2..5)},$for{num : (5..-2)}
e. 子集合迭代:$for{item : list[0..2]},迭代list中索引号从0到2的元素(包含边界),索引号越界时忽略,也可以用$for{item : list[2..*]}表示直到列表结束
f. 过滤迭代:$for{item : list[=> item != 'xxx']},$for{user : users[u => u.name != 'guest']},过滤掉用户名为"guest"的用户,参见"=>"操作符使用
g. 排序迭代:$for{item : list orderby ("+property1", "-property2")},$for{book : books orderby "+price"} 将books内的元素按price属性值升序排列后再迭代 (参见orderby操作符使用)
h. 非空选择迭代:$for{item : list1 || list2 || list3},从左至右选第一个非空的集合进行迭代
(4) 包含指令:
内嵌其它模板: (注:被内嵌的文件,可以访问当前上下文的变量,不可以传参)
$embed{"common.ctl"}
$embed{"common.ctl", "UTF-8"}
内嵌其它模板的一部分: (注:#后为zone的名称, 参见$zone指令)
$embed{"common.ctl#body"} (注:查找时,如果区域的名称不是常量,则在当前上下文中执行)
(注:如果配置项: localizedLookup=true (缺省为true),假设locale为zh_CN,则:首先查找common_zh_CN.ctl,不存在则查找common_zh.ctl,否则查找common.ctl)
(注:支持"./"当前目录:$embed{"./xxx.ctl"} 在当前目录查找xxx.ctl)
(注:支持"../"上级目录:$embed{"../xxx.ctl"} 在当前目录的上级目录中查找xxx.ctl)
(注:支持"*/"通配目录:$embed{"*/xxx.ctl"} 在当前目录以上的目录中逐级查找xxx.ctl)
包含其它模板的输出: (注:只包含输出,不可以访问当前上下文的变量,可以传参)
$include{"header.ctl"}
$include{"header.ctl", "UTF-8"}
$include{"header.ctl", (param1: "value1", param2: "value2")}
$include{"header.ctl", "UTF-8", (param1: "value1", param2: "value2")}
包含其它模板的一部分: (注:#后为zone的名称, 参见$zone指令)
$include{"header.ctl#title"} (注:查找时,如果区域的名称不是常量,则在当前上下文中执行)
(注:如果配置项: localizedLookup=true (缺省为true),假设locale为zh_CN,则:首先查找header_zh_CN.ctl,不存在则查找header_zh.ctl,否则查找header.ctl)
(注:支持"./"当前目录:$include{"./xxx.ctl"} 在当前目录查找xxx.ctl)
(注:支持"../"上级目录:$include{"../xxx.ctl"} 在当前目录的上级目录中查找xxx.ctl)
(注:支持"*/"通配目录:$include{"*/xxx.ctl"} 在当前目录以上的目录中逐级查找xxx.ctl)
显示文件的内容: (注:不解析其内容)
$display{"article.txt"}
$display{"article.txt", "UTF-8"}
(注:如果配置项: localizedLookup=true (缺省为true),假设locale为zh_CN,则:首先查找article_zh_CN.txt,不存在则查找article_zh.txt,否则查找article.txt)
(注:支持"./"当前目录:$display{"./xxx.ctl"} 在当前目录查找xxx.ctl)
(注:支持"../"上级目录:$display{"../xxx.ctl"} 在当前目录的上级目录中查找xxx.ctl)
(注:支持"*/"通配目录:$display{"*/xxx.ctl"} 在当前目录以上的目录中逐级查找xxx.ctl)
抓取远程文件的内容: (注:非Web环境只支持完整URL的远程页面)
$snatch{"list.jsp"} 相对于当前页面路径目录
$snatch{"../list.jsp"} 相对于当前页面路径的上级目录
$snatch{"/list.jsp"} 相对于Web根目录
$snatch{"/list.jsp", 'UTF-8'} 指定编码
$snatch{"http://www.163.com"} 远程页面
(5) 数据指令: 数据格式说明... 扩展...
(注:内置支持xml,json,properties,yaml等数据格式,数据格式可扩展)
数据块:
$data{xml} (注:参数名称化指令,如果类型需取变量,可使用"\"一元操作符:$data{\type})
...
$end
装载外部数据:
$load{xml: "xxx.xml"}
$load{"xxx.xml"} (注:根据文件扩展名识别类型)
$load{xml: "xxx.ctl", "utf-8"} (注:指定加载文件编码)
$load{"xxx.ctl", "utf-8"}
(注:如果配置项: localizedLookup=true (缺省为true),假设locale为zh_CN,则:首先查找xxx_zh_CN.txt,不存在则查找xxx_zh.txt,否则查找xxx.ctl)
(注:支持"./"当前目录:$load{"./xxx.ctl"} 在当前目录查找xxx.ctl)
(注:支持"../"上级目录:$load{"../xxx.ctl"} 在当前目录的上级目录中查找xxx.ctl)
(注:支持"*/"通配目录:$load{"*/xxx.ctl"} 在当前目录以上的目录中逐级查找xxx.ctl)
(6) 变量指令:
变量定义:
局部变量定义
$var{name = "james"} (注:通常用于隐藏上级同名变量)
在指定上下文定义变量 变量上下文说明...
$var{session -> name = "james"}
在最近的上级if指令上下文定义变量
$var{if -> name = "james"}
在上一级上下文定义变量
$var{super -> name = "james"}
在根级上下文定义变量
$var{root -> name = "james"}
在全局上下文定义变量(整个引擎内共享)
$var{global -> name = "james"}
常见上下文简化指令:
$super{name = "james"} 等价于 $var{super -> name = "james"}
$root{name = "james"} 等价于 $var{root -> name = "james"}
$global{name = "james"} 等价于 $var{global -> name = "james"}
给最近区域的变量赋值: (注:若直到根区域均未找到相应变量,则在当前区域创建局部变量)
$set{name = "james"} (注:不能修改即有数据模型状态,也就是不能使用像:$set{user.name = "james"}的层级设值方式,以遵守模板无副作用契约,避免引入业务逻辑)
如果变量为空或未定义,则给其赋初始值:
$init{name = "guest"}
(7) 块变量指令:
将模板块定义为变量: (注: 如果需要将模板块传递到宏或其它模板中, 可以使用块变量)
$block{myblock} (注:参数名称化指令,如果名称需取变量,可使用"\"一元操作符:$block{\name})
...
$end
$block{global -> myblock} (注: 将块变量定义到指定区域)
...
$end
显示块变量: (注:执行块变量所指模板块,模板块可以通过变量传递到其它模板中再show)
$show{myblock}
(8) 宏指令:
宏定义:
$macro{button} (注:参数名称化指令,如果名称需取变量,可使用"\"一元操作符:$macro{\name})
设置参数缺省值
$init{name: "submit"}
...
回调调用者的内部块,如果为行指令方式调用,则inner为空
$inner{param3: "value3"}
...
表达式结果为真时,中断宏调用
$return{name == null}
...
$end
宏的行指令方式调用: (注:宏调用既可以传参,也可以访问当前上下文的变量)
$button{param1: "value1", param2: "value2"}
宏的块指令方式调用:
$button.block{param1: "value1", param2: "value2"} (注:以".block"结尾表示块指令调用)
...
$end
导入模板文件中所有的宏: (注:在新的上下文执行导入的模板, 并忽略输出, 从执行后的上下文取出所定义的宏, 包括层级$import和$embed的宏)
$import{"mymacro.ctl"}
$import{my : "mymacro.ctl"} (注:调用时需带上"名称空间.", 如:$my.button{xxx})
(注:如果以a为名称空间导入a.ctl,而a.ctl中又以b为名称空间导入b.ctl,且b.ctl中有一个button宏,则可以使用$a.b.button进行调用)
使用模板文件作为宏:
$using{button : "button.ctl"}
$using{"button.ctl"} (注:将使用文件名作为宏的名称)
使用模板文件中的宏作为宏: (注:查找时,如果宏的名称不是常量,则在当前上下文中执行)
$using{button : "mymacro.ctl#button"} (注:#后为macro的名称, 参见$macro指令)
$using{"mymacro.ctl#button"} (注:将使用原始宏的名称(#号后的名称)作为宏的名称)
(9) 继承指令: (注:通常用于布局layout) 示例>>
模板区域定义: (注:在父模板中)
$zone{body} (注:参数名称化指令,如果名称需取变量,可使用"\"一元操作符:$zone{\name})
...
$end
继承模板: (注:在子模板中)
$extends{"super.ctl"}
覆盖父模板区域:
$zone{"body"}
调用被覆盖的父模板区域:
$superzone
...
$end
$end
(10) 动态指令:
动态执行模板:
$exec{templateString}
动态表达式求值:
$eval{expressionString}
(11) 过滤指令:
动态内容输出过滤指令:
$filter{x => "<b>" + x.escapeHtml + "</b>"} (注:缺省变量名为value,如:$filter{=> value.escapeHtml})
...
$end
全部内容输出过滤指令: (注:包括静态文本块)
$filterall{x => x.escapeHtml} (注:缺省变量名为value,如:$filterall{=> value.escapeHtml})
...
$end
输出缓冲指令: (注:将内部块输出缓冲为单一字符串输出)
$buffer
...
$end
缓冲并过滤 (注:过滤方式同$filter指令,将内部块输出缓冲为单一字符串后再过滤输出)
$buffer{x => x.escapeHtml} (注:缺省变量名为value,如:$buffer{=> value.escapeHtml})
...
$end
捕获输出指令: (注:捕获指令内部块输出内容到指定变量)
$capture{xxx}
...
$end
$capture{global -> xxx} (注: 指定变量区间)
...
$end
忽略输出指令: (注:执行内部指令,但忽略输出)
$ignore
...
$end
压缩空白符: (注: 将多个连续的空白符压成一个空格)
$compress
...
$end
删除空白符: (注: 将包含的空白符全部删除)
$strip
...
$end
截短空白符: (注: 将两端的空白符截掉)
$trim
...
$end
$ltrim (注: 只将左端的空白符截掉)
...
$end
$rtrim (注: 只将右端的空白符截掉)
...
$end
转义特殊符: (注: 内置支持xml,xhtml,html,js,url,base64,newline等格式,可扩展) 扩展...
$escape{html} (注: 单个过滤器名时,引号可省,如果滤器名需取变量,可使用$escape{\name})
...
$end
$escape{"html", "js"}
...
$end
代码HTML着色: (注: 内置支持java,xml,html,properties,ini等格式,可扩展) 扩展...
$code{java}
...
$end
关键字HTML加亮:
$keyword{"word1", "word2"}
...
$end
(12) 模板控制指令:
模板上下文设置:
$setting{debug:ture,locale:"zh_CN"}
可设置项:
debug:ture
locale:"zh_CN"
timeZone:"GMT+8"
dateFormat:"yyyy-MM-DD HH:mm:ss"
numberFormat:"###,##0.00"
booleanValue:"true|false"
nullValue:""
停止模板解析:
$stop
$stop{loginUser == null} (注: 表达式结果为真时停止页面解析)
(13) 调试指令:
断言指令:
$assert{user != null} (注: 如果表达式结果不为真, 则抛出异常)
$assert{(user != null), "error messages"}
异常捕获指令:
$try
$exec{"a$$ff$$"}
$catch{e: "org.commontemplate.core.RenderingException"}
${e}
$end
或者:
$try
$exec{"a$$ff$$"}
$catch{"org.commontemplate.core.RenderingException"} (注: 缺省变量名为exception)
${exception}
$end
或者:
$try
$exec{"a$$ff$$"}
$catch (注: 没有参数表示捕获所有异常)
${exception}
$end
调试日志:
$log{"debug messages..."}
$log{debug: "debug messages..."}
$log{info: "info messages..."}
$log{warn: "warn messages..."}
$log{error: "error messages..."}
性能监测:(记录其内部块的运行时间,并将时间存入变量中)
$time{xxx}
...
$end
$time{global -> xxx} (注: 指定变量区间)
...
$end
单步调试断点:(此指令将在其所在行设置断点) 调试器说明...
$. (注: 指令名称为点号的指令)
$breakpoint 等价于 $. (注: 用于指令名不能为特殊符的语法外套中)
五. 举例: 语法外套说明...
(1) HTML页面生成: (注:WEB环境下表达式支持:"<"等价于"<",">"等价于">",包括:"->"等价于"->")
<html>
<body>
$if{users != null && users.size > 0}
<table border="1">
$for{user : users}
<tr>
<td>${for.index + 1}</td>
<td>${user.name}</td>
<td>${user.coins}</td>
</tr>
$end
</table>
$end
</body>
</html>
(2) Java代码生成:
package com.${project.company}.${project.name}.domain;
$if{project.framework != project.name}
import com.${project.company}.${project.framework}.domain.BaseEntity;
$end
public class ${entity.name} extends BaseEntity {
$for{field : entity.fields}
private ${field.type} ${field.name};
public ${field.type} get${field.name.capitalize}() {
return ${field.name};
}
public void set${field.name.capitalize}(${field.type} ${field.name}) {
this.${field.name} = ${field.name};
}
$end
}
|
六. 遗留: (注: 将在1.0版本统一删除)
(1) $local
起止版本:0.8.3加入,0.8.5废弃
废弃原因:与$var功能重复,并且不能像$global一样达到简化作用
替代方案:$var
(2) $forelse
起止版本:0.7.6加入,0.8.6废弃
废弃原因:$for和$if统一使$else指令作为否则逻辑
替代方案:$else
(3) $overzone
起止版本:0.7.6加入,0.8.6废弃
废弃原因:区域定义与覆写均采用$zone指令,保持统一及语义完整
替代方案:$zone
(4) $filterAll
起止版本:0.7.8加入,0.8.6废弃
废弃原因:改为全小写$filterall,保持命名统一
替代方案:$filterall
(5) $leftTrim, $rightTrim
起止版本:0.8.5加入,0.8.6废弃
废弃原因:多单词指令名,参照其它模板语法进行简化
替代方案:$ltrim, $rtrim
(6) 简化语法规则:$指令名:参数名 等价于 $指令名{"参数名"}
起止版本:0.8.5加入,0.8.6废弃
废弃原因:语法规则不统一,比$指令名{参数名}方式并不简化多少,而且存在与字母内容无法分隔的问题
替代方案:名称定义性指令(如:$block, $macro, $zone等)参数名引号可省,如:$指令名{参数名} 等价于 $指令名{"参数名"}
七. 不兼容: (注: 升级版本请注意)
(1) 0.8.5版本使用$using代替原有$import指令,$import指令重新实现
(2) 0.8.5版本宏指令块调用默认后缀由"_block"改为".block",可配置.
(3) 0.8.7版本使用$output代替原有$out指令,$out指令改为与${}空名称指令等价
|
|
|