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.exceptions.InvalidFormatException;
\r
32 import org.apache.poi.openxml4j.opc.OPCPackage;
\r
33 import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
\r
34 import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
\r
35 import org.apache.poi.ss.usermodel.Cell;
\r
36 import org.apache.poi.ss.usermodel.Row;
\r
37 import org.apache.poi.ss.usermodel.Sheet;
\r
38 import org.apache.poi.ss.usermodel.Workbook;
\r
39 import org.apache.poi.ss.usermodel.WorkbookFactory;
\r
40 import org.apache.poi.ss.util.CellReference;
\r
41 import org.apache.poi.xssf.usermodel.XSSFChartSheet;
\r
42 import org.apache.poi.xssf.usermodel.XSSFDialogsheet;
\r
43 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
\r
44 import org.hanei.jaxcel.exception.JaxcelInputException;
\r
45 import org.hanei.jaxcel.exception.JaxcelOutputException;
\r
46 import org.slf4j.LoggerFactory;
\r
47 import org.slf4j.Logger;
\r
51 * テンプレートのExcelファイルにデータを挿入することでExcel帳票を生成する。
\r
53 * <h4>テンプレートの書式について</h4>
\r
54 * <h5>${expression}</h5>
\r
55 * <p>値出力。expressionを解析し値を出力する。</p>
\r
57 * <h5>#if(${expression} delete:"left|up|clear" block:"true|false" rows:"number" cols:"number")</h5>
\r
58 * <p>if句。expressionを解析した結果、false判定の場合、指定範囲を設定の方法で削除する。</p>
\r
59 * <table border="1" cellspacing="1">
\r
60 * <tr><th>属性</th><th>説明</th></tr>
\r
62 * <td>${expression}</td>
\r
64 * Bool値を返す式を指定する。Bool値を返さない式の場合は、値がnullでなければtrueと判断する。</td>
\r
68 * <td>オプション。デフォルト "left"<br>
\r
69 * ${expression}がfalse判定の場合のセルに対する操作を指定する。<br>
\r
71 * <tr><td>left</td><td>左に詰める。</td></tr>
\r
72 * <tr><td>up</td><td>上に詰める。</td></tr>
\r
73 * <tr><td>clear</td><td>セルの値・計算式をクリアするのみで詰めない。</td></tr>
\r
78 * <td>オプション。デフォルト "true"<br>
\r
79 * ブロック範囲での削除操作について指定する。"false"指定で行列全体での削除となる。<br>
\r
80 * deleteオプションが"up"且つ、blockオプション"false"で行全体が削除対象となる。<br>
\r
81 * deleteオプションが"left"且つ、blockオプション"false"で列全体が削除対象となる。
\r
85 * <td>オプション。デフォルト "1"<br>
\r
86 * 影響範囲の行数を指定。"1"指定で#if指示子が存在する行のみとなる。
\r
90 * <td>オプション。デフォルト "1"<br>
\r
91 * 影響範囲の列数を指定。"1"指定で#if指示子が存在する列のみとなる。
\r
95 * <h5>#foreach(${object in listObject} direction:"row|col" style:"copy|^copy" shift:"true|false" block:"true|false" start:"number" end:"number" rows:"number" cols:"number")</h5>
\r
96 * <p>foreach句。listObjectで繰返し処理を行う。</p>
\r
97 * <table border="1" cellspacing="1">
\r
98 * <tr><th>属性</th><th>説明</th></tr>
\r
100 * <td>${object in listObject}</td>
\r
102 * オブジェクト in リストオブジェクトのフォーマットでを指定する。リストオブジェクトには配列、List、Mapが使用可能。</td>
\r
105 * <td>direction</td>
\r
106 * <td>オプション。デフォルト "row"<br>
\r
109 * <tr><td>row</td><td>行(縦)方向に繰返す。</td></tr>
\r
110 * <tr><td>col</td><td>列(横)方向に繰返す。</td></tr>
\r
115 * <td>オプション。デフォルト "copy"<br>
\r
116 * 繰返し範囲のセルスタイルのコピーについて指定する。
\r
118 * <tr><td>copy</td><td>スタイルを繰返しコピーする。</td></tr>
\r
119 * <tr><td>copy以外</td><td>スタイルをコピーしない。</td></tr>
\r
124 * <td>オプション。デフォルト "true"<br>
\r
125 * 繰返し範囲より後方の範囲をずらすか(シフトするか)について指定する。
\r
127 * <tr><td>true</td><td>direction方向にシフトする。</td></tr>
\r
128 * <tr><td>false</td><td>シフトしない。</td></tr>
\r
133 * <td>オプション。デフォルト "true"<br>
\r
134 * ブロック範囲での繰返し操作について指定する。"false"指定で行列全体での繰返しとなる。<br>
\r
135 * directionオプションが"row"且つ、blockオプションが"false"で行全体が繰返し範囲となる。<br>
\r
136 * directionオプションが"col"且つ、blockオプションが"false"で列全体が繰返し範囲となる。
\r
140 * <td>オプション。デフォルト "1"<br>
\r
141 * 影響範囲の行数を指定。"1"指定で#foreach指示子が存在する行のみとなる。
\r
145 * <td>オプション。デフォルト "1"<br>
\r
146 * 影響範囲の列数を指定。"1"指定で#foreach指示子が存在する列のみとなる。
\r
150 * <td>オプション。デフォルト "1"<br>
\r
151 * リストオブジェクトの開始インデックス(1起点)。
\r
155 * <td>オプション。デフォルト リストオブジェクトの要素数<br>
\r
156 * リストオブジェクトの終了インデックス(1起点)。<br>
\r
157 * ※start(開始インデックス)・end(終了インデックス)の指定により、降順ループ(start > end)も可能。<br>
\r
158 * ※リストオブジェクトの要素数はsize(list)で取得可能。
\r
163 * @author Noboru Saito
\r
165 public class ReportMaker {
\r
167 private static final Logger log = LoggerFactory.getLogger(ReportMaker.class);
\r
171 * Excel 2003 形式(.xls)
\r
173 private NPOIFSFileSystem npoifs = null;
\r
177 * Excel 2007 形式(.xlsx, .xlsm)
\r
179 private OPCPackage pkg = null;
\r
184 private ELManager elMgr = null;
\r
189 private JaxcelContext context = null;
\r
194 public ReportMaker() {}
\r
197 * 入力ストリームのExcelテンプレートファイルにデータを挿入することでExcel帳票を生成、Workbookオブジェクトを返却する。<br>
\r
198 * 返却されたWorkbookオブジェクトはPOIを使用し、加工・出力が可能。<br>
\r
199 * 入力ストリームは別途クローズが必要。
\r
201 * @param template Excelテンプレートファイル入力ストリーム
\r
202 * @param parameter テンプレートに挿入するデータ
\r
204 * @return Workbookオブジェクト
\r
206 * @throws JaxcelInputException 入力例外発生時
\r
208 public Workbook makeReport(InputStream template, Map<String, Object> parameter) {
\r
209 log.trace("makeReport start");
\r
212 if(template == null) {
\r
213 log.error("template is null");
\r
214 throw new JaxcelInputException("template is null");
\r
216 if(parameter == null) {
\r
217 log.debug("parameter is null");
\r
220 // Excelテンプレートファイルオープン
\r
221 Workbook book = openWorkbook(template);
\r
224 makeReport(book, parameter);
\r
226 log.trace("makeReport end");
\r
231 * 入力ストリームのExcelテンプレートファイルにデータを挿入することでExcel帳票を生成、出力ストリームにExcel帳票を出力する。<br>
\r
232 * 入出力ストリームは別途クローズが必要。
\r
234 * @param template Excelテンプレートファイル入力ストリーム
\r
235 * @param parameter テンプレートに挿入するデータ
\r
236 * @param output Excel帳票出力ストリーム
\r
238 * @throws JaxcelInputException 入力例外発生時
\r
239 * @throws JaxcelOutputException 出力例外発生時
\r
241 public void makeReport(InputStream template, Map<String, Object> parameter, OutputStream output) {
\r
242 log.trace("makeReport start");
\r
245 Workbook book = makeReport(template, parameter);
\r
248 outputReport(book, output);
\r
253 log.trace("makeReport end");
\r
257 * 入力ストリームのExcelテンプレートファイルにデータを挿入することでExcel帳票を生成、Excel帳票ファイルを出力する。<br>
\r
258 * 入力ストリームは別途クローズが必要。
\r
260 * @param template Excelテンプレートファイル入力ストリーム
\r
261 * @param parameter テンプレートに挿入するデータ
\r
262 * @param output Excel帳票出力ファイル
\r
264 * @throws JaxcelInputException 入力例外発生時
\r
265 * @throws JaxcelOutputException 出力例外発生時
\r
267 public void makeReport(InputStream template, Map<String, Object> parameter, File output) {
\r
268 log.trace("makeReport start");
\r
271 Workbook book = makeReport(template, parameter);
\r
274 FileOutputStream _output;
\r
276 _output = new FileOutputStream(output);
\r
278 catch(Exception e) {
\r
279 log.error("output file open error: {}", e.getMessage(), e);
\r
280 throw new JaxcelOutputException("output file open error");
\r
284 outputReport(book, _output);
\r
287 } catch (IOException e) {
\r
288 log.error("output file close error: {}", e.getMessage(), e);
\r
289 throw new JaxcelOutputException("output file close error");
\r
295 log.trace("makeReport end");
\r
299 * Excelテンプレートファイルにデータを挿入することでExcel帳票を生成、Workbookオブジェクトを返却する。<br>
\r
300 * 返却されたWorkbookオブジェクトはPOIを使用し、加工・出力が可能。<br>
\r
301 * Excelテンプレートファイルは別途クローズが必要。
\r
303 * @param template Excelテンプレートファイル
\r
304 * @param parameter テンプレートに挿入するデータ
\r
306 * @return Workbookオブジェクト
\r
308 * @throws JaxcelInputException 入力例外発生時
\r
310 public Workbook makeReport(File template, Map<String, Object> parameter) {
\r
311 log.trace("makeReport start");
\r
314 if(template == null) {
\r
315 log.error("template file is null");
\r
316 throw new JaxcelInputException("template file is null");
\r
318 else if(!template.exists()) {
\r
319 log.error("template file does not exist: {}", template.getAbsolutePath());
\r
320 throw new JaxcelInputException("template file does not exist");
\r
322 else if(!template.canRead()) {
\r
323 log.error("template file can not read: {}", template.getAbsolutePath());
\r
324 throw new JaxcelInputException("template file can not read");
\r
326 if(parameter == null) {
\r
327 log.debug("parameter is null");
\r
330 // Excelテンプレートファイルオープン
\r
331 Workbook book = openWorkbook(template);
\r
334 makeReport(book, parameter);
\r
336 log.trace("makeReport end");
\r
341 * Excelテンプレートファイルにデータを挿入することでExcel帳票を生成、出力ストリームにExcel帳票を出力する。<br>
\r
342 * 出力ストリームは別途クローズが必要。
\r
344 * @param template Excelテンプレートファイル
\r
345 * @param parameter テンプレートに挿入するデータ
\r
346 * @param output Excel帳票出力ストリーム
\r
348 * @throws JaxcelInputException 入力例外発生時
\r
349 * @throws JaxcelOutputException 出力例外発生時
\r
351 public void makeReport(File template, Map<String, Object> parameter, OutputStream output) {
\r
352 log.trace("makeReport start");
\r
355 Workbook book = makeReport(template, parameter);
\r
358 outputReport(book, output);
\r
363 log.trace("makeReport end");
\r
367 * Excelテンプレートファイルにデータを挿入することでExcel帳票を生成、Excel帳票ファイルを出力する。<br>
\r
369 * @param template Excelテンプレートファイル
\r
370 * @param parameter テンプレートに挿入するデータ
\r
371 * @param output Excel帳票出力ファイル
\r
373 * @throws JaxcelInputException 入力例外発生時
\r
374 * @throws JaxcelOutputException 出力例外発生時
\r
376 public void makeReport(File template, Map<String, Object> parameter, File output) {
\r
377 log.trace("makeReport start");
\r
380 Workbook book = makeReport(template, parameter);
\r
383 FileOutputStream _output;
\r
385 _output = new FileOutputStream(output);
\r
387 catch(Exception e) {
\r
388 log.error("output file open error: {}", e.getMessage(), e);
\r
389 throw new JaxcelOutputException("output file open error");
\r
393 outputReport(book, _output);
\r
396 } catch (IOException e) {
\r
397 log.error("output file close error: {}", e.getMessage(), e);
\r
398 throw new JaxcelOutputException("output file close error");
\r
404 log.trace("makeReport end");
\r
408 * ExcelテンプレートのWorkbookオブジェクトにデータを挿入することでExcel帳票を生成する。<br>
\r
409 * Excelテンプレートファイルは別途クローズが必要。
\r
411 * @param book Workbookオブジェクト
\r
412 * @param parameter テンプレートに挿入するデータ
\r
414 * @throws JaxcelInputException 入力例外発生時
\r
416 public void makeReport(Workbook book, Map<String, Object> parameter) {
\r
417 log.trace("makeReport start");
\r
421 log.error("workbook is null");
\r
422 throw new JaxcelInputException("Workbook is null");
\r
424 else if(!(book instanceof HSSFWorkbook) && !(book instanceof XSSFWorkbook)) {
\r
425 log.error("Workbook is unsupport type: {}", book.getClass().getName());
\r
426 throw new JaxcelInputException("Workbook is unsupported type");
\r
428 if(parameter == null) {
\r
429 log.debug("parameter is null");
\r
433 context = new JaxcelContext();
\r
435 // EL式マネージャ生成。パラメータ設定
\r
436 elMgr = new ELManager();
\r
437 elMgr.setParameter(parameter);
\r
439 // JaxlsコンテキストにEL式マネージャ設定
\r
440 context.setElManager(elMgr);
\r
445 log.trace("makeReport end");
\r
451 * @param template Excelテンプレートファイル 入力ストリーム or ファイル
\r
453 * @throws JaxcelInputException Excelテンプレートファイルオープン失敗時
\r
455 private Workbook openWorkbook(Object template) {
\r
456 log.trace("openWorkbook start");
\r
459 Workbook book = null;
\r
461 // Excelテンプレートファイルオープン
\r
465 if(template instanceof File) {
\r
466 npoifs = new NPOIFSFileSystem((File) template);
\r
469 npoifs = new NPOIFSFileSystem((InputStream) template);
\r
471 book = WorkbookFactory.create(npoifs);
\r
472 } catch(OfficeXmlFileException | IOException e1) {
\r
476 if(template instanceof File) {
\r
477 pkg = OPCPackage.open((File) template);
\r
480 pkg = OPCPackage.open((InputStream) template);
\r
482 book = WorkbookFactory.create(pkg);
\r
483 } catch (InvalidFormatException | IOException e2) {
\r
484 log.error("template file open error: {}. {}", e1.getMessage(), e2.getMessage());
\r
490 throw new JaxcelInputException("template file open error");
\r
493 log.trace("openWorkbook end");
\r
499 * 出力ストリームにワークブックを出力する
\r
502 * @param book Workbookオブジェクト
\r
503 * @param output 出力ストリーム
\r
505 * @throws JaxcelOutputException 出力例外発生時
\r
507 private void outputReport(Workbook book, OutputStream output) {
\r
508 log.trace("outputReport start");
\r
511 book.write(output);
\r
512 } catch (IOException e) {
\r
513 log.error("workbook output error: {}", e.getMessage(), e);
\r
514 throw new JaxcelOutputException("workbook output error");
\r
517 log.trace("outputReport end");
\r
521 * Excelテンプレートファイルのクローズ<br>
\r
522 * テンプレートファイルの変更は保存しません。
\r
524 * @throws JaxcelOutputException 出力例外発生時
\r
526 public void close() {
\r
527 log.trace("close start");
\r
530 if (npoifs != null) {
\r
532 log.debug("template file close.");
\r
537 log.debug("template file close.");
\r
540 } catch (IOException e) {
\r
541 log.error("template file close error: {}", e.getMessage(), e);
\r
542 throw new JaxcelOutputException("template file close error");
\r
545 log.trace("close end");
\r
550 * @param book Workbookオブジェクト
\r
552 private void makeBook(Workbook book) {
\r
553 log.trace("makeBook start");
\r
556 log.debug("sheet count: {}", book.getNumberOfSheets());
\r
557 for(int i = 0; i < book.getNumberOfSheets(); i++) {
\r
559 Sheet sheet = book.getSheetAt(i);
\r
560 if(sheet == null) {
\r
561 log.warn("sheet[{}] is null. skip", i);
\r
564 else if((sheet instanceof HSSFSheet && ((HSSFSheet)sheet).getDialog()) || (sheet instanceof XSSFDialogsheet)) {
\r
565 log.debug("sheet[{}] is dialog sheet. skip", i);
\r
568 else if(sheet instanceof XSSFChartSheet) {
\r
569 log.debug("sheet[{}] is chart sheet. skip", i);
\r
573 log.debug("sheet[{}] name: {}", i, sheet.getSheetName());
\r
580 book.setForceFormulaRecalculation(true);
\r
582 log.trace("makeBook end");
\r
587 * @param sheet ワークシートオブジェクト
\r
589 private void makeSheet(Sheet sheet) {
\r
590 log.trace("makeSheet start");
\r
592 Row row; // 行オブジェクト
\r
593 Cell cell; // セルブジェクト
\r
594 TLParser tlParser; // パーサ
\r
597 context.setCurrentSheet(sheet);
\r
598 tlParser = new TLParser(context);
\r
601 int lastRowNum = sheet.getLastRowNum();
\r
602 log.debug("lastRowNum: {}", lastRowNum);
\r
608 for(int rowIdx = 0; rowIdx <= lastRowNum; rowIdx++) {
\r
610 row = sheet.getRow(rowIdx);
\r
614 log.debug("row[{}] is null", (rowIdx + 1));
\r
619 if(row.getLastCellNum() > maxColNum) {
\r
620 maxColNum = row.getLastCellNum();
\r
621 log.debug("maxColNum: {}", maxColNum);
\r
625 for(int cellIdx = 0; cellIdx <= maxColNum; cellIdx++) {
\r
627 cell = row.getCell(cellIdx);
\r
631 log.debug("cell[{}] is null", (new CellReference(rowIdx, cellIdx)).formatAsString());
\r
636 switch (cell.getCellType()) {
\r
638 case Cell.CELL_TYPE_STRING:
\r
639 case Cell.CELL_TYPE_FORMULA:
\r
641 tlParser.parse(cell);
\r
643 if(tlParser.isReParseCell()) {
\r
650 log.debug("cell type is not string or formula");
\r
655 if(row.getLastCellNum() > maxColNum) {
\r
656 maxColNum = row.getLastCellNum();
\r
657 log.debug("maxColNum update: {}", maxColNum);
\r
661 if(lastRowNum < sheet.getLastRowNum()) {
\r
662 lastRowNum = sheet.getLastRowNum();
\r
663 log.debug("lastRowNum update: {}", lastRowNum);
\r
666 log.trace("transformSheet end");
\r