FreemarkerをScalaで使う
Scalaの勉強の為に個人的なプロジェクトに使ってみようと思うのだけど,それにはテンプレートエンジンが必要.ということで,Freemarkerを使ってみた.
FreemarkerはJavaのテンプレートエンジンなので,Scalaからそのまま呼び出すことはできるのだけど,JavaのMapとScalaのMapはちょっと違うし,Listの実装も違うのでそのままでは使えない.ScalaでJava Beansっぽく書くのもばからしい.
Freemarkerはレンダリングするオブジェクトツリーの構造を隠蔽するラッパ機構が用意されていて,例えばDomツリーを直接レンダリングできたりする.同じ枠組みを利用して,JythonやGroovyのためのラッパがデフォルトで用意されている.Scalaでもこれらとおなじようなものを用意してやればいい.ラッパ機構は,型に応じたラッピングオブジェクトと,ディスパッチするクラスで構成する.ディスパッチクラスはこんな感じ.要するに,オブジェクトの型に応じて適当なラッピングオブジェクトでラッピングして返してやれば良い.
package freemarker.ext.scala; import freemarker.template.*; public class ScalaObjectWrapper extends freemarker.ext.beans.BeansWrapper{ static ScalaObjectWrapper _instance = new ScalaObjectWrapper(); static ScalaObjectWrapper instance() { return _instance; } public TemplateModel wrap(Object obj) throws TemplateModelException { if (obj instanceof scala.collection.Map) return new ScalaHashModel((scala.collection.Map)obj); if (obj instanceof scala.Some) return wrap(((scala.Some)obj).get()); if (obj instanceof java.util.Date) return new freemarker.template.SimpleDate((java.util.Date)obj, freemarker.template.SimpleDate.UNKNOWN); if (obj instanceof String) return new freemarker.template.SimpleScalar((String)obj); if (obj instanceof Integer) return new freemarker.template.SimpleNumber( ((Integer)obj).intValue()); if (obj instanceof Double) return new freemarker.template.SimpleNumber( ((Double)obj).doubleValue()); if (obj instanceof scala.$colon$colon) return new ScalaCollectionModel( ((scala.$colon$colon)obj).elements()); if (obj.getClass().toString(). split(" ")[1].startsWith("[")) // looks like array return new freemarker.template.SimpleCollection( java.util.Arrays.asList((Object[])obj), this); return new ScalaModel(obj); } }
Jython用のラッパのコードを見ると,高速化のためにラッピングオブジェクトをキャッシュしているので,そのうちやってみよう.