输入命令
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的类,但是有些方法没有实现,
。
没有办法,只要增加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类有问题。通过直接删除,编译是通过了,有多大的影响,暂时还不清楚。
从这里看,整体来说还可以,把一些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 |
重复 | 0.0% | |
复杂度 | 2.8/方法 | 1.9/方法 |
包耦合指数 | 31.6% | |
包耦合循环 | >18 |
从代码规模来说,Tiny完胜,只有Beetl的不到1/4。代码重复率方面Beetl也相当不错了,当然Tiny的更好一点。复杂度Beetl方面也不错,当然 Tiny的要更好一点。包耦合指数方面差不多,但是包耦合循环方面,tiny只有Beetl的一半。
从上面的数据来看,Tiny的方法更小,包依赖的长度更短,更容易维护。
OK,从上面的静态分析来看,Beetl的包结构组织有进步的空间,有一定的代码重复,整体代码质量还不错,但是包耦合度有点高,所以其可维护性较Tiny稍弱。
到main/antlr中查找Beetl语法定义文件,居然没有找到,最后终于在下面的位置main/java/org/beetl/core/parser/BeetlParser.g4找到了,为什么不能完全遵循Maven规范呢?
Tiny模板引擎则完全遵守规范。
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模板引擎都有良好的支持
项目 | 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 ) ${bc} 当然,#set指令也可以一行写一个赋值指令 |
逻辑表达式 | <% 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() } #for(user in userList) ${userFor.index} ${user.name} #end |
条件语句 | <% var user = map["001"]; if(user.name=="lijz"){ print(user.name); }else{ return ; } %> | #set(user = map."001") #if(user.name=="lijz") ${user.name} #else #return #end |
函数调用 | <% print("hello"); println("hello"); printf("hello,%s,your age is %s","lijz",12+""); %> | ${format("hello")}${format("hello/n")} ${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来获取对象。 假设,这里在上下文中在now和date两个变量 now=${format(now,'yyyy年MM月dd日 HH:mm:SS')} date=${format(date,'yyyy年MM月dd日')} |
成员方法调用 | <% var list = [5,2,4]; %> ${ @java.util.Collections.max(list)} | #set( list = [5,2,4]) ${list.get(1)} |
安全输出 | <% var user1 = null; var user2 = null; var user3 = {"name":"lijz",wife:{'name':'lucy'}}; %> ${user2.wife.name!} ${user3.wife.name!"单身"} | #set(user1 = null, user2 = null, ${user1?.wife?.name?:"单身"} user3 = {"name":"lijz",wife:{'name':'lucy'}}) %> ${user2?.wife?.name ?:"单身" } ${user3?.wife?.name ?:"单身" } |
注释 | <% //最大值是12; /*最大值是12*/ var max = 12;%> | ## 最大值是12; #*最大值是12*# #set( max = 12) |
模板引擎的语法来同样实现的功能。
下面来说说一些有意思的高级功能
项目 | Beetl | Tiny模板引擎 |
异常处理 | <% try{ callOtherSystemView() } catch(error){ print( "暂时无数据"); } %> | Tiny模板引擎的设计者认为如果让模板引擎来处理异常,实际上是有点过度设计的意味,而应该是系统的异常处理框架去处理之。模板只参与展示层的处理,不参与业务逻辑处理。 |
虚拟属性 | ${user. ~ genderShowName} | ${user.toJson()} Tiny支持为某种类增加一些扩展的成员函数,和Beetl的虚拟属性的意思是相同的,但是在函数调用过程中,使用方式与原生成员函数没有区别。如果扩展的方法是getXxx,那么就可以直接调用object.xxx的方式按属性的方式来进行调用。 |
函数扩展 | <% var date =date(); var len = strutil.len( "cbd"); println( "len=" +len); %> | Tiny也提供了函数扩展体系,也完全可以添加类似的函数扩展,调用方式也差不多。#set(date =date(),len=strutil.len("cbd")) |
标签的支持 | public class CmsContentTag extends GeneralVarTagBinding { public void render(){ Object id= this.getAttributeValue("id"); try {ctx.byteWriter.writeString("当前定义了一个窜上:"+id.toString()); } catch (IOException e){ e.printStackTrace(); } } } | Tiny没有提供标签的扩展功能,却提供了强大的宏定义功能 简单宏定义 #macro cmsContent(id) 当前定义了一个内容:${id} #end 调用方式: #cmsContent("abc") 带内容宏定义 前置信息 #macro contentFrame() 前置信息 #bodyContent 后置信息 #end 调用方式: #@contentFrame() 这里是一些信息 #end 运行结果: 后置信息 这里是一些信息 调用方式: 由于Tiny采用的是全部在模板语言中实现的方式,因此定义和使用文本内容更方便,同时在定义和使用时的嵌套支持能力会使得DRY原则得以全面实施,可以整个页面没有重复内容的出现。 |
布局支持 | content.html内容如下: <% //content.html内容如下: layout("/inc/layout.html"){%> this is 正文 .......... <%%}%>layout.html 是布局文件 <% <%include("/inc/header.html"){} %> this is content:${layoutContent} this is footer: <%%}%>运行结果: 运行content.html模板文件后,,正文文件的内容将被替换到layoutContent的地方,变成如下内容 this is header this is content:this is 正文 ............ this is footer: | Tiny的做法是: 首先新建content.layout文件 this is header this is content:#pageContent this is footer 再新建content.page文件 this is 正文 然后访问content.page,运行结果就是: this is header this is content:this is 正文 this is footer 实际上Tiny模板引擎还支持默认布局,多重布局各种花样玩样,由于采用了COC的方式,所以不需要在模板语言中显式引入布局,而是通过目录结构的方式来确定布局渲染方式。在进行重构的时候更也加方便,比如:同样一个文件,放在不同的目录结构中,由于渲染的布局不同,就会出现完全不一样的效果,这在进行重构的时候也更加方便。 Tiny在.layout中还支持指令#layout,如下: #layout(aaaInfo) this is aaaInfo #end #layout(bbbInfo) this is bbbInfo #end 上面就定义了两个布局占位,一个叫aaaInfo,一个叫bbbInfo, 在具体的页面文件中,可以用: #@layout(aaaInfo) this is new aaaInfo #end #@layout(aaaInfo) this is new aaaInfo #end 来覆盖默认的定义,转而显示新的内容,如果不覆盖的话,就显示默认的信息,这里通过引入Java的OverRide的机制,提供了更灵活多变的布局能力。 |
宏引入 | 由于Tiny支持把公用的宏用独立的文件来进行存放,相当于Library,但是由于不同的人定义的库有可能有宏名冲突。因此Tiny引入了#import指令来优先使用先import进来的库中的宏,如下: #import("/a/b/liba.component") #import("/a/b/libb.component") 如果出现同名的宏,那么liba中的会被执行 | |
安全调用 | Beetl采用的是安全表达式的方式来处理安全谳用 | Tiny的在调用属性或成员函数时,可以显式用“?.”来表示安全属性调用,而用“.”来表示非安全属性调用,这样写模板时需要明确使用哪个,这样可以及时发现应用中的问题。 |
错误提示 | <% var a = 1; var b = a/0; %>错误提示如下: >>DIV_ZERO_ERROR:0 位于3行 资源:/org/beetl/sample/s0125/error1.txt 1|<% 2|var a = 1; 3|var b = a/0; 4|%>beetl只给出了具体的位置在哪一行,以及整个模板(或者比较近位置的模板)内容。 | #set(a=1,b=1/0)错误提示如下: 路径:/a.page 位置[1行11列]-[1行13列] =================================================================== 1/0 ===================================================================Tiny则明确给出了精确的坐标,x1,y1-x2,y2,同时还给出了具体出问题的内容,相对来说程序员查找问题更加迅捷。 |
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 具备一定的错误提示,目前只提示第一个发现的错误。
由于篇幅太长,因此这里不贴完整内容,详细请看链接: http://my.oschina.net/tinyframework/admin/edit-blog?blog=365370
OK,工具上完全不在一个等级上。
代码质量这个本身没有唯一标准,这里贴一下类似的功能的代码对比,不做评论:
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); } } }
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版源代码,由于太长,所以就不贴内容了,详细请点击查看源码
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倍左右,特此纠正。