輸入命令 html
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,起步仍是不錯的,這裏須要耐心等待一段時間。 java
咦,中止了,看到一堆錯誤,再看看是什麼問題? git
[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 ajax
此次出來的結果是: shell
[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文件直接放在根目錄也有一點點亂,若是整理一下就更好了。 express
接下來比較關心core apache
這裏面有幾個東東,就有點難理解了,爲何這裏放了個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的方法更小,包依賴的長度更短,更容易維護。
OK,從上面的靜態分析來看,Beetl的包結構組織有進步的空間,有必定的代碼重複,總體代碼質量還不錯,可是包耦合度有點高,因此其可維護性較Tiny稍弱。
到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模板引擎都有良好的支持
項目 | 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沒有提供標籤的擴展功能,卻提供了強大的宏定義功能 #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/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倍左右,特此糾正。