SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

最新POIでOffice Open XML形式のExcelファイルを操作

POIを活用してExcelから帳票を作成

最新POIでOffice Open XML形式のExcelファイルを操作 第4回

  • X ポスト
  • このエントリーをはてなブックマークに追加

POIの利用

 では、ソースを通してPOIの利用箇所を確認していきましょう。まずは本稿の第2回でも説明した「WorkbookFactoryクラス」を利用して、準備したテンプレートファイルを読み込むメソッドの箇所です。

ExcelFileUtil.java
//  (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を利用して納品書を作成します。では、ソースを通して部分ごとに確認していきましょう。

SimpleReportCreator.java
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)
  •  テンプレートシートのシート名である「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)で設定しています。テンプレートシートをシートコピーしただけではコピー後のワークシートに印刷範囲が適切に引き渡せないため、追加した処理です。

  • SimpleReportCreator -(2)
  •  納入先ごとの納品情報を1つのオブジェクト単位として管理している「reportList」という変数名のArrayListに複数の納入先オブジェクトが存在した場合は、ワークブックインターフェースのメソッド「Sheet cloneSheet(int sheetNum)」を利用してテンプレートシートを納入先分シートコピーしています。

  • SimpleReportCreator -(3)
  •  シートコピーして作成したワークシートの名称にWorkbookインターフェースのメソッド「void setSheetName(int sheet, java.lang.String name)」を利用して、納入先の名称を設定しています。

  • SimpleReportCreator -(4)
  •  シートコピーして作成したワークシートにWorkbookインターフェースのメソッド「void setPrintArea(int sheetIndex, java.lang.String reference)」を利用して、テンプレートシートにあらかじめ設定しておいた印刷範囲を設定しています。

  • SimpleReportCreator -(5)
  •  テンプレートシートをWorkbookインターフェースのメソッド「void removeSheetAt(int sheetIndex)」を利用して削除しています。なお、納入先が1つの場合はテンプレートシートに直接データを設定していくため、テンプレートシートの削除はしていません。

SimpleReportCreator.javaの続き
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)
  •  ワークブックオブジェクトに作成されているワークシート単位(納入先単位)に納品書の作成処理をするようにループ処理をしています。「reportData」は各納入先に対応するデータを保持するオブジェクトです。「headerMap」はテンプレートシートに設定した「$変数名」をkeyとして持ち、「$変数名」への置き換えの値をvalueとして格納しているマップです。「detailsMap」はテンプレートシートに設定した「$変数名[]」をkeyとして持ち、置き換え用の一連の明細の値をオブジェクトの配列形式でvalueに格納しているマップです。

  • SimpleReportCreator -(7)
  •  ワークシートごとの行単位に処理を行うためにループ処理をしています。Sheetインターフェースのメソッド「int getLastRowNum()」(戻り値は0-based)を利用して、値または書式/印刷設定がされているセルを含んでいる最終行の位置を取得しています。

  • SimpleReportCreator -(8)
  •  行ごとのセル単位に処理を行うためにループ処理をしています。Rowインターフェースのメソッド「short getLastCellNum()」(戻り値は1-based)を利用して、該当の行で、値または書式/印刷設定がされている最も右のセル位置を取得しています。

  • SimpleReportCreator -(9)
  •  処理範囲(今回の例では印刷範囲が該当)に含まれるセルで、セルが文字列型の値を格納している場合は、該当のセルがデータを置き換えるために用意されたセル(「$変数名」または「$変数名[]」)かどうかを確認しています。データを置き換えるために用意されたセルと判定された場合は、セルに値を置き換えるためのメソッドを呼んでいます。では次にその箇所のソースを確認していきましょう。

SimpleReportCreator.javaの続き
// (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)
  •  セルに設定したいオブジェクトの型に応じてキャストし直し、セルに値を設定しています。

SimpleReportCreator.javaの続き
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)
  •  明細データの数に合わせて、該当する行オブジェクトを取得します。「納品書」の例では明細行数は11行で固定し、あらかじめ罫線も含めて書式も設定済みのため、該当行を取得した時に「null」が戻ることはありません。明細行数をあらかじめ設定していない場合では取得した行オブジェクトが「null」になることがありますが、その場合にはSheetインターフェースのメソッド「Row createRow(int rownum)」を利用して行を生成し、繰り返し処理の開始行の高さをRowインターフェースのメソッド「void setHeight(short height)」で設定できるようにしています(次にサンプルとして示す「月間売上一覧」で利用します)。

  • SimpleReportCreator -(14)
  •  データを設定する箇所のセルオブジェクトを取得します。行と同じ理由で「納品書」の例では「null」が戻ることはありません。明細行数をあらかじめ設定していない場合など、「null」が戻った場合は、Rowインターフェースのメソッド「Cell createCell(int column)」を利用してセルを生成します(次にサンプルとして示す「月間売上一覧」で利用します)。

  • SimpleReportCreator -(15)
  •  コピーメソッドを呼び出し、繰り返し開始位置のセルの情報をコピーします(次にサンプルとして示す「月間売上一覧」で利用します)。

  • SimpleReportCreator -(16)
  •  メソッドを呼び出して、該当セルに値を設定します。

次のページ
月間売上一覧の作成

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
最新POIでOffice Open XML形式のExcelファイルを操作連載記事一覧

もっと読む

この記事の著者

土田 将人(ツチダ マサト)

株式会社ビーブレイクシステムズにて業務システムのパッケージソフト(MA-EYES)の製品開発に従事。英語や会計等、システム開発以外のスキルを活かして官公庁やメーカ系のシステム開発で活躍中。少し前に、パッケージソフトMA-EYESの開発を通して「POI」を知り尽くし、勢い余って本稿の執筆に至る。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/5091 2010/05/18 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング