POIの利用
では、ソースを通してPOIの利用箇所を確認していきましょう。まずは本稿の第2回でも説明した「WorkbookFactoryクラス」を利用して、準備したテンプレートファイルを読み込むメソッドの箇所です。
// (1)テンプレートファイルの読込 public static Workbook getWorkbook(String fileName) throws Exception { InputStream inp = new FileInputStream(fileName); Workbook wb = WorkbookFactory.create(inp); System.out.println("ファイル名「" + fileName + "」を読込ました"); return wb; }
- ExcelFileUtil -(1)
Utilityクラスのクラスメソッドの中で「WorkbookFactoryクラス」の「createメソッド」を利用して、テンプレートとして用意していたExcelファイルを読み込み、ワークブックオブジェクトを返すようにしています。次にワークブックオブジェクトと納品データを格納しているオブジェクトを「SimpleReportCreatorクラス」に渡し、POIを利用して納品書を作成します。では、ソースを通して部分ごとに確認していきましょう。
public Workbook create() { // (1)印刷範囲を取得 String printArea = wb.getPrintArea(wb.getSheetIndex("TEMPLATE")); if (printArea != null) { int sheetPosition = printArea.indexOf("!"); if (sheetPosition != -1) { printArea = printArea.substring(sheetPosition + 1); } else { printArea = null; } } if (reportList.size() > 1) { for (int reportIndex = 0; reportIndex < reportList.size(); reportIndex++) { // (2)テンプレートシートをデータ数分シートコピーする Sheet cloneSheet = wb.cloneSheet(wb.getSheetIndex("TEMPLATE")); // (3)ワークシート名を設定 wb.setSheetName(wb.getSheetIndex(cloneSheet), reportList.get( reportIndex).getHeader().getReportName()); // (4)印刷範囲を設定 if (printArea != null) { wb.setPrintArea(wb.getSheetIndex(cloneSheet), printArea); } } // (5)テンプレーシートの削除 wb.removeSheetAt(wb.getSheetIndex("TEMPLATE")); } ~続く~
- SimpleReportCreator -(1)
- SimpleReportCreator -(2)
- SimpleReportCreator -(3)
- SimpleReportCreator -(4)
- SimpleReportCreator -(5)
テンプレートシートのシート名である「TEMPLATE」を引数に渡して、テンプレートシートのシートインデックスをWorkbookインターフェースのメソッド「int getSheetIndex(java.lang.String name)」で取得後、同じくWorkbookインターフェースのメソッド「java.lang.String getPrintArea(int sheetIndex)」を利用してテンプレートシートに設定されている印刷範囲を取得しています。印刷範囲はPOIの仕様で「TEMPLATE!$A$1:$D$27」のようにシート名が自動で追加されているため、シート名を除いた印刷範囲「$A$1:$D$27」を取得する処理が続いています。取得した印刷範囲はテンプレートシートをシートコピーして作成したワークシートの印刷範囲として(4)で設定しています。テンプレートシートをシートコピーしただけではコピー後のワークシートに印刷範囲が適切に引き渡せないため、追加した処理です。
納入先ごとの納品情報を1つのオブジェクト単位として管理している「reportList」という変数名のArrayListに複数の納入先オブジェクトが存在した場合は、ワークブックインターフェースのメソッド「Sheet cloneSheet(int sheetNum)」を利用してテンプレートシートを納入先分シートコピーしています。
シートコピーして作成したワークシートの名称にWorkbookインターフェースのメソッド「void setSheetName(int sheet, java.lang.String name)」を利用して、納入先の名称を設定しています。
シートコピーして作成したワークシートにWorkbookインターフェースのメソッド「void setPrintArea(int sheetIndex, java.lang.String reference)」を利用して、テンプレートシートにあらかじめ設定しておいた印刷範囲を設定しています。
テンプレートシートをWorkbookインターフェースのメソッド「void removeSheetAt(int sheetIndex)」を利用して削除しています。なお、納入先が1つの場合はテンプレートシートに直接データを設定していくため、テンプレートシートの削除はしていません。
ReportData reportData = null; Map<String, String> headerMap = null; Map<String, Object[]> detailsMap = null; // (6)ワークシート単位の繰返し処理 int numOfSheet = wb.getNumberOfSheets(); for (int sheetIndex = 0; sheetIndex < numOfSheet; sheetIndex++) { Sheet sheet = wb.getSheetAt(sheetIndex); // ワークシートに対応するデータを取得 reportData = reportList.get(sheetIndex); headerMap = reportData.getHeader().getDataMap(); detailsMap = reportData.getDetails().getDataListMap(); if (reportData.getDetails().getNumOfDetails() != 0) { numOfDetails = reportData.getDetails().getNumOfDetails(); } // (7)行単位の繰返し処理 int lastRow = sheet.getLastRowNum(); for (int rowIndex = 0; rowIndex <= lastRow; rowIndex++) { Row row = sheet.getRow(rowIndex); if (row == null) { continue; } // (8)セル単位の繰返し処理 int lastColumn = row.getLastCellNum(); for (int columnIndex = 0; columnIndex < lastColumn; columnIndex++) { Cell cell = row.getCell(columnIndex); if (cell == null) { continue; } if (cell.getCellType() == Cell.CELL_TYPE_STRING) { // (9)セルにデータを設定 String key = cell.getStringCellValue(); if (headerMap.containsKey(key)) { setCellValue(cell, headerMap.get(key)); } else if (detailsMap.containsKey(key)) { setCellValues(cell, sheet, detailsMap.get(key)); } } } } } // 完成したワークブックオブジェクトを戻す return wb; }
- SimpleReportCreator -(6)
- SimpleReportCreator -(7)
- SimpleReportCreator -(8)
- SimpleReportCreator -(9)
ワークブックオブジェクトに作成されているワークシート単位(納入先単位)に納品書の作成処理をするようにループ処理をしています。「reportData」は各納入先に対応するデータを保持するオブジェクトです。「headerMap」はテンプレートシートに設定した「$変数名」をkeyとして持ち、「$変数名」への置き換えの値をvalueとして格納しているマップです。「detailsMap」はテンプレートシートに設定した「$変数名[]」をkeyとして持ち、置き換え用の一連の明細の値をオブジェクトの配列形式でvalueに格納しているマップです。
ワークシートごとの行単位に処理を行うためにループ処理をしています。Sheetインターフェースのメソッド「int getLastRowNum()」(戻り値は0-based)を利用して、値または書式/印刷設定がされているセルを含んでいる最終行の位置を取得しています。
行ごとのセル単位に処理を行うためにループ処理をしています。Rowインターフェースのメソッド「short getLastCellNum()」(戻り値は1-based)を利用して、該当の行で、値または書式/印刷設定がされている最も右のセル位置を取得しています。
処理範囲(今回の例では印刷範囲が該当)に含まれるセルで、セルが文字列型の値を格納している場合は、該当のセルがデータを置き換えるために用意されたセル(「$変数名」または「$変数名[]」)かどうかを確認しています。データを置き換えるために用意されたセルと判定された場合は、セルに値を置き換えるためのメソッドを呼んでいます。では次にその箇所のソースを確認していきましょう。
// (10)セルに値を設定 private void setCellValue(Cell cell, Object value) { CellStyle style = cell.getCellStyle(); if (value != null) { if (value instanceof String) { cell.setCellValue((String) value); } else if (value instanceof Number) { Number numValue = (Number) value; if (numValue instanceof Float) { Float floatValue = (Float) numValue; numValue = new Double(String.valueOf(floatValue)); } cell.setCellValue(numValue.doubleValue()); } else if (value instanceof Date) { Date dateValue = (Date) value; cell.setCellValue(dateValue); } else if (value instanceof Boolean) { Boolean boolValue = (Boolean) value; cell.setCellValue(boolValue); } } else { cell.setCellType(Cell.CELL_TYPE_BLANK); cell.setCellStyle(style); } }
- SimpleReportCreator -(10)
セルに設定したいオブジェクトの型に応じてキャストし直し、セルに値を設定しています。
private void setCellValues(Cell baseCell, Sheet sheet, Object[] values) { // (11)開始セルの位置情報を保持 int startRowPosition = baseCell.getRowIndex(); int columnPosition = baseCell.getColumnIndex(); // (12)明細の数分繰返し処理をする for (int i = 0; i < numOfDetails; i++) { // (13)行を取得または生成 Row row = sheet.getRow(startRowPosition + i); if (row == null) { row = sheet.createRow(startRowPosition + i); row.setHeight(sheet.getRow(startRowPosition).getHeight()); } // (14)セルを取得または生成 Cell cell = row.getCell(columnPosition); if (cell == null) { cell = row.createCell(columnPosition); // (15)開始セルの情報をコピー copyCell(baseCell, cell); } // (16)セルに値を設定 setCellValue(cell, values[i]); } }
- SimpleReportCreator -(11)
- SimpleReportCreator -(12)
- SimpleReportCreator -(13)
- SimpleReportCreator -(14)
- SimpleReportCreator -(15)
- SimpleReportCreator -(16)
繰り返し値を設定する箇所の先頭セルの行と列の位置情報を保持しておきます。
明細データの数(この例では納品する商品の種類)分、ループ処理をしています。
明細データの数に合わせて、該当する行オブジェクトを取得します。「納品書」の例では明細行数は11行で固定し、あらかじめ罫線も含めて書式も設定済みのため、該当行を取得した時に「null」が戻ることはありません。明細行数をあらかじめ設定していない場合では取得した行オブジェクトが「null」になることがありますが、その場合にはSheetインターフェースのメソッド「Row createRow(int rownum)」を利用して行を生成し、繰り返し処理の開始行の高さをRowインターフェースのメソッド「void setHeight(short height)」で設定できるようにしています(次にサンプルとして示す「月間売上一覧」で利用します)。
データを設定する箇所のセルオブジェクトを取得します。行と同じ理由で「納品書」の例では「null」が戻ることはありません。明細行数をあらかじめ設定していない場合など、「null」が戻った場合は、Rowインターフェースのメソッド「Cell createCell(int column)」を利用してセルを生成します(次にサンプルとして示す「月間売上一覧」で利用します)。
コピーメソッドを呼び出し、繰り返し開始位置のセルの情報をコピーします(次にサンプルとして示す「月間売上一覧」で利用します)。
メソッドを呼び出して、該当セルに値を設定します。