Beetl的环境搭建
输入命令
git clone https://git.oschina.net/xiandafu/beetl2.0.git
不一会儿,输出了下面的内容
Cloning into 'beetl2.0'...
remote: Counting objects: 5807, done.
remote: Compressing objects: 100% (2145/2145), done.
remote: Total 5807 (delta 3050), reused 5383 (delta 2733)
Receiving objects: 100% (5807/5807), 14.60 MiB | 684.00 KiB/s, done.
Resolving deltas: 100% (3050/3050), done.
Checking connectivity... done.
嗯嗯,好的开头是成功的一半,不错,代码取下来了。
cd beetl2.0
mvn install
输出结果:
[WARNING]
[WARNING] Some problems were encountered while building the effective settings
[WARNING] 'servers.server.id' must be unique but found duplicate server with id tiny-nexus-releases @ /Users/luoguo/Develop/apache-maven-3.1.0/conf/settings.xml
[WARNING]
[INFO] Scanning for projects...
[ERROR] The build could not read 1 project -> [Help 1]
[ERROR]
[ERROR] The project org.beetl:beetl-core:2.2.4-SNAPSHOT (/Users/luoguo/git/beetl2.0/beetl-core/pom.xml) has 1 error
[ERROR] Non-resolvable parent POM: Could not find artifact org.beetl:beetl-parent:pom:2.2.4-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 4, column 10 -> [Help 2]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectBuildingException
[ERROR] [Help 2] http://cwiki.apache.org/confluence/display/MAVEN/UnresolvableModelException
咦,这是什么鬼?
猜想是由于我用的是maven 3.1.x导致,于是升级到maven 3.3.3,执行 mvn install,可以看到开始下载相关的资源文件了,OK,起步还是不错的,这里需要耐心等待一段时间。
咦,停止了,看到一堆错误,再看看是什么问题?
[ERROR] /Users/luoguo/git/beetl2.0/beetl-core/src/main/java/org/beetl/ext/jodd/BeetlActionResult.java:[13,8] org.beetl.ext.jodd.BeetlActionResult不是抽象的, 并且未覆盖jodd.madvoc.result.ActionResult中的抽象方法getResultType()
[ERROR] /Users/luoguo/git/beetl2.0/beetl-core/src/main/java/org/beetl/ext/jodd/BeetlActionResult.java:[60,9] 方法不会覆盖或实现超类型的方法
[INFO] 2 errors
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] beetl-core ......................................... FAILURE [ 44.926 s]
[INFO] beetl-parent ....................................... SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 45.061 s
[INFO] Finished at: 2015-07-28T14:08:38+08:00
[INFO] Final Memory: 18M/262M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project beetl-core: Compilation failure: Compilation failure:
[ERROR] /Users/luoguo/git/beetl2.0/beetl-core/src/main/java/org/beetl/ext/jodd/BeetlActionResult.java:[13,8] org.beetl.ext.jodd.BeetlActionResult不是抽象的, 并且未覆盖jodd.madvoc.result.ActionResult中的抽象方法getResultType()
[ERROR] /Users/luoguo/git/beetl2.0/beetl-core/src/main/java/org/beetl/ext/jodd/BeetlActionResult.java:[60,9] 方法不会覆盖或实现超类型的方法
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
看起来是Beetl继承了jodd的类,但是有些方法没有实现,
![](http://my.oschina.net/js/ke/plugins/emoticons/images/17.gif)
没有办法,只要增加fae指令再来执行:
mvn clean install -fae
结果还是原样的错误,至此已经无法进行。
根据文件名分析,这个东东可能是对jodd的一个扩展,理论上可以删除之,于是删除了类BeetlActionResult,然后重新执行mvn install
这次出来的结果是:
[INFO] Reactor Summary:
[INFO]
[INFO] beetl-core ......................................... SUCCESS [03:52 min]
[INFO] beetl-parent ....................................... SUCCESS [ 0.008 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 03:52 min
[INFO] Finished at: 2015-07-28T14:26:09+08:00
[INFO] Final Memory: 25M/309M
[INFO] ------------------------------------------------------------------------
从环境搭建的过程来看,只要是maven 3.3.3,搭建还算顺利,美中不足是有一个Jodd的扩展
BeetlActionResult类有问题。通过直接删除,编译是通过了,有多大的影响,暂时还不清楚。
Beetl工程结构静态分析
从这里看,整体来说还可以,把一些bak文件上传上来,稍嫌不严谨,另外有些jpg文件直接放在根目录也有一点点乱,如果整理一下就更好了。
接下来比较关心core
这里面有几个东东,就有点难理解了,为什么这里放了个jar文件?为什么这里放了个lib目录?为什么这里放了个performance工程?性能评测的代码怎么会放到core工程中??
上面这个应该就是关键工程了?core应该就是引擎核心代码所在的位置,ext应该是它对各种开源框架方面的扩展或支持。有这些扩展还是非常不错的,方便使用者上手,赞一个。但是把ext和core放在一个工程里还是有点随意了,如果能把ext单独开个工程就更好了。
相对应的,可以看看Tiny模板引擎的目录结构:
就简洁清爽多了。
再来看看beetl模板的代码行数:
可以看到core工程中的java代码是20291行,不算空行,不算注释行。
Tiny模板引擎的代码行数,纯纯的java代码只有4944行,也就是beetl的代码整整是Tiny模板引擎4倍多。
上面是Beetl的sonar检查情况
上面的统计数据是Tiny模板引擎的统计数据:
这里的数据和上面用Statistics统计的数据稍有区别,但是基本上差别不大。
从上面的数据可以看出:
项目 | Beetl | Tiny模板引擎 |
代码行数 | 23087 | 4944 |
文件数 | 230 | 171 |
重复 | 3.3% | 0.0% |
复杂度 | 2.8/方法 | 1.9/方法 |
包耦合指数 | 31.5% | 31.6% |
包耦合循环 | >35 | >18 |
从代码规模来说,Tiny完胜,只有Beetl的不到1/4。代码重复率方面Beetl也相当不错了,当然Tiny的更好一点。复杂度Beetl方面也不错,当然 Tiny的要更好一点。包耦合指数方面差不多,但是包耦合循环方面,tiny只有Beetl的一半。
从上面的数据来看,Tiny的方法更小,包依赖的长度更短,更容易维护。
OK,从上面的静态分析来看,Beetl的包结构组织有进步的空间,有一定的代码重复,整体代码质量还不错,但是包耦合度有点高,所以其可维护性较Tiny稍弱。
Beetl语法
到main/antlr中查找Beetl语法定义文件,居然没有找到,最后终于在下面的位置main/java/org/beetl/core/parser/BeetlParser.g4找到了,为什么不能完全遵循Maven规范呢?
statement
: block #blockSt
| textStatment #textOutputSt
| constantsTextStatment #staticOutputSt
| COMMENT_TAG commentTypeTag #commentTagSt
| If parExpression statement (Else statement)? #ifSt
| For LEFT_PAR forControl RIGHT_PAR statement ( Elsefor statement)? #forSt
| While parExpression statement #whileSt
| Switch parExpression switchBlock #siwchSt
| Select g_switchStatment #selectSt
| Try block (Catch LEFT_PAR Identifier? RIGHT_PAR block )? #trySt
| Return expression? END #returnSt
| Break END #breakSt
| Continue END #continueSt
| Var varDeclareList END #varSt
| Directive directiveExp #directiveSt
| assignMent END #assignSt
| functionTagCall #functionTagSt
| statementExpression END #statmentExpSt
| Ajax Identifier COLON block #ajaxSt
| END #end
上面是Beetl支持的语法。
tiny模板引擎支持的语法有:
directive : set_directive
| if_directive
| while_directive
| for_directive
| break_directive
| import_directive
| continue_directive
| stop_directive
| include_directive
| macro_directive
| layout_directive
| layout_impl_directive
| call_block_directive
| call_directive
| endofline_directive
| blank_directive
| tabs_directive
| indent_directive
| dent_directive
| call_macro_directive
| call_macro_block_directive
| bodycontent_directive
| invalid_directive
;
二者做个对比:
语法体系的差异,Beetl采用的是类似jsp的方式,而Tiny模板引擎采用的是Velocity的方式,二者各有优缺点,因此并无好坏之分,只是萝卜青菜上的差异。从我本人来说,是非常讨厌类似于<% ... %>来方式圈定脚本,而更喜欢Velocity的直接用指令嵌入的方式来进行使用,所以我选择了类 Velocity的方式。因此语法体系方面没有什么好比较的。
对于常规指令Beetl和Tiny模板引擎都有良好的支持
- 循环指令两者都支持for和while,都支持break,contine,stop/return等。同时也都支持else,也就是当循环次数为0时,执行一些操作,比如:有数据的时候在循环体内展示数据,没有数据的时候显示else中的默认内容。
- 在条件判断方面Beetl支持了if、switch、select等指令,而tiny模板引擎则是由强大的#if() ... #elseif()... #else...#end指令格式来完成所有的条件判断,两者功能都可以互相覆盖。
项目 | Beetl | Tiny |
定义临时变量 | var number=1 | #set(number=1) |
定义页面变量 | template.binding("number",1) | #!set(number=1) |
属性引用 | ${user.wife.name} | ${user.wife.name} |
算述表达式 | <% var a1 = 12; var b1 = (a1+15)/3-2*a1; var bc = -1-b1; %> ${bc} | #set(a1=12,b1 = (a1+15)/3-2*a1,bc = -1-b1) |
逻辑表达式 | <% var a1 = 12; var b1 = a1==12; var b2 = a1!=12; %> ${b1} ${b2} | #set(a1 = 12,b1 = a1==12,b2 = a1!=12) ${b1} ${b2} |
循环语句 | <% print("总共"+userList.~size+"<br>"); for(user in userList){ %> ${userLP.index} ${user.name} <br> <%}%> | 总共${userList.size()} |
条件语句 | <% var user = map["001"]; if(user.name=="lijz"){ print(user.name); }else{ return ; } %> | #set(user = map."001") |
函数调用 | <% print("hello"); println("hello"); printf("hello,%s,your age is %s","lijz",12+""); %> | ${format("hello")} ${format("hello,%s,your age is %s","lijz",12)} |
格式化 | <% var now = date(); var date = date("2013-1-1","yyyy-MM-dd"); %> now=${now,dateFormat='yyyy年MM月dd日'} date=${date,dateFormat='yyyy年MM月dd日'} or now=${now,'yyyy年MM月dd日'} | tiny模板引擎不允许动态创建对象,但是允许通过自定义函数或SpringBean来获取对象。 |
成员方法调用 | <% var list = [5,2,4]; %> ${ @java.util.Collections.max(list)} | #set( list = [5,2,4]) |
安全输出 | <% ${user2.wife.name!} ${user3.wife.name!"单身"} | #set(user1 = null,user2 = null,user3 = {"name":"lijz",wife:{'name':'lucy'}}) ${user2?.wife?.name?:"单身"} ${user3?.wife?.name?:"单身"} |
注释 | <% //最大值是12; | ##最大值是12; |
上面做了两个模板引擎的常规指令的示例和对比,基本上采用Beetl在线示例中的示例然后用Tiny模板引擎的语法来同样实现的功能。
下面来说说一些有意思的高级功能
项目 | Beetl | Tiny模板引擎 | |
异常处理 | <% | Tiny模板引擎的设计者认为如果让模板引擎来处理异常,实际上是有点过度设计的意味,而应该是系统的异常处理框架去处理之。模板只参与展示层的处理,不参与业务逻辑处理。 | |
虚拟属性 |
| ${user.toJson()} Tiny支持为某种类增加一些扩展的成员函数,和Beetl的虚拟属性的意思是相同的,但是在函数调用过程中,使用方式与原生成员函数没有区别。如果扩展的方法是getXxx,那么就可以直接调用object.xxx的方式按属性的方式来进行调用。 | |
函数扩展 | <% | Tiny也提供了函数扩展体系,也完全可以添加类似的函数扩展,调用方式也差不多。 | |
标签的支持 | public class CmsContentTag extends GeneralVarTagBinding { | Tiny没有提供标签的扩展功能,却提供了强大的宏定义功能 调用方式: 带内容宏定义前置信息 调用方式: 运行结果: 由于Tiny采用的是全部在模板语言中实现的方式,因此定义和使用文本内容更方便,同时在定义和使用时的嵌套支持能力会使得DRY原则得以全面实施,可以整个页面没有重复内容的出现。 | |
布局支持 | content.html内容如下: layout.html 是布局文件 运行结果: 运行content.html模板文件后,,正文文件的内容将被替换到layoutContent的地方,变成如下内容 | Tiny的做法是: 首先新建content.layout文件 再新建content.page文件 然后访问content.page,运行结果就是: 实际上Tiny模板引擎还支持默认布局,多重布局各种花样玩样,由于采用了COC的方式,所以不需要在模板语言中显式引入布局,而是通过目录结构的方式来确定布局渲染方式。在进行重构的时候更也加方便,比如:同样一个文件,放在不同的目录结构中,由于渲染的布局不同,就会出现完全不一样的效果,这在进行重构的时候也更加方便。 Tiny在.layout中还支持指令#layout,如下: 上面就定义了两个布局占位,一个叫aaaInfo,一个叫bbbInfo, 在具体的页面文件中,可以用: 来覆盖默认的定义,转而显示新的内容,如果不覆盖的话,就显示默认的信息,这里通过引入Java的OverRide的机制,提供了更灵活多变的布局能力。 | |
宏引入 | | 由于Tiny支持把公用的宏用独立的文件来进行存放,相当于Library,但是由于不同的人定义的库有可能有宏名冲突。因此Tiny引入了#import指令来优先使用先import进来的库中的宏,如下: 如果出现同名的宏,那么liba中的会被执行 | |
安全调用 | Beetl采用的是安全表达式的方式来处理安全谳用 | Tiny的在调用属性或成员函数时,可以显式用“?.”来表示安全属性调用,而用“.”来表示非安全属性调用,这样写模板时需要明确使用哪个,这样可以及时发现应用中的问题。 | |
错误提示 | 错误提示如下: beetl只给出了具体的位置在哪一行,以及整个模板(或者比较近位置的模板)内容。 | 错误提示如下: Tiny则明确给出了精确的坐标,x1,y1-x2,y2,同时还给出了具体出问题的内容,相对来说程序员查找问题更加迅捷。 | |
| | |
工具的支持
beetl的插件功能
Beetl插件如约而来!
安装说明:
本插件是beetl模板语言插件,请放到dropins目录下重启即可。如果以前安装过,需要删除以前保本
如果文件以.btl结尾,则自动以插件方式打开,否则,可以通过右键此文件,选择open-with,并选择beetl editor,不建议使用btl结尾,请尽量使用原有编辑器,参考使用说明4快捷使用beetl editor
使用说明:
1 工程属性里有个beetl属性,可以指定定界符号等,默认是<%%> ${}。也可以指定模板根目录(可选,不必手工填写,在模板单击定位里会提示你选择)
2 ctrl-2 定位到下一个beetl 块
3 ctrl-3 定位到上一个beetl块
4 ctrl-4 将普通文件以beetl editor方式打开,并保持同步编辑
5 ctrl-5 静态文本全部折叠和打开静态文本折叠
6 可以ctrl+单击字符串定位到字符串对应的模板文件,第一次使用的时候,需要选择模板根目录,随后,也可以在project属性的beetl配置里配置模板根目录
7 alt-/ 进行上下文提示。也可以键入此快速输入定界符号和占位符号
8 alt-shift-p 从{ 快速移动到 匹配的},或者反之亦然。如果只单击{ 则会框选住匹配的} 而光标不移动
9 选中任何id,都能全文框选住同样的id。
10 ctrl-/ 单行注释,或者取消注释
11 通常eclipse具有的快捷操作方式,beetl仍然予以保留不变
12 具备一定的错误提示,目前只提示第一个发现的错误。
Tiny模板引擎的插件功能
- 大纲支持:支持在大纲当中显示一些关键内容,并可以快速定位
- 语法高亮:支持在编辑器中,根据语法进行着色,使得代码更容易阅读和排错
- 错误提示:如果模板语言存在错误,则可以在工程导航、错误视图及编辑窗口进行错误提示
- 代码折叠:支持对代码块进行代码折叠,方便查阅
- 语法提示:支持Tiny模板引擎语法提示及Html语法提示方便快速录入
- 快速定位:支持Tiny模板中开始语句与结束语句间快速切换
- 变量快速提示:点鼠标点击某变量时,会高亮显示文件中的所有同名变量
- 宏定义对应位置显示:在tiny块处理的标签头部按ctrl时,会高亮显示与其对应的#end,反之亦然
- 格式化:可以按快捷键ctrl+shift+F进行格式化了
- 注释处理:可以按快捷键ctrl+/来进行快速设置单行注释或取消单行注释,可以按ctrl+shift+/来进行快速设置块注释或取消块注释
由于篇幅太长,因此这里不贴完整内容,详细请看链接:http://my.oschina.net/tinyframework/blog/365370
OK,工具上完全不在一个等级上。
代码质量对比
代码质量这个本身没有唯一标准,这里贴一下类似的功能的代码对比,不做评论:
for语句实现
Beetl版
public final class ForStatement extends Statement implements IGoto
{
public Expression idNode;
public Expression exp;
public Statement forPart;
public Statement elseforPart;
public boolean hasGoto = false;
public short itType = 0;
public boolean hasSafe;
/**
* for(idNode in exp) {forPath}elsefor{elseforPart}
* @param idNode
* @param exp
* @param forPart
* @param elseforPart
* @param token
*/
public ForStatement(VarDefineNode idNode, Expression exp, boolean hasSafe, Statement forPart,
Statement elseforPart, GrammarToken token)
{
super(token);
this.idNode = idNode;
this.exp = exp;
this.hasSafe = hasSafe;
this.elseforPart = elseforPart;
this.forPart = forPart;
}
public final void execute(Context ctx)
{
// idNode 是其后设置的
int varIndex = ((IVarIndex) idNode).getVarIndex();
Object collection = exp.evaluate(ctx);
IteratorStatus it = null;
if (collection == null)
{
if (!this.hasSafe)
{
BeetlException ex = new BeetlException(BeetlException.NULL);
ex.pushToken(exp.token);
throw ex;
}
else
{
it = new IteratorStatus(Collections.EMPTY_LIST);
}
}
else
{
it = IteratorStatus.getIteratorStatusByType(collection, itType);
if (it == null)
{
BeetlParserException ex = new BeetlParserException(BeetlParserException.COLLECTION_EXPECTED_ERROR);
ex.pushToken(exp.token);
throw ex;
}
}
ctx.vars[varIndex + 1] = it;
// loop_index
// ctx.vars[varIndex+2] = 0;
// ctx.vars[varIndex+3] = it.getSize();
//
if (this.hasGoto)
{
while (it.hasNext())
{
ctx.vars[varIndex] = it.next();
forPart.execute(ctx);
switch (ctx.gotoFlag)
{
case IGoto.NORMAL:
break;
case IGoto.CONTINUE:
ctx.gotoFlag = IGoto.NORMAL;
continue;
case IGoto.RETURN:
return;
case IGoto.BREAK:
ctx.gotoFlag = IGoto.NORMAL;
return;
}
}
if (!it.hasData())
{
if (elseforPart != null)
elseforPart.execute(ctx);
}
return;
}
else
{
while (it.hasNext())
{
ctx.vars[varIndex] = it.next();
forPart.execute(ctx);
}
if (!it.hasData())
{
if (elseforPart != null)
elseforPart.execute(ctx);
}
}
}
@Override
public final boolean hasGoto()
{
// TODO Auto-generated method stub
return hasGoto;
}
@Override
public final void setGoto(boolean occour)
{
this.hasGoto = occour;
}
@Override
public void infer(InferContext inferCtx)
{
exp.infer(inferCtx);
if (exp.getType().types != null)
{
if (Map.class.isAssignableFrom(exp.getType().cls))
{
idNode.type = Type.mapEntryType;
}
else
{
//list or array
idNode.type = exp.getType().types[0];
}
}
else
{
idNode.type = Type.ObjectType;
}
int index = ((IVarIndex) idNode).getVarIndex();
inferCtx.types[index] = idNode.type;
inferCtx.types[index + 1] = new Type(IteratorStatus.class, idNode.type.cls);
forPart.infer(inferCtx);
if (elseforPart != null)
{
elseforPart.infer(inferCtx);
}
}
}
Tiny版
public class ForProcessor implements ContextProcessor<TinyTemplateParser.For_directiveContext> {
public Class<TinyTemplateParser.For_directiveContext> getType() {
return TinyTemplateParser.For_directiveContext.class;
}
public boolean processChildren() {
return false;
}
public Object process(TemplateInterpreter interpreter, TemplateFromContext templateFromContext, TinyTemplateParser.For_directiveContext parseTree, TemplateContext pageContext, TemplateContext context, TemplateEngineDefault engine, Writer writer, String fileName) throws Exception {
String name = parseTree.for_expression().IDENTIFIER().getText();
Object values = interpreter.interpretTree(engine, templateFromContext, parseTree.for_expression().expression(),pageContext, context, writer,fileName);
ForIterator forIterator = new ForIterator(values);
context.put(name + "For", forIterator);
boolean hasItem = false;
while (forIterator.hasNext()) {
TemplateContext forContext=new TemplateContextDefault();
forContext.setParent(context);
hasItem = true;
Object value = forIterator.next();
forContext.put(name, value);
try {
interpreter.interpretTree(engine, templateFromContext, parseTree.block(),pageContext, forContext, writer,fileName );
} catch (ForBreakException be) {
break;
} catch (ForContinueException ce) {
continue;
}
}
if (!hasItem) {
TinyTemplateParser.Else_directiveContext elseDirectiveContext = parseTree.else_directive();
if (elseDirectiveContext != null) {
interpreter.interpretTree(engine, templateFromContext, elseDirectiveContext.block(), pageContext,context, writer,fileName);
}
}
return null;
}
}
解释引擎核心处理代码
Beetl版
beetl版源代码,由于太长,所以就不贴内容了,详细请点击查看源码
Tiny版
public class TemplateInterpreter {
TerminalNodeProcessor[] terminalNodeProcessors = new TerminalNodeProcessor[200];
Map<Class<ParserRuleContext>, ContextProcessor> contextProcessorMap = new HashMap<Class<ParserRuleContext>, ContextProcessor>();
OtherTerminalNodeProcessor otherNodeProcessor = new OtherTerminalNodeProcessor();
public void addTerminalNodeProcessor(TerminalNodeProcessor processor) {
terminalNodeProcessors[processor.getType()] = processor;
}
public void addContextProcessor(ContextProcessor contextProcessor) {
contextProcessorMap.put(contextProcessor.getType(), contextProcessor);
}
public TinyTemplateParser.TemplateContext parserTemplateTree(String sourceName, String templateString) {
char[] source = templateString.toCharArray();
ANTLRInputStream is = new ANTLRInputStream(source, source.length);
// set source file name, it will be displayed in error report.
is.name = sourceName;
TinyTemplateParser parser = new TinyTemplateParser(new CommonTokenStream(new TinyTemplateLexer(is)));
return parser.template();
}
public void interpret(TemplateEngineDefault engine, TemplateFromContext templateFromContext, String templateString, String sourceName, TemplateContext pageContext, TemplateContext context, Writer writer, String fileName) throws Exception {
interpret(engine, templateFromContext, parserTemplateTree(sourceName, templateString), pageContext, context, writer,fileName );
writer.flush();
}
public void interpret(TemplateEngineDefault engine, TemplateFromContext templateFromContext, TinyTemplateParser.TemplateContext templateParseTree, TemplateContext pageContext, TemplateContext context, Writer writer, String fileName) throws Exception {
for (int i = 0; i < templateParseTree.getChildCount(); i++) {
interpretTree(engine, templateFromContext, templateParseTree.getChild(i), pageContext, context, writer,fileName );
}
}
public Object interpretTree(TemplateEngineDefault engine, TemplateFromContext templateFromContext, ParseTree tree, TemplateContext pageContext, TemplateContext context, Writer writer, String fileName) throws Exception {
Object returnValue = null;
if (tree instanceof TerminalNode) {
TerminalNode terminalNode = (TerminalNode) tree;
TerminalNodeProcessor processor = terminalNodeProcessors[terminalNode.getSymbol().getType()];
if (processor != null) {
returnValue = processor.process(terminalNode, context, writer);
} else {
returnValue = otherNodeProcessor.process(terminalNode, context, writer);
}
} else if (tree instanceof ParserRuleContext) {
try {
ContextProcessor processor = contextProcessorMap.get(tree.getClass());
if (processor != null) {
returnValue = processor.process(this, templateFromContext, (ParserRuleContext) tree, pageContext, context, engine, writer,fileName);
}
if (processor == null || processor != null && processor.processChildren()) {
for (int i = 0; i < tree.getChildCount(); i++) {
Object value = interpretTree(engine, templateFromContext, tree.getChild(i), pageContext, context, writer,fileName );
if (value != null) {
returnValue = value;
}
}
}
} catch (StopException se) {
throw se;
} catch (TemplateException te) {
if (te.getContext() == null) {
te.setContext((ParserRuleContext) tree,fileName);
}
throw te;
} catch (Exception e) {
throw new TemplateException(e, (ParserRuleContext) tree,fileName);
}
} else {
for (int i = 0; i < tree.getChildCount(); i++) {
Object value = interpretTree(engine, templateFromContext, tree.getChild(i), pageContext, context, writer,fileName );
if (returnValue == null && value != null) {
returnValue = value;
}
}
}
return returnValue;
}
public static void write(Writer writer, Object object) throws IOException {
if (object != null) {
writer.write(object.toString());
}
}
}
嗯嗯,不到100行的规模
当然整个通读下来,就会慢慢发现为什么Tiny的代码行数这么少功能却又多的原因之所在了。
总结
Beetl算得上是较好的模板语言框架和不错的开源项目,但是距离“最好的”三个字还是有一定差距的,作为@闲.大赋 的粉丝,偶会持续支持他,也希望他能再积再累,真正当得起“最好的”三个字。
补充说明
beetl里面有4014行由antlr生成的代码,实际统计中,由于Beetl的目录结构没有按标准化的来,导致统计中包含了这部分代码,因此实际上,应该是在16000+,因此规模是Tiny模板引擎的3倍左右,特此纠正。