ColdFusion 実験室で公開している情報は、ColdFusionの標準の機能では無いため動作の保証外(未サポート)となります。下記の事項も含めてそれらを予めご認識頂いた上で、参考にされるかどうかをご判断下さい。
ColdFusionには、Excelファイルへのさまざまな処理に対応するためにcfspreadsheetタグや多数のSpreadSheet系の関数が用意されていますが、CFタグや関数に汎用化されることによって、どうしても制限等が発生する事もあります。今回はそのような場面に遭遇した時に、内部で使用する POIライブラリを直接呼び出した例についてをご紹介します。
呼び出すメソッドなどは、Webで検索して引っかかったページを参考に、そのコードをColdFusionベースに反映しています。実験室3の「ExcelのセルのフォーマットをPOIライブラリを直接呼び出して指定してみる」でも説明しましたが、ColdFusionは .xls も .xlsx も汎用的に呼び出せるように実装されていますが、Apache POIを直接呼び出す際は、ファイル形式に合わせて呼び出すライブラリが異なります。例えばExcelワークブックを読み込むために使用するクラスは、
.xls形式の場合…org.apache.poi.hssf.usermodel.HSSFWorkbook
.xlsx形式の場合…org.apache.poi.hssf.usermodel.XSSFWorkbook
となり、使用するクラスが異なるので注意が必要です。
Excelの処理で、別のセルの値を参照して計算を行うようなことはよく行われます。例えば、下記のExcelシート(売上表.xls)の「合計(F列)」は、左側のQ1(B列)〜Q4(E列)の値を参照し、足した合計を表示しています。そのため、Excel上でオレンジ色のセルの部分(東北地方Q4)の値を変更すると、合計の値も再計算されます。
しかし、Aoacge POIの動作では、セルの値を書き換えるだけでは、自動で再計算は行われません。上記のExcelファイル(売上表.xls)に対して、下記のようにオレンジ色のセルの部分(東北地方Q4)の値を2000に変更してみます。
<cfspreadsheet action="read" src="#ExpandPath('売上表.xls')#" sheet="1" name="sExcel" /> //東北地方 Q4 のセルの値を変更 <cfset SpreadSheetSetCellValue(sExcel,"2000",3,5)> <cfspreadsheet action="write" filename="売上表_out.xls" name="sExcel" overwrite="yes" autosize="false" />
上記の実行結果を見ると、グラフの値は変更された2000が反映されていますが、合計(赤字の部分)は変更前の1800のままです。
ColdFusionのタグや関数には再計算を行うものが見つかりませんでした。そこで次はライブラリであるPOIに同様の報告がないかを確認してみます。すると、下記のサイトに同様の現象の報告とその回避プログラムの例が詳しく紹介されていました。
https://so-kai-app.sakura.ne.jp/blog/1067/2014/12/05/
上記の内容から、POIの機能を使って数式の再計算をさせるか、Excelを開いた時に再計算をさせるフラグを有効にすることができるようです。
cfspreadsheet の name 属性を指定することで、スプレッドシートオブジェクトとして変数に値を格納することができます。@で紹介しているプログラムでは、1〜2行目の<cfspreadsheet action="read" ...> の中で売上表.xlsを読み込み sExcel という名前で変数を作成しました。
次にセルの値を書き換えた処理の後にプログラムを追加します。まず、変数 sExcel に POI のgetWorkBook()メソッドを実行し、Workbookオブジェクトを取得します。
続いて、Aでご紹介したサイトで紹介されていた情報を参考に、getCreationHelper()メソッドと createFormulaEvaluator() メソッドを利用して、セルの値の評価を行いました。
<cfspreadsheet action="read" src="#ExpandPath('売上表.xls')#" sheet="1" name="sExcel" /> //東北地方 Q4 のセルの値を変更 <cfset SpreadSheetSetCellValue(sExcel,"2000",3,5)> //POIのメソッドを追加 <cfset obj=sExcel.getWorkBook()> <cfset obj.getCreationHelper().createFormulaEvaluator().evaluateAll()> <cfspreadsheet action="write" filename="売上表_out2.xls" name="sExcel" overwrite="yes" autosize="false" />
上記のプログラムを実行することにより、POIのメソッドが実行され、セルの数式の再計算が行われます。上記のプログラムを実施後、書き出されたExcelファイル(売上表_out2.xls)を見ると、下記のように計算が反映された結果が表示されました。
Aのサイトに紹介されているもう一つの方法も試してみます。POI のgetWorkBook()メソッドを実行し、Workbookオブジェクトを取得するところまでは同じです。
その後、setForceFormulaRecalculation()メソッドを実行して、ワークブックを開いたときにアプリケーションが完全な再計算を実行します。値にtrueを設定すると、次に開くときにブック内のすべての数式を再計算する必要があることをExcelに通知します。再計算によってキャッシュされた数式の結果が更新され、ワークブックが変更されます(バージョンによっては、「(ファイル名)の変更を保存しますか?」というメッセージが表示されます)
<cfspreadsheet action="read" src="#ExpandPath('売上表.xls')#" sheet="1" name="sExcel" /> //東北地方 Q4 のセルの値を変更 <cfset SpreadSheetSetCellValue(sExcel,"2000",3,5)> //POIのメソッドを追加 <cfset obj=sExcel.getWorkBook()> <cfset obj.setForceFormulaRecalculation(true)> <cfspreadsheet action="write" filename="売上表_out3.xls" name="sExcel" overwrite="yes" autosize="false" />
上記のプログラムを実行することにより、Excelファイルを開いた時に数式の再計算が行われました。そのままExcelファイルを閉じようとすると、下記のように変更を保存するかを確認するダイヤログが表示されるようになります。
今回の実験プログラムはこちらからダウンロードできます⇒lab6.zip
ColdFusionのExcel処理系にPOIが使用されていることを利用して、オブジェクトに対してそのままgetWorkBook()メソッドを利用することができました。Workbookオブジェクトに対して実行可能なメソッドについては、こちらのPOIのサイトをご確認下さい。
今回のように処理の途中でJavaのメソッドを直接呼ぶような機会はなかなかありませんが、CFタグや関数だけでは実行できなかった事をやっていく一つの可能性が出て来ると思います。
今後、POIの機能を使うことで便利な事が増える情報を確認しましたら、このページに更新していこうと思います。