Groovy のビルダーを使っていてチョット思ったことをメモ。
ビルダーは、XML のようなツリー構造を構築するのに便利なツールです。 例えば以下のような XML 文書は*1
<table customer='c' invoice='i'> <join c='invoice_id' i='id' /> <and> <greater i.total='1000' /> <like c.name='%Bill%' >desription</like> </and> </table>
次のようなビルダーで構築できます:
def builder = new MarkupBuilder() def result = builder.table(customer:'c', invoice:'i'){ join(c:'invoice_id', i:'id') and{ greater('i.total':1000) like('c.name':'%Bill%', 'description') } }
このくらいのサンプルだとあまり有難味が分かりにくいですが、開始・終了タグでタグ名を書く必要がないとか、Groovy の定数、変数、メソッド呼び出しを使えるなど、ビルダーにはいろいろ便利なことがあります。
で、ここから本末を転倒させて、既存の(例えば独自の)ビルダーがあったときに、上記のような XML 文書を入力としてこのビルダーでツリー構造を構築させる方法を考えてみます。
まぁ、普通に考えて、Closure 使えば実装できそうですけど、拙者がイマイチ Closure に慣れてないせいかチョット実装の方向性がよく分からないので、とりあえず力業の実装を。 実装の流れとしては、ビルダーで構築するコードを StringBuilder で頑張って作って、それを GroovyShell で評価してやろう!って感じです。
def xml2builder = new XmlToBuilder(builder:new MarkupBuilder()) def result = xml2builder.build(xml) class XmlToBuilder{ def builder def build(String xmlText){ String expr = buildExpression(xmlText) def binding = new Binding(builder:this.builder) def shell = new GroovyShell(binding) return shell.evaluate(expr) } def buildExpression(String xml){ def node = new XmlParser().parseText(xml) def expr = new StringBuilder('builder.') buildNode(node, expr) } def buildNode(node, expr){ expr << node.name() expr << '(' node.attributes().each{ name, value -> expr << "'$name':'$value'," } if(node.text()){ expr << "'${node.text()}');" }else{ expr << '){' node.children().each{ buildNode(it, expr) } expr << '};' } } }
GroovyShell で評価するコードはこんな感じに出来上がります:
builder.table('customer':'c','invoice':'i',){join('c':'invoice_id','i':'id',){};and(){greater('i.total':'1000',){};like('c.name':'%Bill%','desription');};};
ちょっとコンマ (,) とか セミコロン (;) が多いですが、まぁ読むのは GroovyShell さんなので問題なし。
これでとりあえず動きますが、ちょっと力ずく感が否めないので、Closure 使った実装を引き続き模索(予定)。
- 作者: Dierk Konig,Andrew Glover,Paul King,Guillaume Laforge,Jon Skeet,杉浦孝,櫻井正樹,須江信洋,関谷和愛,佐野徹郎,寺沢尚史
- 出版社/メーカー: 毎日コミュニケーションズ
- 発売日: 2008/09/27
- メディア: 単行本(ソフトカバー)
- 購入: 5人 クリック: 146回
- この商品を含むブログ (121件) を見る
*1:このサンプルコードは『[asin:4839927278:title]』から拝借。 かつチョットだけ変更してます。