Java 模板引擎:webit-script

jopen 11年前發布 | 17K 次閱讀 模板引擎 webit-script

Java 模板引擎,基于java 5 開發,不依賴其他第三方庫,弱類型,語法類似于Javascript。

How to use(如何使用)

Maven

<repositories>
    <repository>
        <id>webit-script</id>
        <name>Webit script</name>
        <url>http://zqq90.github.io/maven/</url>
    </repository>
</repositories>
<dependencies>
    ...
    <dependency>
        <groupId>com.github.zqq90.webit-script</groupId>
        <artifactId>webit-script</artifactId>
        <version>1.2.1</version>
    </dependency>
    ...
</dependencies>

Or Add jars

  • webit-script-1.2.1.jar

Code in Java like this

// !! engine 并不會被緩存, 請根據需要自行實現 Engine的單例模式
Engine engine = Engine.createEngine("/webit-script-config.props", extraSettingsMap);
...
// template 已緩存, 線程安全, 并自動檢測模板源是否被更新
// 當然您也可以緩存 Template 實例,模板更新時更新實例內部AST, 其實例不會變化
Template template = engine.getTemplate("/your/template/path/filename.ext");
...
template.merge(parametersMap, outputStream); 
//template.merge(parametersMap, writer);

Config(配置)

  • 配置文件格式: Use Jodd-props, see:Jodd Props doc Tips: Java-Properties also works
  • 多文件支持 "/webit-script-config1.props,/webit-script-config2.props"
  • 可選額外參數: extraSettingsMap 類型為Map, 支持props 宏
  • 默認配置: webit-script-default.props

Grammar(語法)

Hello word

Hello Webit Script!
<%
var books;
{
    for(book : books){
%>
${for.iter.index}.《${book.name}》 ¥${book.price}
<%
    }
}
{
    //this is a function
    var func = function(a, b){
        return a + b + arguments[3];
    };
    echo func("a", "b", "c");
    echo '\n';
}
{
    var map = {
        1: 1,
        "key2": "value2",
        3: 2 + 1
    };
    map[5] = 2 + 3;
    for(key, value : map){
        echo key + ":" +value + "\n";
    }
}
%>

更多實例可見:測試模板

