groovy.util.NodeBuilder の練習のため、dom4j の XML ドキュメントオブジェクトを構築するビルダーを作ってみます(一覧)。 簡単のため、以下の条件を課すことにします:
サンプル・コード
まずはサンプルコード。 ビルダーのインスタンスを生成する以外は、通常のビルダーと同じ方法で XML 文書を構築します。
def builder = new SimpleDom4jBuilder() def doc = builder.table(customer:'c', invoice:'i'){ join(c:'invoice_id', i:'id') and{ greater('i.total':1000) like('c:name':'%Bill%', 'child') } } assert doc instanceof org.dom4j.Element def writer = new XMLWriter( new OutputStreamWriter(System.out), OutputFormat.createPrettyPrint()) writer.write(doc) writer.flush()
これを実行すると、以下の XML ドキュメント(要素)が表示されます。
<table customer="c" invoice="i"> <join c="invoice_id" i="id"/> <and> <greater i.total="1000"/> <like c:name="%Bill%">child</like> </and> </table>
Builder クラスの実装
実装するクラスの名前は SimpleDom4jBuilder とします(名前空間をサポートしないという意味で Simple)。 このビルダークラスは BuilderSupport クラスを継承して作成します。 実装する必要のあるメソッドは以下の5つです:
- protected void setParent(parent, child)
- protected createNode(name)
- protected createNode(name, text)
- protected createNode(name, Map atts)
- protected createNode(name, Map atts, Otext)
クラス定義、フィールド、コンストラクタ
まずはクラス定義、フィールド、コンストラクタを。 フィールドとしては org.dom4j.DocumentFactory オブジェクトを保持するようにします。
import org.dom4j.DocumentFactory class SimpleDom4jBuilder extends BuilderSupport{ /** 読み取り可能 */ final DocumentFactory factory /** デフォルトコンストラクタ */ SimpleDom4jBuilder(){ this(new DocumentFactory()); } /** DocumentFactory を受け取るコンストラクタ */ SimpleDom4jBuilder(DocumentFactory factory){ this.factory = factory } }
親を設定する
次は setParent() メソッドの実装。 Element#add() メソッドで要素を付加します。
class SimpleDom4jBuilder extends BuilderSupport{ // ... @Override protected void setParent(parent, child) { parent.add(child) } }
createNode() メソッドを委譲する
全ての createNode() をそのまま実装するのは面倒なので、一番コアになる createNode(Object, Map, Object) メソッドを(後で)キチンと実装することにして、他の createNode() メソッドは適当なパラメータでこのメソッドを呼び出すようにします。
class SimpleDom4jBuilder extends BuilderSupport{ // ... /** {@link SimpleDom4jBuilder#createNode(Object, Map, Object) } へ処理を委譲 */ @Override protected createNode(name) { return createNode(name, [:], null) } /** {@link SimpleDom4jBuilder#createNode(Object, Map, Object) } へ処理を委譲 */ @Override protected createNode(name, text) { return createNode(name, [:], text) } /** {@link SimpleDom4jBuilder#createNode(Object, Map, Object) } へ処理を委譲 */ @Override protected createNode(name, Map atts) { return createNode(name, atts, null); } }
新しい要素を生成する
最後はコアになる createNode(name, Map atts, text) メソッドの実装です。
import org.dom4j.Element class SimpleDom4jBuilder extends BuilderSupport{ // ... @Override protected createNode(name, Map atts, text) { Element e = this.factory.createElement(name.toString()); atts.each{ attName, value -> e.addAttribute(attName.toString(), value.toString()) } if(text != null){ e.addText(text.toString()); } return e; }
- 属性に対応する Map が空の場合も問題なく動作する
- 子要素に対応する text が null かどうかを判定しなければならない
- Map の値、text は String オブジェクトとは限らないので、toString() メソッドを呼び出す必要がある(上の実装では、念のため要素の名前、属性のキーに対しても toString() メソッドを呼び出しています。)
制限はいくつか課しましたが、意外と簡単に Builder のサブクラスを作成できた気がしますが、どうでしょう?
- 作者: Dierk Konig,Andrew Glover,Paul King,Guillaume Laforge,Jon Skeet,杉浦孝,櫻井正樹,須江信洋,関谷和愛,佐野徹郎,寺沢尚史
- 出版社/メーカー: 毎日コミュニケーションズ
- 発売日: 2008/09/27
- メディア: 単行本(ソフトカバー)
- 購入: 5人 クリック: 146回
- この商品を含むブログ (121件) を見る