Beetl的環(huán)境搭建
輸入命令
1 |
git clone https://git.oschina.net/xiandafu/beetl2.0.git |
不一會兒,輸出了下面的內(nèi)容
1 2 3 4 5 6 7 |
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. |
嗯嗯,好的開頭是成功的一半,不錯(cuò),代碼取下來了。
1 2 |
cd beetl2.0 mvn install |
輸出結(jié)果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[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/confluen ... ctBuildingException [ERROR] [Help 2] http://cwiki.apache.org/confluen ... vableModelException |
咦,這是什么鬼? 猜想是由于我用的是maven 3.1.x導(dǎo)致,于是升級到maven 3.3.3,執(zhí)行 mvn install,可以看到開始下載相關(guān)的資源文件了,OK,起步還是不錯(cuò)的,這里需要耐心等待一段時(shí)間。
咦,停止了,看到一堆錯(cuò)誤,再看看是什么問題?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
[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] 方法不會覆蓋或?qū)崿F(xiàn)超類型的方法 [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] 方法不會覆蓋或?qū)崿F(xiàn)超類型的方法 [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/confluen ... ojoFailureException |
看起來是Beetl繼承了jodd的類,但是有些方法沒有實(shí)現(xiàn),

1 |
mvn clean install -fae |
結(jié)果還是原樣的錯(cuò)誤,至此已經(jīng)無法進(jìn)行。 根據(jù)文件名分析,這個(gè)東東可能是對jodd的一個(gè)擴(kuò)展,理論上可以刪除之,于是刪除了類BeetlActionResult,然后重新執(zhí)行mvn install
這次出來的結(jié)果是:
1 2 3 4 5 6 7 8 9 10 11 |
[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] ------------------------------------------------------------------------ |
從環(huán)境搭建的過程來看,只要是maven 3.3.3,搭建還算順利,美中不足是有一個(gè)Jodd的擴(kuò)展 BeetlActionResult類有問題。通過直接刪除,編譯是通過了,有多大的影響,暫時(shí)還不清楚。
Beetl工程結(jié)構(gòu)靜態(tài)分析

從這里看,整體來說還可以,把一些bak文件上傳上來,稍嫌不嚴(yán)謹(jǐn),另外有些jpg文件直接放在根目錄也有一點(diǎn)點(diǎn)亂,如果整理一下就更好了。
接下來比較關(guān)心core

這里面有幾個(gè)東東,就有點(diǎn)難理解了,為什么這里放了個(gè)jar文件?為什么這里放了個(gè)lib目錄?為什么這里放了個(gè)performance工程?性能評測的代碼怎么會放到core工程中??

上面這個(gè)應(yīng)該就是關(guān)鍵工程了?core應(yīng)該就是引擎核心代碼所在的位置,ext應(yīng)該是它對各種開源框架方面的擴(kuò)展或支持。有這些擴(kuò)展還是非常不錯(cuò)的,方便使用者上手,贊一個(gè)。但是把ext和core放在一個(gè)工程里還是有點(diǎn)隨意了,如果能把ext單獨(dú)開個(gè)工程就更好了。

從上面的目錄結(jié)構(gòu)看還是不錯(cuò)的,但是很顯然下面的一些類和接口看起來就比較亂了,應(yīng)該相當(dāng)有改進(jìn)的空間。 相對應(yīng)的,可以看看Tiny模板引擎的目錄結(jié)構(gòu):

就簡潔清爽多了。
再來看看beetl模板的代碼行數(shù):

可以看到core工程中的java代碼是20291行,不算空行,不算注釋行。

Tiny模板引擎的代碼行數(shù),純純的java代碼只有4944行,也就是beetl的代碼整整是Tiny模板引擎4倍多。

上面是Beetl的sonar檢查情況

上面的統(tǒng)計(jì)數(shù)據(jù)是Tiny模板引擎的統(tǒng)計(jì)數(shù)據(jù):
這里的數(shù)據(jù)和上面用Statistics統(tǒng)計(jì)的數(shù)據(jù)稍有區(qū)別,但是基本上差別不大。
從上面的數(shù)據(jù)可以看出:
項(xiàng)目 | Beetl |
Tiny模板引擎 |
代碼行數(shù) | 23087 | 4944 |
文件數(shù) | 230 | 171 |
重復(fù) | 3.3% |
0.0% |
復(fù)雜度 | 2.8/方法 |
1.9/方法 |
包耦合指數(shù) | 31.5% |
31.6% |
包耦合循環(huán) | >35 |
>18 |
從代碼規(guī)模來說,Tiny完勝,只有Beetl的不到1/4。代碼重復(fù)率方面Beetl也相當(dāng)不錯(cuò)了,當(dāng)然Tiny的更好一點(diǎn)。復(fù)雜度Beetl方面也不錯(cuò),當(dāng)然 Tiny的要更好一點(diǎn)。包耦合指數(shù)方面差不多,但是包耦合循環(huán)方面,tiny只有Beetl的一半。 從上面的數(shù)據(jù)來看,Tiny的方法更小,包依賴的長度更短,更容易維護(hù)。
OK,從上面的靜態(tài)分析來看,Beetl的包結(jié)構(gòu)組織有進(jìn)步的空間,有一定的代碼重復(fù),整體代碼質(zhì)量還不錯(cuò),但是包耦合度有點(diǎn)高,所以其可維護(hù)性較Tiny稍弱。
Beetl語法 到main/antlr中查找Beetl語法定義文件,居然沒有找到,最后終于在下面的位置main/java/org/beetl/core/parser/BeetlParser.g4找到了,為什么不能完全遵循Maven規(guī)范呢?

Tiny模板引擎則完全遵守規(guī)范。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
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模板引擎支持的語法有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
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 ; |
二者做個(gè)對比: 語法體系的差異,Beetl采用的是類似jsp的方式,而Tiny模板引擎采用的是Velocity的方式,二者各有優(yōu)缺點(diǎn),因此并無好壞之分,只是蘿卜青菜上的差異。從我本人來說,是非常討厭類似于<% ... %>來方式圈定腳本,而更喜歡Velocity的直接用指令嵌入的方式來進(jìn)行使用,所以我選擇了類 Velocity的方式。因此語法體系方面沒有什么好比較的。
對于常規(guī)指令Beetl和Tiny模板引擎都有良好的支持
- 循環(huán)指令兩者都支持for和while,都支持break,contine,stop/return等。同時(shí)也都支持else,也就是當(dāng)循環(huán)次數(shù)為0時(shí),執(zhí)行一些操作,比如:有數(shù)據(jù)的時(shí)候在循環(huán)體內(nèi)展示數(shù)據(jù),沒有數(shù)據(jù)的時(shí)候顯示else中的默認(rèn)內(nèi)容。
- 在條件判斷方面Beetl支持了if、switch、select等指令,而tiny模板引擎則是由強(qiáng)大的#if() ... #elseif()... #else...#end指令格式來完成所有的條件判斷,兩者功能都可以互相覆蓋。
項(xiàng)目 | Beetl |
Tiny |
定義臨時(shí)變量 | var number=1 | #set(number=1) |
定義頁面變量 | template.binding("number",1) | #!set(number=1) |
屬性引用 | ${user.wife.name} | ${user.wife.name} |
算述表達(dá)式 | <% 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} 當(dāng)然,#set指令也可以一行寫一個(gè)賦值指令 |
邏輯表達(dá)式 | <% var a1 = 12; var b1 = a1==12; var b2 = a1!=12; %> ${b1} ${b2} |
#set(a1 = 12,b1 = a1==12,b2 = a1!=12) ${b1} ${b2} |
循環(huán)語句 | <% 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 |
函數(shù)調(diào)用 | <% 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模板引擎不允許動(dòng)態(tài)創(chuàng)建對象,但是允許通過自定義函數(shù)或SpringBean來獲取對象。 假設(shè),這里在上下文中在now和date兩個(gè)變量 now=${format(now,'yyyy年MM月dd日 HH:mm:SS')} date=${format(date,'yyyy年MM月dd日')} |
成員方法調(diào)用 | <% 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'}}; %> ${user1.wife.name!"單身"} ${user2.wife.name!} ${user3.wife.name!"單身"} |
#set(user1 = null,user2 = null,user3 = {"name":"lijz",wife:{'name':'lucy'}}) ${user1?.wife?.name?:"單身"}%> ${user2?.wife?.name?:"單身"} ${user3?.wife?.name?:"單身"} |
注釋 | <% //最大值是12; /*最大值是12*/ var max = 12; %> |
##最大值是12; #*最大值是12*# #set( max = 12) |
下面來說說一些有意思的高級功能
項(xiàng)目 | Beetl | Tiny模板引擎 | ||||||||||||||||
異常處理 | <% try{ callOtherSystemView() }catch(error){ print("暫時(shí)無數(shù)據(jù)"); } %> |
Tiny模板引擎的設(shè)計(jì)者認(rèn)為如果讓模板引擎來處理異常,實(shí)際上是有點(diǎn)過度設(shè)計(jì)的意味,而應(yīng)該是系統(tǒng)的異常處理框架去處理之。模板只參與展示層的處理,不參與業(yè)務(wù)邏輯處理。 | ||||||||||||||||
虛擬屬性 |
|
${user.toJson()} Tiny支持為某種類增加一些擴(kuò)展的成員函數(shù),和Beetl的虛擬屬性的意思是相同的,但是在函數(shù)調(diào)用過程中,使用方式與原生成員函數(shù)沒有區(qū)別。如果擴(kuò)展的方法是getXxx,那么就可以直接調(diào)用object.xxx的方式按屬性的方式來進(jìn)行調(diào)用。 | ||||||||||||||||
函數(shù)擴(kuò)展 | <% var date = date(); var len = strutil.len("cbd"); println("len="+len); %> |
Tiny也提供了函數(shù)擴(kuò)展體系,也完全可以添加類似的函數(shù)擴(kuò)展,調(diào)用方式也差不多。 #set(date =date(),len=strutil.len("cbd")) | ||||||||||||||||
標(biāo)簽的支持 | public class CmsContentTag extends GeneralVarTagBinding { public void render(){ Object id= this.getAttributeValue("id"); try {ctx.byteWriter.writeString("當(dāng)前定義了一個(gè)竄上:"+id.toString()); }catch (IOException e){ e.printStackTrace(); } } } |
Tiny沒有提供標(biāo)簽的擴(kuò)展功能,卻提供了強(qiáng)大的宏定義功能 簡單宏定義
調(diào)用方式:
帶內(nèi)容宏定義 前置信息
調(diào)用方式:
運(yùn)行結(jié)果:
由于Tiny采用的是全部在模板語言中實(shí)現(xiàn)的方式,因此定義和使用文本內(nèi)容更方便,同時(shí)在定義和使用時(shí)的嵌套支持能力會使得DRY原則得以全面實(shí)施,可以整個(gè)頁面沒有重復(fù)內(nèi)容的出現(xiàn)。 | ||||||||||||||||
布局支持 | content.html內(nèi)容如下:
layout.html 是布局文件
運(yùn)行結(jié)果: 運(yùn)行content.html模板文件后,,正文文件的內(nèi)容將被替換到layoutContent的地方,變成如下內(nèi)容
|
Tiny的做法是: 首先新建content.layout文件
再新建content.page文件
然后訪問content.page,運(yùn)行結(jié)果就是:
實(shí)際上Tiny模板引擎還支持默認(rèn)布局,多重布局各種花樣玩樣,由于采用了COC的方式,所以不需要在模板語言中顯式引入布局,而是通過目錄結(jié)構(gòu)的方式來確定布局渲染方式。在進(jìn)行重構(gòu)的時(shí)候更也加方便,比如:同樣一個(gè)文件,放在不同的目錄結(jié)構(gòu)中,由于渲染的布局不同,就會出現(xiàn)完全不一樣的效果,這在進(jìn)行重構(gòu)的時(shí)候也更加方便。 Tiny在.layout中還支持指令#layout,如下:
上面就定義了兩個(gè)布局占位,一個(gè)叫aaaInfo,一個(gè)叫bbbInfo, 在具體的頁面文件中,可以用:
來覆蓋默認(rèn)的定義,轉(zhuǎn)而顯示新的內(nèi)容,如果不覆蓋的話,就顯示默認(rèn)的信息,這里通過引入Java的OverRide的機(jī)制,提供了更靈活多變的布局能力。 | ||||||||||||||||
宏引入 | 由于Tiny支持把公用的宏用獨(dú)立的文件來進(jìn)行存放,相當(dāng)于Library,但是由于不同的人定義的庫有可能有宏名沖突。因此Tiny引入了#import指令來優(yōu)先使用先import進(jìn)來的庫中的宏,如下:
如果出現(xiàn)同名的宏,那么liba中的會被執(zhí)行 | |||||||||||||||||
安全調(diào)用 | Beetl采用的是安全表達(dá)式的方式來處理安全讞用 | Tiny的在調(diào)用屬性或成員函數(shù)時(shí),可以顯式用“?.”來表示安全屬性調(diào)用,而用“.”來表示非安全屬性調(diào)用,這樣寫模板時(shí)需要明確使用哪個(gè),這樣可以及時(shí)發(fā)現(xiàn)應(yīng)用中的問題。 | ||||||||||||||||
錯(cuò)誤提示 |
錯(cuò)誤提示如下:
beetl只給出了具體的位置在哪一行,以及整個(gè)模板(或者比較近位置的模板)內(nèi)容。 |
錯(cuò)誤提示如下:
Tiny則明確給出了精確的坐標(biāo),x1,y1-x2,y2,同時(shí)還給出了具體出問題的內(nèi)容,相對來說程序員查找問題更加迅捷。 | ||||||||||||||||
工具的支持 beetl的插件功能 Beetl插件如約而來!

