-DataSetとExcel

ひがさんのところSeasar2の次バージョン追加機能としてExcelを操作できるDataSetの予告がありました。Java-Excelの処理を行うPOIのTipsもありました。今の仕事でちょうど既存の顧客データベースに対してシステム投資効果を計るためのROIを取る作業をしていて、その技術側面はDB + Seasar2 + POIなんですよ。ひがさんも似たようなことをちょうどやっていたのでしょうか。
DBからSesar2コンポーネントで結果セットを取ってくるところ、以下のようなつくりでやってます。

public interface ActionArg {
  public String getPrepareArgument();
}

public interface Action {
  public void addArg(ActionArg arg);
  public void execute() throws Exception;
}

public interface Outputter {
  public void write(Action action) throws Exception;
  public void flash() throws Exception;
}

public interface ActionManager {
  public void addAction(Action action);
  public void executeActions() throws Exception;
}

<component name="roi1sql" class="analize.SQLFileArg">
  <arg>'roi1sql.sql'</arg>
  <initMethod name="setDateParam">
    <arg>0</arg>
    <arg>'2003-11-30'</arg>
  </initMethod>
  <initMethod name="setDateParam">
    <arg>1</arg>
    <arg>'2004-03-16'</arg>
  </initMethod>
</component>

<component name="roi1" class="analize.SQLAction">
  <initMethod name="addArg">
    <arg>roi1sql</arg>
  </initMethod>
  <aspect pointcut="execute">backgroundRunningAdvice</aspect>
</component>

<component name="excelOutputter" class="analize.ExcelOutputter">
  <arg>'ANALIZED_20040322.xls'</arg>
</component>

<component class="analize.DBCPActionManager">
  <arg>excelOutputter</arg>
  <arg>connectionPool</arg>
  <initMethod name="addAction">roi1<property>
  <initMethod name="addAction">roi2<property>
  <initMethod name="addAction">roi3<property>
  <destroyMethod name="flashOutputter"/>
</component>

ActionManager manager = 
  (ActionManager)container.getComponent(ActionManager.class);
manager.executeActions();

コンテナの中で名前がユニークになれば同じ型のクラスでも登録できる例になります。<initMethod name="addAction">roi1<property>のようなやり方はTipsですね。その後、Excelに出力します。この場合、ActionとOutputterの依存性が内部でペアになりがちなうまくないつくりですが、次のSeasar2のDataSetみたいな仕組みで一回受ければ柔軟なつくりになるでしょう。そのうち、ExcelOutputterに続いてHTMLOutputterの需要が見えていて、丸ごとTapestryの後ろに入ることになりそうですから、そのうちリファクタリングしましょう。Seasar2のテスト兼ROIレポート作成の仕事でコードは納品物でないから、手を抜くところ抜いてます。
ExcelOutputterでは、結果セットをExcelのシートに単純に書き出してます。後で、その書き出しをExcelのピボットテーブルやグラフを使ってレポート体裁を整えています。Excelのピボットテーブルやグラフは、Javaでやるのはバカバカしいぐらい便利で高機能ですから、これはこれで一つのソリューションだと思います。惜しむらくは、POIにピボットテーブルを作る機能がまだ実装されていない(既知の制限事項)ので、そこが手動になってしまうということです。グラフはPOIで未確認ながらできるみたいですが、これも手でやってます。
POIのTipsです。

public static HSSFSheet createSheet(HSSFWorkbook wb, String name) {
  HSSFSheet ret = null;
  ret = wb.getSheet(name);
  if(ret != null) {
    wb.removeSheetAt(wb.getSheetIndex(name));
    ret = null;
  }
  String dummy = (new Date()).toString();
  ret = wb.createSheet(dummy);
  int i = wb.getSheetIndex(dummy);
  wb.setSheetName(i, name, HSSFWorkbook.ENCODING_UTF_16);
  return ret;
}

public static HSSFCell createCell(HSSFRow row, int col) {
  HSSFCell ret = row.createCell((short)col);
  ret.setEncoding(HSSFWorkbook.ENCODING_UTF_16);
  return ret;
}

例外処理とか省略してますがPOIで日本語に対応するTipsです。HSSFWorkbook.ENCODING_UTF_16という定数をエンコーディングに指定しないと、マルチバイト文字が化けます。シート名に日本語をセットするcreateSheet(...)のほうは、POIのExcelシートを作るAPIでエンコーディングを指定できるものが見つからなかったので、一度日付から作ったダミー名で作ってから、シート名の変更をしてます。これはおそらくもっとうまいやり方があるはずだと思います。
ひがさんのBLOGフォローによると、createSheet(/*引数なし*/)メソッドでシートが最後尾に作られるの保証されているみたいですね。それなら、まずシートを作って、後に最後尾シートにsetSheetName(...)でOK。でももっとなんか直接的なのがあってもいいかなと。理想はcreateSheet(String name, short encoding)。