2 * Copyright 2014 Hanei Management Co.,Ltd.
\r
4 * This file is part of Jaxcel
\r
6 * Jaxcel is free software: you can redistribute it and/or modify
\r
7 * it under the terms of the GNU Lesser General Public License as published by
\r
8 * the Free Software Foundation, either version 3 of the License, or
\r
9 * (at your option) any later version.
\r
11 * Jaxcel is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU Lesser General Public License for more details.
\r
16 * You should have received a copy of the GNU Lesser General Public License
\r
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
\r
19 package org.hanei.jaxcel.report;
\r
21 import java.io.File;
\r
22 import java.io.FileOutputStream;
\r
23 import java.io.IOException;
\r
24 import java.io.InputStream;
\r
25 import java.io.OutputStream;
\r
26 import java.util.Map;
\r
29 import org.apache.poi.hssf.usermodel.HSSFSheet;
\r
30 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
\r
31 import org.apache.poi.openxml4j.opc.OPCPackage;
\r
32 import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
\r
33 import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
\r
34 import org.apache.poi.ss.usermodel.Cell;
\r
35 import org.apache.poi.ss.usermodel.Row;
\r
36 import org.apache.poi.ss.usermodel.Sheet;
\r
37 import org.apache.poi.ss.usermodel.Workbook;
\r
38 import org.apache.poi.ss.usermodel.WorkbookFactory;
\r
39 import org.apache.poi.ss.util.CellReference;
\r
40 import org.apache.poi.xssf.usermodel.XSSFChartSheet;
\r
41 import org.apache.poi.xssf.usermodel.XSSFDialogsheet;
\r
42 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
\r
43 import org.hanei.jaxcel.exception.JaxcelInputException;
\r
44 import org.hanei.jaxcel.exception.JaxcelOutputException;
\r
45 import org.slf4j.LoggerFactory;
\r
46 import org.slf4j.Logger;
\r
50 * テンプレートのExcelファイルにデータを挿入することでExcel帳票を生成する。
\r
52 * <h4>テンプレートの書式について</h4>
\r
53 * <h5>${expression}</h5>
\r
54 * <p>値出力。expressionを解析し値を出力する。</p>
\r
56 * <h5>#foreach(${object in aryObject}[ rows:"number"][ cols:"number"][ direction:"row|col"][ style:"copy|^copy"][ shift:"true|false"][ block:"true|false"][ start:"number"][ end:"number"])</h5>
\r
57 * <p>foreach句。配列やListオブジェクト、Mapオブジェクトの繰返し出力を行う。<br>
\r
58 * プロパティにより、繰返し出力方向の指定や、繰返し回数、出力範囲以降のセルのシフト方法等を指定可能。</p>
\r
59 * <table border="1" cellspacing="1">
\r
60 * <tr><th>属性</th><th>説明</th></tr>
\r
62 * <td>${object in aryObject}</td>
\r
63 * <td>"${" 要素オブジェクト in 配列オブジェクト "}" のフォーマットで指定する。配列オブジェクトには配列、List、Mapが使用可能。<br>
\r
64 * rows・colsオプションで指定した範囲内で、要素オブジェクトで指定した名称で各インデックスの要素が出力可能。</td>
\r
68 * <td>繰返し元の行数を正の整数で指定する。"1"指定で#foreach指示子が記述された行のみとなる。省略すると規定値である "1"となる。</td>
\r
72 * <td>繰返し元の列数を正の整数で指定する。"1"指定で#foreach指示子が記述された列のみとなる。省略すると規定値である "1"となる。</td>
\r
75 * <td>direction</td>
\r
76 * <td>繰返し処理の方向を指定する。省略すると規定値である"row"となる。<br>
\r
78 * <tr><td>row</td><td>行方向に繰返す。</td></tr>
\r
79 * <tr><td>col</td><td>列方向に繰返す。</td></tr>
\r
84 * <td>繰返し元範囲(rows・colsオプションで指定する範囲)のセルスタイルのコピーについて指定する。省略すると規定値である"copy"となる。<br>
\r
86 * <tr><td>copy</td><td>セルスタイルをコピーする。</td></tr>
\r
87 * <tr><td>copy以外</td><td>セルスタイルをコピーしない。</td></tr>
\r
92 * <td>繰返し範囲より後方の範囲のシフト方法について指定する。省略すると規定値である"true"となる。<br>
\r
94 * <tr><td>true</td><td>繰返し範囲より後方の範囲をdirectionオプションで指定した方向にシフトする。</td></tr>
\r
95 * <tr><td>false</td><td>シフトしない。</td></tr>
\r
100 * <td>矩形範囲での繰返し処理を行うかについて指定する。省略すると規定値である"true"となる。<br>
\r
102 * <tr><td>true</td><td>繰返し範囲を矩形範囲(rows・colsオプションで指定する範囲)とする。</td></tr>
\r
103 * <tr><td>false</td><td>繰返し範囲を繰返し元範囲に含まれる行・列全体とする。<br>
\r
104 * directionオプションが"row"の場合、繰返し元範囲に含まれる行全体が繰返し範囲となる。<br>
\r
105 * directionオプションが"col"の場合、繰返し元範囲に含まれる列全体が繰返し範囲となる。</td></tr>
\r
110 * <td>配列オブジェクトの出力開始インデックス(1起点)を正の整数、もしくは、正の整数を返却する関数で指定する。省略すると規定値である"1"となる。</td>
\r
114 * <td>配列オブジェクトの出力終了インデックス(1起点)を正の整数、もしくは、正の整数を返却する関数で指定する。省略すると配列オブジェクトの要素数となる。</td>
\r
117 * <td colspan="2">startオプションよりendオプションの指定値が小さい場合、配列オブジェクトを降順で出力する。<br>
\r
118 * 配列オブジェクトの要素数は"size(配列オブジェクト)"関数で取得可能。</td>
\r
122 * <h5>#if(${expression}[ rows:"number"][ cols:"number"][ delete:"left|up|clear"][ block:"true|false"])</h5>
\r
123 * <p>if句。expression判定式がtrue判定の場合は指定範囲の出力、false判定の場合は指定の方法で範囲を削除する。</p>
\r
124 * <table border="1" cellspacing="1">
\r
125 * <tr><th>属性</th><th>説明</th></tr>
\r
127 * <td>${expression}</td>
\r
128 * <td>判定式。"${" 式 "}" のフォーマットで指定する。Bool値を返す式を指定する。Bool値を返さない式の場合は、値がnullでなければtrueと判断する。</td>
\r
132 * <td>制御範囲の行数を正の整数で指定する。"1"指定で#if指示子が記述された行のみとなる。省略すると規定値である "1"となる。</td>
\r
136 * <td>制御範囲の列数を正の整数で指定する。"1"指定で#if指示子が記述された列のみとなる。省略すると規定値である "1"となる。</td>
\r
140 * <td>判定式がflse判定の場合の制御範囲に対する操作を指定する。省略すると規定値である"left"となる。<br>
\r
142 * <tr><td>left</td><td>制御範囲セルを削除し、左に詰める。</td></tr>
\r
143 * <tr><td>up</td><td>制御範囲セルを削除し、上に詰める。</td></tr>
\r
144 * <tr><td>clear</td><td>制御範囲セルの値・計算式をクリアするのみで詰めない。</td></tr>
\r
149 * <td>矩形範囲での制御を行うかについて指定する。省略すると規定値である"true"となる。<br>
\r
151 * <tr><td>true</td><td>制御範囲を矩形範囲(rows・colsオプションで指定する範囲)とする。</td></tr>
\r
152 * <tr><td>false</td><td>制御範囲を矩形範囲に含まれる行・列全体とする。<br>
\r
153 * deleteオプションが"left"の場合、矩形範囲に含まれる列全体が制御範囲となる。<br>
\r
154 * deleteオプションが"up"の場合、矩形範囲に含まれる行全体が制御範囲となる。<br>
\r
155 * deleteオプションが"clear"の場合は無効。</td></tr>
\r
161 * @author Noboru Saito
\r
163 public class ReportMaker {
\r
165 private static final Logger log = LoggerFactory.getLogger(ReportMaker.class);
\r
169 * Excel 2003 形式(.xls)
\r
171 private NPOIFSFileSystem npoifs = null;
\r
175 * Excel 2007 形式(.xlsx, .xlsm)
\r
177 private OPCPackage pkg = null;
\r
182 private ELManager elMgr = null;
\r
187 private JaxcelContext context = null;
\r
192 public ReportMaker() {}
\r
195 * 入力ストリームのExcelテンプレートファイルにデータを挿入することでExcel帳票を生成、Workbookオブジェクトを返却する。<br>
\r
196 * 返却されたWorkbookオブジェクトはPOIを使用し、加工・出力が可能。<br>
\r
197 * 入力ストリームは別途クローズが必要。
\r
199 * @param template Excelテンプレートファイル入力ストリーム
\r
200 * @param parameter テンプレートに挿入するデータ
\r
202 * @return Workbookオブジェクト
\r
204 * @throws JaxcelInputException 入力例外発生時
\r
206 public Workbook makeReport(InputStream template, Map<String, Object> parameter) {
\r
207 log.trace("makeReport start");
\r
210 if(template == null) {
\r
211 log.error("template is null");
\r
212 throw new JaxcelInputException("template is null");
\r
214 if(parameter == null) {
\r
215 log.debug("parameter is null");
\r
218 // Excelテンプレートファイルオープン
\r
219 Workbook book = openWorkbook(template);
\r
222 makeReport(book, parameter);
\r
224 log.trace("makeReport end");
\r
229 * 入力ストリームのExcelテンプレートファイルにデータを挿入することでExcel帳票を生成、出力ストリームにExcel帳票を出力する。<br>
\r
230 * 入出力ストリームは別途クローズが必要。
\r
232 * @param template Excelテンプレートファイル入力ストリーム
\r
233 * @param parameter テンプレートに挿入するデータ
\r
234 * @param output Excel帳票出力ストリーム
\r
236 * @throws JaxcelInputException 入力例外発生時
\r
237 * @throws JaxcelOutputException 出力例外発生時
\r
239 public void makeReport(InputStream template, Map<String, Object> parameter, OutputStream output) {
\r
240 log.trace("makeReport start");
\r
243 Workbook book = makeReport(template, parameter);
\r
246 outputReport(book, output);
\r
251 log.trace("makeReport end");
\r
255 * 入力ストリームのExcelテンプレートファイルにデータを挿入することでExcel帳票を生成、Excel帳票ファイルを出力する。<br>
\r
256 * 入力ストリームは別途クローズが必要。
\r
258 * @param template Excelテンプレートファイル入力ストリーム
\r
259 * @param parameter テンプレートに挿入するデータ
\r
260 * @param output Excel帳票出力ファイル
\r
262 * @throws JaxcelInputException 入力例外発生時
\r
263 * @throws JaxcelOutputException 出力例外発生時
\r
265 public void makeReport(InputStream template, Map<String, Object> parameter, File output) {
\r
266 log.trace("makeReport start");
\r
269 Workbook book = makeReport(template, parameter);
\r
272 FileOutputStream _output;
\r
274 _output = new FileOutputStream(output);
\r
276 catch(Exception e) {
\r
277 log.error("output file open error: {}", e.getMessage(), e);
\r
278 throw new JaxcelOutputException("output file open error");
\r
282 outputReport(book, _output);
\r
285 } catch (IOException e) {
\r
286 log.error("output file close error: {}", e.getMessage(), e);
\r
287 throw new JaxcelOutputException("output file close error");
\r
293 log.trace("makeReport end");
\r
297 * Excelテンプレートファイルにデータを挿入することでExcel帳票を生成、Workbookオブジェクトを返却する。<br>
\r
298 * 返却されたWorkbookオブジェクトはPOIを使用し、加工・出力が可能。<br>
\r
299 * Excelテンプレートファイルは別途クローズが必要。
\r
301 * @param template Excelテンプレートファイル
\r
302 * @param parameter テンプレートに挿入するデータ
\r
304 * @return Workbookオブジェクト
\r
306 * @throws JaxcelInputException 入力例外発生時
\r
308 public Workbook makeReport(File template, Map<String, Object> parameter) {
\r
309 log.trace("makeReport start");
\r
312 if(template == null) {
\r
313 log.error("template file is null");
\r
314 throw new JaxcelInputException("template file is null");
\r
316 else if(!template.exists()) {
\r
317 log.error("template file does not exist: {}", template.getAbsolutePath());
\r
318 throw new JaxcelInputException("template file does not exist");
\r
320 else if(!template.canRead()) {
\r
321 log.error("template file can not read: {}", template.getAbsolutePath());
\r
322 throw new JaxcelInputException("template file can not read");
\r
324 if(parameter == null) {
\r
325 log.debug("parameter is null");
\r
328 // Excelテンプレートファイルオープン
\r
329 Workbook book = openWorkbook(template);
\r
332 makeReport(book, parameter);
\r
334 log.trace("makeReport end");
\r
339 * Excelテンプレートファイルにデータを挿入することでExcel帳票を生成、出力ストリームにExcel帳票を出力する。<br>
\r
340 * 出力ストリームは別途クローズが必要。
\r
342 * @param template Excelテンプレートファイル
\r
343 * @param parameter テンプレートに挿入するデータ
\r
344 * @param output Excel帳票出力ストリーム
\r
346 * @throws JaxcelInputException 入力例外発生時
\r
347 * @throws JaxcelOutputException 出力例外発生時
\r
349 public void makeReport(File template, Map<String, Object> parameter, OutputStream output) {
\r
350 log.trace("makeReport start");
\r
353 Workbook book = makeReport(template, parameter);
\r
356 outputReport(book, output);
\r
361 log.trace("makeReport end");
\r
365 * Excelテンプレートファイルにデータを挿入することでExcel帳票を生成、Excel帳票ファイルを出力する。<br>
\r
367 * @param template Excelテンプレートファイル
\r
368 * @param parameter テンプレートに挿入するデータ
\r
369 * @param output Excel帳票出力ファイル
\r
371 * @throws JaxcelInputException 入力例外発生時
\r
372 * @throws JaxcelOutputException 出力例外発生時
\r
374 public void makeReport(File template, Map<String, Object> parameter, File output) {
\r
375 log.trace("makeReport start");
\r
378 Workbook book = makeReport(template, parameter);
\r
381 FileOutputStream _output;
\r
383 _output = new FileOutputStream(output);
\r
385 catch(Exception e) {
\r
386 log.error("output file open error: {}", e.getMessage(), e);
\r
387 throw new JaxcelOutputException("output file open error");
\r
391 outputReport(book, _output);
\r
394 } catch (IOException e) {
\r
395 log.error("output file close error: {}", e.getMessage(), e);
\r
396 throw new JaxcelOutputException("output file close error");
\r
402 log.trace("makeReport end");
\r
406 * ExcelテンプレートのWorkbookオブジェクトにデータを挿入することでExcel帳票を生成する。<br>
\r
407 * Excelテンプレートファイルは別途クローズが必要。
\r
409 * @param book Workbookオブジェクト
\r
410 * @param parameter テンプレートに挿入するデータ
\r
412 * @throws JaxcelInputException 入力例外発生時
\r
414 public void makeReport(Workbook book, Map<String, Object> parameter) {
\r
415 log.trace("makeReport start");
\r
419 log.error("workbook is null");
\r
420 throw new JaxcelInputException("Workbook is null");
\r
422 else if(!(book instanceof HSSFWorkbook) && !(book instanceof XSSFWorkbook)) {
\r
423 log.error("Workbook is unsupport type: {}", book.getClass().getName());
\r
424 throw new JaxcelInputException("Workbook is unsupported type");
\r
426 if(parameter == null) {
\r
427 log.debug("parameter is null");
\r
431 context = new JaxcelContext();
\r
433 // EL式マネージャ生成。パラメータ設定
\r
434 elMgr = new ELManager();
\r
435 elMgr.setParameter(parameter);
\r
437 // JaxlsコンテキストにEL式マネージャ設定
\r
438 context.setElManager(elMgr);
\r
443 log.trace("makeReport end");
\r
449 * @param template Excelテンプレートファイル 入力ストリーム or ファイル
\r
451 * @throws JaxcelInputException Excelテンプレートファイルオープン失敗時
\r
453 private Workbook openWorkbook(Object template) {
\r
454 log.trace("openWorkbook start");
\r
457 Workbook book = null;
\r
459 // Excelテンプレートファイルオープン
\r
463 if(template instanceof File) {
\r
464 npoifs = new NPOIFSFileSystem((File) template);
\r
467 npoifs = new NPOIFSFileSystem((InputStream) template);
\r
469 book = WorkbookFactory.create(npoifs);
\r
470 } catch(OfficeXmlFileException | IOException e1) {
\r
474 if(template instanceof File) {
\r
475 pkg = OPCPackage.open((File) template);
\r
478 pkg = OPCPackage.open((InputStream) template);
\r
480 book = WorkbookFactory.create(pkg);
\r
481 } catch (Exception e2) {
\r
482 log.error("template file open error: {}. {}", e1.getMessage(), e2.getMessage());
\r
488 throw new JaxcelInputException("template file open error");
\r
491 log.trace("openWorkbook end");
\r
497 * 出力ストリームにワークブックを出力する
\r
500 * @param book Workbookオブジェクト
\r
501 * @param output 出力ストリーム
\r
503 * @throws JaxcelOutputException 出力例外発生時
\r
505 private void outputReport(Workbook book, OutputStream output) {
\r
506 log.trace("outputReport start");
\r
509 book.write(output);
\r
510 } catch (Exception e) {
\r
511 log.error("workbook output error: {}", e.getMessage(), e);
\r
512 throw new JaxcelOutputException("workbook output error");
\r
515 log.trace("outputReport end");
\r
519 * Excelテンプレートファイルのクローズ<br>
\r
520 * テンプレートファイルの変更は保存しません。
\r
522 * @throws JaxcelOutputException 出力例外発生時
\r
524 public void close() {
\r
525 log.trace("close start");
\r
528 if (npoifs != null) {
\r
530 log.debug("template file close.");
\r
535 log.debug("template file close.");
\r
538 } catch (IOException e) {
\r
539 log.error("template file close error: {}", e.getMessage(), e);
\r
540 throw new JaxcelOutputException("template file close error");
\r
543 log.trace("close end");
\r
548 * @param book Workbookオブジェクト
\r
550 private void makeBook(Workbook book) {
\r
551 log.trace("makeBook start");
\r
554 log.debug("sheet count: {}", book.getNumberOfSheets());
\r
555 for(int i = 0; i < book.getNumberOfSheets(); i++) {
\r
557 Sheet sheet = book.getSheetAt(i);
\r
558 if(sheet == null) {
\r
559 log.warn("sheet[{}] is null. skip", i);
\r
562 else if((sheet instanceof HSSFSheet && ((HSSFSheet)sheet).getDialog()) || (sheet instanceof XSSFDialogsheet)) {
\r
563 log.debug("sheet[{}] is dialog sheet. skip", i);
\r
566 else if(sheet instanceof XSSFChartSheet) {
\r
567 log.debug("sheet[{}] is chart sheet. skip", i);
\r
571 log.debug("sheet[{}] name: {}", i, sheet.getSheetName());
\r
578 book.setForceFormulaRecalculation(true);
\r
580 log.trace("makeBook end");
\r
585 * @param sheet ワークシートオブジェクト
\r
587 private void makeSheet(Sheet sheet) {
\r
588 log.trace("makeSheet start");
\r
590 Row row; // 行オブジェクト
\r
591 Cell cell; // セルブジェクト
\r
592 TLParser tlParser; // パーサ
\r
595 context.setCurrentSheet(sheet);
\r
596 tlParser = new TLParser(context);
\r
599 int lastRowNum = sheet.getLastRowNum();
\r
600 log.debug("lastRowNum: {}", lastRowNum);
\r
606 for(int rowIdx = 0; rowIdx <= lastRowNum; rowIdx++) {
\r
608 row = sheet.getRow(rowIdx);
\r
612 log.debug("row[{}] is null", (rowIdx + 1));
\r
617 if(row.getLastCellNum() > maxColNum) {
\r
618 maxColNum = row.getLastCellNum();
\r
619 log.debug("maxColNum: {}", maxColNum);
\r
623 for(int cellIdx = 0; cellIdx <= maxColNum; cellIdx++) {
\r
625 cell = row.getCell(cellIdx);
\r
629 log.debug("cell[{}] is null", (new CellReference(rowIdx, cellIdx)).formatAsString());
\r
634 switch (cell.getCellType()) {
\r
636 case Cell.CELL_TYPE_STRING:
\r
637 case Cell.CELL_TYPE_FORMULA:
\r
639 tlParser.parse(cell);
\r
641 if(tlParser.isReParseCell()) {
\r
648 log.debug("cell type is not string or formula");
\r
653 if(row.getLastCellNum() > maxColNum) {
\r
654 maxColNum = row.getLastCellNum();
\r
655 log.debug("maxColNum update: {}", maxColNum);
\r
659 if(lastRowNum < sheet.getLastRowNum()) {
\r
660 lastRowNum = sheet.getLastRowNum();
\r
661 log.debug("lastRowNum update: {}", lastRowNum);
\r
664 log.trace("transformSheet end");
\r