G*Magazine Vol.6

オレオレ・プログラミング GROOVY

G* な皆さん、こんにちは。Groovy を学ぶ際に最初に読むべき書籍といえば「プログラミングGROOVY」ですよね?しかし、書籍で紹介されているのは、Groovy の魅力のごく一部に過ぎません。そこで、このコラムでは「プログラミングGROOVY」を読んで、これから Groovy を活用していこうと考えている人向けに、即戦力となるであろう API を紹介したいと思います。

Groovy のテンプレートエンジン

Java では、Apache の Velocity Engine が代表的なテンプレートエンジンとして知られていますが、Groovy では言語本体でテンプレートエンジンの API を提供しています。テンプレートエンジンとは、ひな形となる定型のテンプレートにデータをバインドして、文字列を出力したり、ファイルの生成を行うための機能です。身近な例を挙げると、Grails の generate 系コマンドで生成されるコントローラやビューなどは、Groovy のテンプレートエンジンを利用して生成しています。

Groovy のテンプレートエンジンは groovy.text パッケージ(groovy-templates-x.x.x.jar)に格納されています。僅か5ファイルだけの小さなパッケージですので、それぞれについて解説します。

※Groovy 2.0 からのモジュール化により、物理的には別モジュールになっています。

Template

テンプレートのインタフェースで、オーバーロードによる2つの make() メソッドが定義されています。このインタフェースの実装クラスの中でバインドしたものを出力するための Writable なクラスを生成します。

TemplateEngine

テンプレートエンジンの抽象クラスで、オーバーロードによる4つの createTemplate() メソッドが定義されています。最終的には抽象メソッドとなっている Reader を引数とする createTemplate() が呼ばれ、具象クラスのテンプレートエンジンが生成されます。

SimpleTemplateEngine

3つのテンプレートエンジンの中で最も汎用的に使えるテンプレートエンジンです。JSP形式(<% %>, <%= %>)とGString形式(${})のプレースホルダを記述できます。例えば、テンプレートファイルを読み込んでクラスファイルを生成するコードは以下のようになります。

  • テンプレート
package $packageName

class $className {
<% fields.each { type, fieldName -> %>
    private $type $fieldName<% } %>
}
  • サンプルコード
import groovy.text.SimpleTemplateEngine

def path = '/Users/bikisuke/sandbox/templateEngine'
def className = 'SampleBean'
def binding = [
        packageName:'org.jggug.bean', 
        className:className, 
        fields:['String':'name', 'int':'value']
    ]
def file = new File("${path}/simple.template")
def engine = new SimpleTemplateEngine()
def template = engine.createTemplate(file).make(binding)
new File("${path}/${className}.groovy").write(template.toString())
  • 実行結果
package org.jggug.bean

class SampleBean {

    private String name
    private int value
}

SimpleTemplateEngine では、デバッグ確認用の verbose フラグがあり、コンストラクタまたは setVerbose() で値を true にすることで、実行時にテンプレートを解析した結果を標準出力することができます。前述のテンプレートの場合は以下のような出力になります。

-- script source --
out.print("""package $packageName

class $className {
"""); fields.each { type, fieldName -> ;
out.print("""
    private $type $fieldName"""); } ;
out.print("""
}
""");

/* Generated by SimpleTemplateEngine */
-- script end --

GStringTemplateEngine

GStringTemplateEngine は SimpleTemplateEngine と同等の機能を持ちながら、クロージャを用いることで、SimpleTemplateEngine よりも拡張性のあるテンプレートエンジンとなっています。クロージャ内では出力を out という変数に委譲するため、スクリプトレットで out 変数を使いながらロジックを記述することができます。一つのスクリプトレット内で値の判定をさせて out に出力すると、SimpleTemplateEngine よりも柔軟で読みやすいテンプレートを作成することができます。

  • テンプレート
$familyName $firstName 様、おめでとうございます。

厳正な抽選の結果、<%= familyName %>様に
<% out << (hasLoan? '7億' : '3億') %>円プレゼントが当選しました。

ご連絡いただければ、すぐにお振込いたしますので、
下記 URL へのアクセスお願いいたします。
http://akutoku.com
  • サンプルコード
import groovy.text.GStringTemplateEngine

def path = '/Users/bikisuke/sandbox/templateEngine'
def binding = [
        familyName:'御金', 
        firstName:'内造',
        hasLoan:true
    ]
