独自タグとそのパーサを追加出来るようにしました。

と云っても、セル単位のあれこれをやる奴はまで出来なくて、行単位で処理するもの限定です。標準のものだと例えば「#if」や「#foreach」などのブロック構文、それと「#pageBreak」や「#exec」などの単独で処理するものなど。要するにA列のセルに書くものです。

例えば、A列に

#hoge

と書いておくと、その行のA列に「ほげ」と表示されるという、ものの役にも立たないタグを思いついてしまいました。一応、#hogeと書かれているセルの書式も反映するようにしましょう。

まずTemplateElementインタフェースを実装します。

public class Hoge implements TemplateElement{
    private CellWrapper originalCell;
    
    public Hoge(CellWrapper cell){
        originalCell = cell;
    }

    public void merge(FPContext context) throws FPMergeException {
        HSSFCell currentCell = context.getCurrentCell();
        currentCell.setCellStyle(originalCell.getHSSFCell().getCellStyle());
        currentCell.setCellValue(new HSSFRichTextString("ほげ"));
        context.nextRow();
    }
}

コンストラクタで「#hoge」と書かれているセルそのものを受け取ります。それをoriginalCellというメンバ変数に保持しておきます。CellWrapperというのはPOIのクラスであるHSSFCellを、Fisshplateが解析しやすいようにラップしたクラスです。

mergeはTemplateElementインタフェースに定義されているメソッドです。これが実際にデータ埋め込み時に呼ばれます。引数はデータ埋め込み時にグローバルで管理する各変数を保持しているFPContextクラスです。ここから埋め込みデータやら現在位置やらを取得します。

ここでは、現在処理中のセルをHSSFCellとして取得します。そこへ、originalCellからラップしてるHSSFCellを取得して、そこからまたスタイルを取得して現在処理中のセルにコピーしています。そしてそのセルに「ほげ」という文字列をつっこんでます。

これでA列に「ほげ」が入ったので、FPContext#nextRow()を呼び出して、FPContextに「処理を次の行に移してくれ」と命令します。

ブロック構文の場合など他にも色々とルールがあるんですが、取り敢えずブロックの場合は「IteratorBlock」や「WhileBlock」を見ると判るかと思います。

次に、このタグを解析して上のHogeクラスを生成するパーサを作ります。StatementParserインタフェースを実装します。

public class HogeParser implements StatementParser{
    public boolean process(CellWrapper cell, FPParser parser)  throws FPParseException {
        String value =cell.getStringValue();
        if(!"#hoge".equals(value)){
            return false;
        }
        TemplateElement elem = new Hoge(cell);
        parser.addTemplateElement(elem);
        return true;
    }
}

StatementParserインタフェースのprocessメソッドを実装します。この最中に何かエラーが起きた場合は、FPParserExceptionを投げるようにします。

パース時に見るセル(その行のA列)と、大元のパーサであるFPParserが引数で渡って来ます。セルの値を文字列として取得します。StatementParser#processがFPParserから呼ばれた時点でセルの値は必ず文字列である事が保証されていますので大丈夫です。

その値が「#hoge」じゃなかったらパース対象でないのでfalseを戻します。「#hoge」ならば、Hogeクラスを生成して、FPParser#addTemplateElementにつっこんで要素として登録します。

これもブロック構文の場合はまたルールが違ったりしてます。「IteratorBlockParser」あたりが参考になるかと思います。

また今回は単純な文字列比較で判断してますが、正規表現を使ったりはたまたもっといい方法があればどんな判断方法でも構いませんです。大体正規表現でやってるんですが、かっこいい方法あったら是非教えて下さい。

では実際に使ってみます。

InputStream is = getClass().getResourceAsStream("/Template.xls");
template = new FPTemplate();
template.addRowParser(new HogeParser()); //HogeParserの登録
Map map = new HashMap();
//もろもろデータ設定など
HSSFWorkbook wb = template.process(is, map);
is.close();
FileOutputStream fos = new FileOutputStream("out.xls");
wb.write(fos);
fos.close();

try catchなどは省略してます。ここに書いた通りのソースでは動作確認してないんですけど、確かにテストは通ってますのできっと大丈夫です。

0.1.4-SNAPSHOTでMavenリポジトリにデプロイ済みですので、ご興味ある方はお試し下さい。正式リリースはもうちょっとお待ち下さい。他要望の食べ残しがまだありますゆえ。