Common Template Engine 发布版本 | 每日构建 >>  English | 中文
下载 更新 论坛 关于我们
文档
首页概览
模板指南
表达式指南
语法外套指南
配置指南
扩展指南
API使用指南
常见问题
对比其它模板引擎
集成
MVC框架集成
JSP标签集成
缓存策略集成
日志框架集成
数据格式集成
脚本引擎集成
邮件发送组件集成
第三方集成依赖包
工具
调试器说明
查看器说明
模板生成器说明
模板转换器说明
编辑器插件说明
代码生成器说明
开发
架构设计
开发规范
项目计划
需求场景
资源
下载
许可协议
更新日志
UML图
Java Doc
测试覆盖率报告
社区
开发团队
论坛
知识库
邮件列表
问题列表
报告问题

 
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环境下表达式支持:"&lt;"等价于"<","&gt;"等价于">",包括:"-&gt;"等价于"->")
<html>
    <body>
        $if{users != null && users.size &gt; 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.nameextends BaseEntity {
  $for{field : entity.fields}
  private ${field.type${field.name};

  public ${field.typeget${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指令改为与${}空名称指令等价

 

版权所有 © 2007 - 2009 CommonTemplate 开发小组