def file = new File("${path}/gstring.template")
def engine = new GStringTemplateEngine()
def template = engine.createTemplate(file).make(binding)
println template.toString()
  • 実行結果
御金 内造 様、おめでとうございます。

厳正な抽選の結果、御金様に
7億円プレゼントが当選しました。

ご連絡いただければ、すぐにお振込いたしますので、
下記 URL へのアクセスお願いいたします。
http://akutoku.com

XmlTemplateEngine

 3つ目は名前の通り XML を出力するためのテンプレートエンジンです。テンプレートの解析に XmlParser を使用し、テンプレート自体も XML 書式で記述します。このテンプレートでは <gsp:scriptlet> と <gsp:expression> の2つのタグを使ってデータとのバインドを行います。

  • テンプレート
<?xml version="1.0" encoding="UTF-8"?>
<WizardRings xmlns:gsp='http://groovy.codehaus.org/2005/gsp'>
  <gsp:scriptlet>rings.each {</gsp:scriptlet>
  <TransformationRing style="${it.style}">
    <stone element="${it.stone.element}" >
      <gsp:expression>it.stone.base</gsp:expression>
    </stone>
    <chants><gsp:expression>it.chants</gsp:expression></chants>
  </TransformationRing>
  <gsp:scriptlet>}</gsp:scriptlet>
</WizardRings>
  • サンプルコード
import groovy.text.XmlTemplateEngine

def path = '/Users/bikisuke/sandbox/templateEngine'
def rings = []
rings << [style:'Flame', stone:[element:'Fire', base:'Ruby'], chants:'Hi']
rings << [style:'Water', stone:[element:'Water', base:'Sapphire'], chants:'Sui']
rings << [style:'Hurricane', stone:[element:'Wind', base:'Emerald'], chants:'Fu']
rings << [style:'Land', stone:[element:'Earth', base:'Yellow-Topaz'], chants:'Do and Don']

def file = new File("${path}/xml.template")
def engine = new XmlTemplateEngine()
def template = engine.createTemplate(file).make([rings:rings])
println template.toString()
  • 実行結果
<WizardRings>
  <TransformationRing style='Flame'>
    <stone element='Fire'>
      Ruby
    </stone>
    <chants>
      Hi
    </chants>
  </TransformationRing>
  <TransformationRing style='Water'>
    <stone element='Water'>
      Sapphire
    </stone>
    <chants>
      Sui
    </chants>
  </TransformationRing>
  <TransformationRing style='Hurricane'>
    <stone element='Wind'>
      Emerald
    </stone>
    <chants>
      Fu
    </chants>
  </TransformationRing>
  <TransformationRing style='Land'>
    <stone element='Earth'>
      Yellow-Topaz
    </stone>
    <chants>
      Do and Don
    </chants>
  </TransformationRing>
</WizardRings>

テンプレートエンジンの使いどころ

SimpleTemplateEngine は、オールラウンドで使用することができるので、ソースコード生成のような自動生成ツールを作成する際には非常に役に立つクラスです。筆者はこれまで生成ツールをいくつか作成してきましたが、使い勝手の良さから SimpleTemplateEngine を使うケースが多いです。

GStringTemplateEngine は、データの値に応じて出力時の内容を変更させたいような場合に有効です。ただし、テンプレートでのロジックが実装しやすい反面、肥大したテンプレートになってしまう可能性もあるため、簡単なデータ変換や出力元の切替など効果的に利用すると良いと思います。

XmlTemplateEngine は使いどころは限られますが、XML に特化したテンプレートエンジンですので、XML を出力する際には検討する価値はあると思います。

まとめ

簡単ではありますが、Groovy のテンプレートエンジンをおさらいしてみました。2.x 系になって、魅力的な新機能が増えていますが、まだまだ Groovy にはたくさんの実用的な機能があります。皆さんも API を眺めてみて、オレオレGROOVY を探求してはいかがでしょうか。

G*Magazine Vol.6

Groovy臨機応変(第一回) 〜動中の静…Groovy 2.1.0の新機能その1〜
アプリケーションをGroovyでコントロールする
オレオレ・プログラミング GROOVY
『Yokohama.groovy』について 〜活動報告など〜
Grails Plugin探訪 〜第7回 Remote Methodsプラグイン〜
ぐるーびーたん 第6話
リリース情報 2013.02.19