前回、Apache POI (のスプレッドシート)を構築するビルダーを構築しました。 そこではノードを構築するビルダーはそれなりのコーディングをしましたが、(JavaBean としての)プロパティは属性として指定すると自動的に注入されるようにしました。
で、プロパティの値として、数値や文字列を指定する場合は特に問題はないのですが、そうではなくて値として列挙型や定数*1などを指定する場合は、それらが定義されているクラスやインターフェースを import する必要があります。 IDE を使って開発する場合は大した手間でもありませんが、そうでない場合はクラスなどの完全修飾名なんて覚えてないのが普通なので*2、こういった import 文を書かなくてもいいようにしましょう。
では具体的に・・・
もうすこし具体的に見てみましょう。 前回作成したビルダーではセルのタイプを指定するには以下のようにする必要がありました:
import org.waman.tools.poi.PoiBuilder import org.apache.poi.ss.usermodel.Cell def workbook = new PoiBuilder().workbook{ row(0){ cell(0, cellType:Cell.CELL_TYPE_STRING) cell(1, cellType:Cell.CELL_TYPE_NUMERIC) cell(2, cellType:Cell.CELL_TYPE_BLANK) cell(3, cellType:Cell.CELL_TYPE_ERROR) } } new File('sample.xls').withOutputStream { workbook.write(it) }
セルのタイプが何かってのはあまり関係なくて、属性の値に指定するときに Cell インターフェースを参照する必要があるのがここでの問題です。 まぁ言ってみれば、Cell インターフェースの import がちょっと面倒だね!ってことです。 ついでに、Cell インターフェースに定義されている定数を使用する際の、'Cell.' ってのも書かなくていいようにします。 つまり、ビルド・スクリプトをこんな感じに書けるようにします:
import org.waman.tools.poi.PoiBuilder // Cell を import する必要なし def workbook = new PoiBuilder().workbook{ row(0){ // CELL_TYPE_XXXX を直接書ける cell(0, cellType:CELL_TYPE_STRING) cell(1, cellType:CELL_TYPE_NUMERIC) cell(2, cellType:CELL_TYPE_BLANK) cell(3, cellType:CELL_TYPE_ERROR) } } new File('sample.xls').withOutputStream { workbook.write(it) }
PoiBuilder の import は仕方ないですね*3。 また、CELL_TYPE_XXXX にどんなものがあるかは、結局 API ドキュメントを見るなり IDE の機能を使うなりしないといけません。
解決策は・・・
どのようにこれを解決するかというと、ビルダー(PoiBuilder) に対して 'CELL_TYPE_XXXX' というプロパティにアクセスすると、対応する Cell のフィールド Cell.CELL_TYPE_XXXX が返されるようにします。 実装の詳細はどうでもいいですが、これはビルダーの getProperties() メソッドをオーバーライドすることで実現できます:
import java.lang.reflect.Field import java.lang.reflect.Modifier import org.apache.poi.ss.usermodel.Cell class PoiBuilder extends FactoryBuilderSupport{ private static final Map<String, Object> CONSTANTS static{ def map = [:] Cell.declaredFields.each{ Field f -> if(isPublicStaticFinal(f.modifiers)){ map.put(f.name, f.get(null)) } } CONSTANTS = map.asImmutable() } /** public static final なフィールドを定数と見なしています。 */ private static boolean isPublicStaticFinal(int mod){ return Modifier.isPublic(mod) && Modifier.isStatic(mod) && Modifier.isFinal(mod) } @Override Object getProperty(String name){ if(CONSTANTS.containsKey(name)){ // 定数が参照されれば対応するフィールドを返します。 return CONSTANTS.get(name) }else{ return super.getProperty(name) } } ... }
Cell 以外の型に定義されている定数も、よく使いそうなものは同じように登録しておくといいでしょう。 あまりあれこれ登録すると、クラスの初期化にコストがかかりますが。
- 作者: 関谷和愛,上原潤二,須江信洋,中野靖治
- 出版社/メーカー: 技術評論社
- 発売日: 2011/07/06
- メディア: 単行本(ソフトカバー)
- 購入: 6人 クリック: 392回
- この商品を含むブログ (152件) を見る
Groovy for Domain-specific Languages
- 作者: Fergal Dearle
- 出版社/メーカー: Packt Publishing
- 発売日: 2010/05/30
- メディア: ペーパーバック
- この商品を含むブログ (9件) を見る