結構

  • 腳本必須都放在<% %> 內. <% .code inside.. %> plain text outside
  • 替換占位符${} 只能允許帶返回值的單個表達式,只能在腳本塊<% %> 以外使用.${ .. expression .. }
  • 轉義字符 \\ to \ , \<% to <%, \${ to ${
  • Node: 只有緊挨 <% ${\ 才需要轉義 ,
  • 轉義竅門: 偶數個 \ 會 打印 一半數量的\ 并進入代碼段, 奇數 會 打印 (count-1)/2 個 \ 然后打印被轉移的符號。

注釋

  • 單行注釋 //
  • 塊注釋 /* */

關鍵字

var  super  this
if  else
switch  case  default
for  do  while  break  continue 
function  return
import  include  echo
native  new  @import

保留的關鍵字

static  instanceof  class  const  final
throw  try  catch  finally

操作符

與Java 保持一致,順序按優先級從高到低

[] . () @
=>
!  ~  ++  --  – (取負)
*  /  %
+  -
<<  >>  >>>
<  <=  >  >=
^
|
&&
||
?:
..
=  +=  -=  *=  /=  %=  ^=  <<=  >>=  >>>=

語句

  • 結尾分號不能省略

作用域(代碼段) { }

  • 作用域引用上層變量
  • 本層作用域變量不會影響上層
  • 同一作用域不能重復聲明變量
  • 模板傳入的變量僅在該作用域查找同名變量并賦值 1. 調用模板傳入的變量; 2.import 返回的變量

變量

變量聲明 var

var a;
var a, b, c=0, d="d";

變量名規則

  • 先聲明 后使用,所有變量 必須全部聲明
  • 可開啟 弱聲明模式,所有變量不需要 事先聲明,解析時自動聲明
  • 對大小寫敏感
  • 不能使用關鍵字
  • 僅能包含 0-9a-zA-Z_$
  • 特殊變量名: ++ super. 用于 取得指定上層且僅該層作用域的變量, 可嵌套super.super.a ++ this. 用于 取得本層且僅本層作用域的變量, 可嵌套this.super.a ++ for.iter 用于 最近一層for循環的 迭代狀態對象, 可使用superthis 限定作用域super.for.iter

數據結構

擁有動態類

var x                             //  null
var x2 = 6;                    //  數字
var x3 = "Bill";               //  字符串
var x4 = 'a';                   //  字符
var x5 = [1, "string"];     //  數組
var x6 = {};                    // Map

字符串

  • 轉義,\\ \" \' \n \r \t \f \b
  • 允許換行,行后轉義字符 可屏蔽該換行符
var string = "第一行  \
這還是在第一行
這是第二行\n第三行\n
這是第五行,第四行是空行";

數字

var x1=34;  //Integer
var x2=34L;  //Long
var x3=34.00; //Double
var x4=34.00D;  //Double
var x5=34.00F;  //Float
var x6 = 0b10101;  //二進制
var x7 = 0123; //八進制
var x8 = 0x1A; //十六進制

布爾

var x = true;
var y = false;

數組

帶初始值的數組

var array = [1, "a string", book];
var item;
item = array[0];
item = array[1];
item = array[2];
array[0] = "a new value";

Native 方法聲明定長數組,

// 引入生成數組的 native 方法
var new_int_array = native int [];
var new_Object_array = native Object [];
var new_DateTime_array = native java.util.DateTime [];

//得到定長數組
var int_array = new_int_array(5); //長度為5 的int數組
var objects = new_Object_array(5);//長度為5 的Object數組

var a;
a = objects[4];
objects[4]=4; // 自動 裝箱為Integer 然后放入數組
var len = objects.length; //數組長度
len = objects.size; //數組長度

//不定長數組 可使用Java提供的List實現
var new_list = native new java.util.ArrayList();
var list_add = native java.util.List.add(Object);

var list = new_list();
list@list_add(0); 
list@list_add(1);

var a = list[0];
list[0] = "zero";
list[1] = "a string";

Map

var map = {};
var map2 = {1:1,"2","2"};
map["key"] = "a string value";

var value = map[1];
value = map["2"];
value = map["key"];

Java對象

聲明

var new_list = native new java.util.ArrayList();
var list = new_list();
var list2 = new_list();

訪問屬性

var book;
var name = book.name; // book.getName();
book.name = "new name"; //book.setName("new name"); 

訪問方法

訪問方法必須事先native導入成本地函數

var list_add = native java.util.List.add(Object);
list@list_add(0);
list_add(list, 1);

訪問靜態方法

var now = native java.lang.System.currentTimeMillis();
echo now();

函數

聲明

  • 格式同java
  • 可變參數,
  • 可通過 arguments 獲得所有傳入的參數, java類型為 Object[]
  • 可訪問父層作用域
  • 函數內部可嵌套函數
var outSideVar;
var a;
var myFunc = function(arg1, arg2){
    var arg3 = arguments[3]; // 如果沒有將拋出異常,最好通過 arguments.size確認
    outSideVar = "a new "; //可訪問
    var a = 0; //內部變量
    super.a ++ ; //訪問上層變量
    var func = function(){ ... }; //內部嵌套函數
}; //不要忘了分號!!!

導入Java內的 方法

  • 僅可導入公共類的公共方法, 包括靜態方法 和 成員方法
  • 可使用@import 導入類名 或者包名 用法同Java里的 import, 以簡化類名輸入
  • @import java.util.*; v1.2.0+ 不再支持導入包
@import  java.lang.System; //實際上默認已經導入  java.lang.* 只是演示使用方法
@import  java.util.List;
@import  java.util.ArrayList;
var now = native java.lang.System.currentTimeMillis();
var list_add = native List.add(Object);
var new_list = native new ArrayList(); // 導入 構造函數
var new_list2 = native new ArrayList(int); // 導入 構造函數

調用

  • 可變參數個數, 多余函數同樣會被傳入, 多余函數是否被使用 取決于函數本身
  • 缺少的參數 自動 null 填充, 為了良好的設計 不建議使用缺少函數自動填充
  • 可使用@ 將第一個參數 外置
func(arg1, arg2);
//等同于
arg1@func(arg2);
list_add(list, item);
//等同于
list@list_add(item);

重定向輸出符 =>

  • 作用: 將指定 范圍 產生的輸出流 重定向到 指定變量
  • 意義: 可以延后輸出
  • 使用對象: 1. 代碼段; 2. 函數調用
  • 數據格式: 使用OutputStream 時, 為 byte[] ; 使用 Writer 時, 為String.
    var out;
    var book;
    //代碼段 輸出重定向
    {
    echo "a String";
    >${book.name} <
    } => out; //不要忘了分號!!!
    // "a String" 以及 book.name 都將輸出到 out

    var out;
    // 函數 輸出重定向
    func() => out;
    //由于 `=>` 具有較高的優先級,也可以這么些
    var a = arg1@func() => out +1; 
    //此時 a為 func()+1 , func() 本次執行的輸出內容賦值給out

import & include

  • 區別: import 將把調用模板的 上層變量 推入調用層的當前已存在變量
  • 共同點: 都會在調用位置產生 輸出
  • 將使用默認的Loader 加載模板,可使用相對路徑或絕對路徑
  • 可跟隨 一個 map 格式的傳參
  • 模板名可以動態生成
  • import 可支持指定需要導出的變量, 否則只導出本層作用域內的同名變量
//相對路徑
include "./book-head.wtl";
//等同于 
include "book-head.wtl";
//絕對路徑
include "/copyright.wtl";
//動態模板名
var style = "";
import "book-list-"+ style  +".wtl";
//可傳入參數 函數同樣也可以作為變量傳輸
var func = function(){}; 
var book;
import "book-head.wtl"  {"book": book, "func":func};
//傳入Map 變量作為參數
var map =  {"book": book, "func":func};
map["one"] = 1; 
import "book-head.wtl"  {map};
//導出指定變量
var a;
var b;
//導出 : a 到a ,c 到 b
import "book-head.wtl"  {"param1":1}  a,b=c;

關于條件判斷的三種情況

  • 如果是boolean(Boolean)值 會原封返回
  • 如果 ==null 返回 false
  • **如果 是空集合 或者 空數組 (.size==0) 返回 false **
  • 否則 返回 true

三元條件運算符 & 其簡寫

  • 操作符按 自右向左 結合 [不是執行順序], 詳解看下面例子
  • 簡寫時 ?: 之間不能有空白
var a1 = isTrue ? "Yes" : "No";
//簡寫
var a2 = value ?: defaultValue; //取默認值
//自右向左 結合
var x =  expr1 ?  expr3 :  expr2 ? expr4 : expr5;
//這個等同于
var x =  expr1 ?  expr3 :  (expr2 ? expr4 : expr5);
// 如果 是 自左向右 就會變成這樣
var x =  (expr1 ?  expr3 :  expr2) ? expr4 : expr5;
//簡寫 就按 從左向右 “執行” 
var a4 = list1 ?: list2 ?: list3;

判斷語句

判斷表達式 ?:

判斷控制語句 if - else if - else

  • 不能省略 { }
if( ... ){
    ...;
}else if(...){
    ...;
}else{
    ...;
}

循環控制語句

  • 支持 數組, java.util.Collection, java.util.Iterator, java.util.Enumeration, CharSequence, java.util.Map, 整型遞增/遞減
  • 當集合為null 或者為空時將不會執行循環
  • 支持 else , 可選, 當不符合執行循環體的條件時執行else體.

for-in

//集合 數組 等
for(item : list){
    echo item;
    //echo for.iter.index; // .isFirst .hasNext .isOdd .isEven
} else{
    echo "list is empty";
}
//遞增 
for(i: 3..6){
    echo i;
}
//遞減
for(i: 6..3){
    echo i;
    //支持 for.iter.*
}

for-in Map version

for(key, value : map){
    echo key + " = " value;
    echo "\n";
    //同樣支持 for.iter.*
}

while do-while

  • 不支持 for.iter 特殊變量
//
var iter;
... ;
while(iter.hasNext){
    var item = iter.next;
    ....;
}
//
do{
    ....;
}while( ... );

Switch-Case

  • 支持普通 Object, 包括 String
  • 使用 Object.equls() 判斷是否相等
  • 需要 break, 否則無條件繼續執行下一個標簽的句柄
  • 每個case 命名空間獨立
switch(a){
    case 1:
        ....;
        break;
    case "c": //String
        ....;
        break;
    case 'c': //Char
        ....;
        break;
    default:
        ....;
}

break continue

  • 支持 label, 直接操作該循環體 或 switch
//break continue
outter: for(i: 6..3){
    echo i;
    //支持 for.iter.*
    inner: for(item : list){
        if( ..... ){
           break outter;
        }
        .....;
        break; // break inner;
    }
    //
    switch(a){
        ...;
        case 'x':
           break outter;
        ...;
    }
}

正在完善。。。

其他

Performance(性能)

  • 缺省開啟ASM構建Native 調用減少反射, 不同于將整個模板編譯成Java字節碼,該方法不會造成無限制的perm溢出;
  • 解析之后的Template AST會放入緩存, 檢測到模板源文件改變時將重新加載資源并解析;
  • 性能測試結果比較理想, 待比較權威的模版測試程序;
  • 使用OutputStream 輸出時, 選擇 SimpleTextStatmentFactory 將會預先將純文本根據缺省編碼編碼成字節流.
  • boilit/ebm 測試結果 by:boilit/ebm or see
Engine                                Time            Size
BSL-2.0.0                              559        68118050
webit-script-1.1.4                     590        68318250
HTTL-1.0.11                            958        68118050
BeeTL-1.25.01                          958        68138070
Rythm-1.0.0-b10-SNAPSHOT              1624        48728680
Velocity-1.7                          1834        75046912
FreeMarker-2.3.19                     2369        68157440
JdkStringBuffer-1.7.0_40               606        67395584
JdkStringBuilder-1.7.0_40              735        67395584

SPI

  • TextStatmentFactory 對模板純文本的存貯以及輸出形式進行管理
  • Filter 輸出過濾
  • CoderFactory 編碼/解碼
  • Loader 模板資源加載(原ResourceLoader)
  • Logger 日志
  • GetResolver, SetResolver, OutResolver Bean屬性解釋器
  • NativeSecurityManager Native調用安全管理器

項目主頁:http://www.baiduhome.net/lib/view/home/1385607413718

     本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
     轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
     本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!