プログラマ38の日記

主にプログラムメモです。

Java: 「StringTemplate」ライブラリを使ってみました

Javaで、「StringTemplate」のライブラリを使ってみました。

 

はじめに

Javaのテンプレートエンジンは、Apache Velocityを代表として色々なものがあると思います。VelocityはVTLが使いやすいのですが様々な理由でVelocity以外が使いたいときもあると思います。例えば、Apache Velocityで指定されているjarの別のバージョンを既に使っていて、検証や調査が必要となる場合などが考えられます。

 

ただ、テンプレートの文字列とバインドする変数があればいいという人には「StringTemplate」は便利だと思います。長いSQLの一部分だけ変数を差し込みたいときなど重宝します。

 

今回、「StringTemplate」のv3を選択しました。

 

テンプレートを用意

こんなテンプレートを作りました。基本バインド変数は$で囲む書き方です。

文字として$を使う場合は「\$」とします。シンプルで良いと思いました。もちろんIFなどの関数も用意されています。

$callcnt$ 回目の出力

変数の差込10個

01 $value01$
02 $value02$
03 $value03$
04 $value04$
05 $value05$
06 $value06$
07 $value07$
08 $value08$
09 $value09$
10 $value10$

プロパティの差込10個 (1段階)
01 $obj.value01$
02 $obj.value02$
03 $obj.value03$
04 $obj.value04$
05 $obj.value05$
06 $obj.value06$
07 $obj.value07$
08 $obj.value08$
09 $obj.value09$
10 $obj.value10$

プロパティの差込10個 (2段階)
01 $obj.prop.value01$
02 $obj.prop.value02$
03 $obj.prop.value03$
04 $obj.prop.value04$
05 $obj.prop.value05$
06 $obj.prop.value06$
07 $obj.prop.value07$
08 $obj.prop.value08$
09 $obj.prop.value09$
10 $obj.prop.value10$

ループ(要素10)
$rows:{ row | $row.rownum$ $row.value
}$

Javaプログラムの書き方

    StringTemplate st = new StringTemplate();
    st.setTemplate("テンプレートの文字列");

    st.reset(); //バインド変数をクリア
    st.setAttribute("callcnt",callcnt );
    st.setAttribute("value01",value01 );
    st.setAttribute("value02",value02 );
    st.setAttribute("value03",value03 );
    st.setAttribute("value04",value04 );
    st.setAttribute("value05",value05 );
    st.setAttribute("value06",value06 );
    st.setAttribute("value07",value07 );
    st.setAttribute("value08",value08 );
    st.setAttribute("value09",value09 );
    st.setAttribute("value10",value10 );

    st.setAttribute("obj", c1);
    st.setAttribute("rows", rows);
    
    String ret = st.toString(); //バインド変数を展開して出力

変数をバインドする箇所が長いですが、「StringTemplate」だけの操作は非常に少ないです。

性能について

1度目だけ遅くて、それ以外は早いです。またテンプレートの文字列を変えても早いです。テンプレートからバイトコードに変換していると思いますが凄いです。安心して利用できます。

あくまで参考ですが、こんな性能になりました。

実行回数 ナノ秒
1 8810818
2 2216037
3 1696282
4 1775865
5 1458761
6 1586751
7 1512911
8 1441531
9 1192115
10 1180628

上記の性能を算出した際のテンプレートは上記に記載したテンプレートで、プログラムは次の通りです。



import java.io.File;
import java.io.FileInputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;


import org.antlr.stringtemplate.StringTemplate;


public class STSample {
  

  
  
  public static void main(String... args) throws Exception{
    
    DecimalFormat df = new DecimalFormat("000000");
    
    StringTemplate st = new StringTemplate();
    
    
    
    long templatestart = System.nanoTime();
    st.setTemplate(templateStr());
    long templateend = System.nanoTime();
    System.out.println(templateend - templatestart);

    
    for( int i=1; i<=1000 ; i++) {
      long bindstart = System.nanoTime();    
      new STSample().execute(st, i );
      long bindend = System.nanoTime();
      System.out.println( df.format(i) + "回目:" + (bindend - bindstart));
      
      
    }
    
  }


  public  void execute(StringTemplate st , int callcnt) throws Exception{
    
    
    String rnd = ":" + (int)(Math.random() * 10000);
    
    String value01 = "value01"+rnd;
    String value02 = "value02"+rnd;
    String value03 = "value03"+rnd;
    String value04 = "value04"+rnd;
    String value05 = "value05"+rnd;
    String value06 = "value06"+rnd;
    String value07 = "value07"+rnd;
    String value08 = "value08"+rnd;
    String value09 = "value09"+rnd;
    String value10 = "value10"+rnd;


    Class1 c1 = new Class1();
    c1.value01 = "nest1obj." + value01;
    c1.value02 = "nest1obj." + value02;
    c1.value03 = "nest1obj." + value03;
    c1.value04 = "nest1obj." + value04;
    c1.value05 = "nest1obj." + value05;
    c1.value06 = "nest1obj." + value06;
    c1.value07 = "nest1obj." + value07;
    c1.value08 = "nest1obj." + value08;
    c1.value09 = "nest1obj." + value09;
    c1.value10 = "nest1obj." + value10;


    c1.prop.value01 = "nest21obj." + value01;
    c1.prop.value02 = "nest21obj." + value02;
    c1.prop.value03 = "nest21obj." + value03;
    c1.prop.value04 = "nest21obj." + value04;
    c1.prop.value05 = "nest21obj." + value05;
    c1.prop.value06 = "nest21obj." + value06;
    c1.prop.value07 = "nest21obj." + value07;
    c1.prop.value08 = "nest21obj." + value08;
    c1.prop.value09 = "nest21obj." + value09;
    c1.prop.value10 = "nest21obj." + value10;

    ArrayList<Class2> rows = new ArrayList<>();
    
    for(int i=1; i<=10; i++){
      Class2 c2 = new Class2();
      c2.rownum = i;
      c2.value = "loopvalue" + rnd;
      rows.add(c2);
    }
    
    
    st.reset();
    
    
    st.setAttribute("callcnt",callcnt );
    st.setAttribute("value01",value01 );
    st.setAttribute("value02",value02 );
    st.setAttribute("value03",value03 );
    st.setAttribute("value04",value04 );
    st.setAttribute("value05",value05 );
    st.setAttribute("value06",value06 );
    st.setAttribute("value07",value07 );
    st.setAttribute("value08",value08 );
    st.setAttribute("value09",value09 );
    st.setAttribute("value10",value10 );

    st.setAttribute("obj", c1);
    st.setAttribute("rows", rows);
    
    String ret = st.toString();
    

    



  }
  
  public  class Class1{
    public String value01;
    public String value02;
    public String value03;
    public String value04;
    public String value05;
    public String value06;
    public String value07;
    public String value08;
    public String value09;
    public String value10;
    
    public Nest1 prop = new Nest1();
  }
  
  public  class Nest1{
    public String value01;
    public String value02;
    public String value03;
    public String value04;
    public String value05;
    public String value06;
    public String value07;
    public String value08;
    public String value09;
    public String value10;

  }
  
  
  
  public  class Class2 {
    public int rownum;
    public String value;
  }
  
  public static String templateStr() throws Exception{
  //上記のテンプレートをsample1.txtでshift_jisで保存しておく
    File t = new File("template1.txt");
    
    byte[] bytes = new byte[(int)t.length()];
    
    FileInputStream fis = new FileInputStream(t);
    
    fis.read(bytes);
    
    fis.close();
    
    return new String(bytes, "Windows-31J");
  }
}