ゴミ箱.net

汚物は消毒

POIでシートの並べ替え→削除をすると内部データが壊れる件

JavaでExcelのデータを触れる便利なライブラリApache POI。色々なところで使われていてかなりメジャーなライブラリのはず。

そのPOIのけっこうきついバグを、ついこの間見つけてしまったようだ…

それは、ExcelワークブックをPOIで開き、シートの並べ替え→シートの削除をすると、内部データの一貫性が壊れるというもの。
その状態でワークブックを保存しようとすると例外が発生してしまう。内部データの一貫性が崩れてしまったせいで、データのシリアライズに必要な領域サイズの計算結果と、実際にシリアライズしたときの領域サイズがずれてしまうらしい。
それ以外に悪影響があるかどうかは分からないが。

Excelのデータは内部的にいくつかのレコードのリストとして表現されていて、その中にはシートに対応するレコードというのも存在する。POIの内部において、「すべてのレコードのリスト」と「シートに対応するレコードのみのリスト」がそれぞれ別個のリストとして保持されている。
シートを並べ替えるには、クラスorg.apache.poi.hssf.usermodel.HSSFWorkbookのメソッドsetSheetOrder(String, int)を呼ぶが、この内部処理において、シートに対応するレコードのリストのみが並べ替えられる。
その後にシートを削除するメソッドremoveSheetAt(int)を呼ぶと、両方のリストから同一のレコードが削除されるはずだが、レコードの並べ替えがあった場合を考慮していないロジックなので異なるレコードを削除してしまう。
その結果、内部状態に矛盾が生じてしまう。

デバッガでロジックを追っかけたりソースコードをダウンロードして修正したり面倒だった…(#^ω^)
とりあえず修正方法まで含めてBugzillaに報告してみた。どうなるかなw

さて、とりあえずどうやって回避しようか。
最も手っ取り早い方法はバグの発生する機能(この場合はシートの並べ替え+削除)を使わないことだが、この機能が別のライブラリから呼ばれている処理だからどうしようもない。
仕方がないからバグのある部分だけソースコードを書き直して対応する。
バグのあるクラスのソースコードをリポジトリからダウンロードして、Eclipseなりなんなりのプロジェクトのソースコードのフォルダに追加する。(パッケージは変えないこと)

そして、ソースコードの以下の部分を、

     public void removeSheet(int sheetIndex) {
if (boundsheets.size() > sheetIndex) {
  records.remove(records.getBspos() - (boundsheets.size() - 1) + sheetIndex);
  boundsheets.remove(sheetIndex);
fixTabIdRecord();
}

このように修正する。
     public void removeSheet(int sheetIndex) {
if (boundsheets.size() > sheetIndex) {
 BoundSheetRecord removedRecord = boundsheets.remove(sheetIndex);

  // keep consistency between "boundsheets" and "records"
  records.remove(removedRecord);
fixTabIdRecord();
}


そうすれば、普通はソースコードからビルドしたクラスファイルのほうがjarファイルより優先してロードされるのでパッチが当たるというわけだ。
スポンサーサイト

PageTop

コメント


管理者にだけ表示を許可する