安裝說明:
本插件是beetl模板語言插件,請放到dropins目錄下重啟即可。如果以前安裝過,需要?jiǎng)h除以前保本
如果文件以.btl結(jié)尾,則自動(dòng)以插件方式打開,否則,可以通過右鍵此文件,選擇open-with,并選擇beetl editor,不建議使用btl結(jié)尾,請盡量使用原有編輯器,參考使用說明4快捷使用beetl editor
使用說明:
1 工程屬性里有個(gè)beetl屬性,可以指定定界符號等,默認(rèn)是<%%> ${}。也可以指定模板根目錄(可選,不必手工填寫,在模板單擊定位里會提示你選擇)2 ctrl-2 定位到下一個(gè)beetl 塊3 ctrl-3 定位到上一個(gè)beetl塊4 ctrl-4 將普通文件以beetl editor方式打開,并保持同步編輯 5 ctrl-5 靜態(tài)文本全部折疊和打開靜態(tài)文本折疊6 可以ctrl+單擊字符串定位到字符串對應(yīng)的模板文件,第一次使用的時(shí)候,需要選擇模板根目錄,隨后,也可以在project屬性的beetl配置里配置模板根目錄7 alt-/ 進(jìn)行上下文提示。也可以鍵入此快速輸入定界符號和占位符號8 alt-shift-p 從{ 快速移動(dòng)到 匹配的},或者反之亦然。如果只單擊{ 則會框選住匹配的} 而光標(biāo)不移動(dòng)9 選中任何id,都能全文框選住同樣的id。10 ctrl-/ 單行注釋,或者取消注釋11 通常eclipse具有的快捷操作方式,beetl仍然予以保留不變 12 具備一定的錯(cuò)誤提示,目前只提示第一個(gè)發(fā)現(xiàn)的錯(cuò)誤。Tiny模板引擎的插件功能
- 大綱支持:支持在大綱當(dāng)中顯示一些關(guān)鍵內(nèi)容,并可以快速定位
- 語法高亮:支持在編輯器中,根據(jù)語法進(jìn)行著色,使得代碼更容易閱讀和排錯(cuò)
- 錯(cuò)誤提示:如果模板語言存在錯(cuò)誤,則可以在工程導(dǎo)航、錯(cuò)誤視圖及編輯窗口進(jìn)行錯(cuò)誤提示
- 代碼折疊:支持對代碼塊進(jìn)行代碼折疊,方便查閱
- 語法提示:支持Tiny模板引擎語法提示及Html語法提示方便快速錄入
- 快速定位:支持Tiny模板中開始語句與結(jié)束語句間快速切換
- 變量快速提示:點(diǎn)鼠標(biāo)點(diǎn)擊某變量時(shí),會高亮顯示文件中的所有同名變量
- 宏定義對應(yīng)位置顯示:在tiny塊處理的標(biāo)簽頭部按ctrl時(shí),會高亮顯示與其對應(yīng)的#end,反之亦然
- 格式化:可以按快捷鍵ctrl+shift+F進(jìn)行格式化了
- 注釋處理:可以按快捷鍵ctrl+/來進(jìn)行快速設(shè)置單行注釋或取消單行注釋,可以按ctrl+shift+/來進(jìn)行快速設(shè)置塊注釋或取消塊注釋
OK,工具上完全不在一個(gè)等級上。
代碼質(zhì)量對比
代碼質(zhì)量這個(gè)本身沒有唯一標(biāo)準(zhǔn),這里貼一下類似的功能的代碼對比,不做評論:
for語句實(shí)現(xiàn) Beetl版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
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 是其后設(shè)置的 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版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
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版源代碼,由于太長,所以就不貼內(nèi)容了,詳細(xì)請點(diǎn)擊查看源碼
Tiny版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
public class TemplateInterpreter { TerminalNodeProcessor[] terminalNodeProcessors = new TerminalNodeProcessor[200]; Map<Class< ![]() ![]() 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行的規(guī)模
當(dāng)然整個(gè)通讀下來,就會慢慢發(fā)現(xiàn)為什么Tiny的代碼行數(shù)這么少功能卻又多的原因之所在了。
總結(jié)
Beetl算得上是較好的模板語言框架和不錯(cuò)的開源項(xiàng)目,但是距離“最好的”三個(gè)字還是有一定差距的,作為@閑.大賦 的粉絲,偶會持續(xù)支持他,也希望他能再積再累,真正當(dāng)?shù)闷?#8220;最好的”三個(gè)字。
補(bǔ)充說明
beetl里面有4014行由antlr生成的代碼,實(shí)際統(tǒng)計(jì)中,由于Beetl的目錄結(jié)構(gòu)沒有按標(biāo)準(zhǔn)化的來,導(dǎo)致統(tǒng)計(jì)中包含了這部分代碼,因此實(shí)際上,應(yīng)該是在16000+,因此規(guī)模是Tiny模板引擎的3倍左右,特此糾正。