Javaのテンプレートエンジン
Javaのテンプレートエンジンについて調べてみた.ざっと検索してよく引っかかってきたのは二つ.
VelocityはApacheプロジェクトの一環で,Velocity Engineを中核として周辺にいくつかサブプロジェクトがある模様.現在version 1.5.FreeMarkerは独立したプロジェクトで,version 2.4 Preview 1が最新.どちらもアクティブに活動している模様だし,ぱっと見るとほとんど機能に差がないので,どちらを使ったらいいのか分からない.どちらもXMLを読み込んで別のXMLを出力するXSLT的な使い方もできるようだ.
FreeMarkerのページには,Migrating from Velocityというページがあり,Velocity engineのテンプレートファイルを自動的にFreeMarkerのテンプレートに変換するツールが公開されている.ただし,対応しているVelocityは1.2で,現行の1.5にどの程度対応しているのかは分からない.
さらにFreeMarker vs. Velocityという機能比較のページまである.FreeMarkerによれば,Velocityのほうがライトウェイトでシンプルなので一見良さそうにみえるかもしれないけど,シンプルな結果,さまざまなワークアラウンドを使うことになってしまい,生産性が低下している,とのこと.本当だろうか?
FreeMarkerを使ってみる.
とりあえず,後発でがんばっていそうな感じのするFreeMarkerを使ってみた.
インストール
上記ページからダウンロード,展開,antで簡単にビルドできた.ダウンロードしてきたパッケージは小さいのだが,ビルド時に必要なJarファイルを取りにいっているようだ.ビルド後のlibディレクトリはこんな感じ.
> ls lib README.txt freemarker.jar jsp-api-1.2.jar log4j.jar struts.jar ant.jar javacc.jar jsp-api-2.0.jar logkit.jar xalan.jar dom4j.jar jaxen.jar jsp-api-2.1.jar rt122.jar emma.jar jdom.jar junit.jar saxpath.jar emma_ant.jar js.jar jython.jar servlet.jar
すごいことになっているが,実行に必要なのはfreemarker.jarだけ.
ざっくりとした使い方
どんなテンプレートエンジンでも同じだけど,使い方はこんな感じ.
- オブジェクトツリーを作る.
- テンプレートを読み込む.
- テンプレートにオブジェクトツリーを与えてレンダリング
オブジェクトツリーとしては,Map, List,Javaの配列,JavaBeansが使える.
テンプレート例
例としてタイトルと日付と中身を持つメモ帳をレンダリングすることを考えてみた.テンプレートはこんな感じ.
<html><head> <title> ${pageTitle} </title> </head> <body><H1> ${pageTitle} </H1> <#list entries as entry > <div> <span> ${entry.title} </span> <span>${entry.date?datetime}</span> <div> ${entry.content} </div> </div> </#list> </body></html>
複数のメモをループでレンダリングする部分が
<#list entries as entry >
となっているのが,ちょっとわかりにくい.普通にforeachで
<#foreach entry in entries >
とかにしてくれればいいのに.
${entry.date?datetime}
と「?datetime」がついているのは,Dateオブジェクトに対して,日付と時間の両方を出力するようにというおまじない.
オブジェクトツリーの構成
まず,メモのクラスを定義する.
public class Entry { String title; Calendar date; String content; public String getTitle() { return title; } public Date getDate() { return date.getTime(); } public String getContent() { return content; } }
JavaBeansとしてアクセスするために,get メソッドが定義してある.putメソッド群は不要.
で,このメモエントリのリストを作る.
List<Entry> entries = new ArrayList<Entry>(); entries.add(new Entry("title 1", new GregorianCalendar(), "content 1")); entries.add(new Entry("title 2", new GregorianCalendar(), "content 2")); entries.add(new Entry("title 3", new GregorianCalendar(), "content 3"));
ルートとなるオブジェクトをマップとして作り,そこにこのリストを登録.ついでに,ページタイトルも.
Map<String, Object> root = new HashMap<String, Object>(); root.put("entries", entries); root.put("pageTitle", "samplePage");
テンプレートを読みこんでレンダリング
テンプレートを読み込むにはちょっとおまじないが要る.まずコンフィギュレーションオブジェクトを作って,適当な設定をし,そのコンフィギュレーションからテンプレートを作る形になる.テンプレートができてしまえばあとは,オブジェクトツリーを食わせるだけ.
Configuration cfg = new Configuration(); cfg.setDirectoryForTemplateLoading(new File(".")); cfg.setObjectWrapper(new DefaultObjectWrapper()); Template temp = cfg.getTemplate("template.ftl"); temp.process(root, new OutputStreamWriter(System.out));