POIUtils.java
- package com.gh.mygreen.xlsmapper.util;
- import java.awt.Point;
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.Date;
- import java.util.List;
- import java.util.TimeZone;
- import java.util.concurrent.atomic.AtomicBoolean;
- import org.apache.poi.common.usermodel.HyperlinkType;
- import org.apache.poi.hssf.model.InternalSheet;
- import org.apache.poi.hssf.model.InternalWorkbook;
- import org.apache.poi.hssf.record.DVRecord;
- import org.apache.poi.hssf.record.Record;
- import org.apache.poi.hssf.record.aggregates.DataValidityTable;
- import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
- import org.apache.poi.hssf.usermodel.HSSFSheet;
- import org.apache.poi.hssf.usermodel.HSSFWorkbook;
- import org.apache.poi.ss.SpreadsheetVersion;
- import org.apache.poi.ss.usermodel.BorderStyle;
- import org.apache.poi.ss.usermodel.Cell;
- import org.apache.poi.ss.usermodel.CellStyle;
- import org.apache.poi.ss.usermodel.CellType;
- import org.apache.poi.ss.usermodel.DataValidation;
- import org.apache.poi.ss.usermodel.DataValidationConstraint;
- import org.apache.poi.ss.usermodel.DataValidationHelper;
- import org.apache.poi.ss.usermodel.Hyperlink;
- import org.apache.poi.ss.usermodel.Name;
- import org.apache.poi.ss.usermodel.Row;
- import org.apache.poi.ss.usermodel.Sheet;
- import org.apache.poi.ss.usermodel.Workbook;
- import org.apache.poi.ss.util.AreaReference;
- import org.apache.poi.ss.util.CellRangeAddress;
- import org.apache.poi.ss.util.CellRangeAddressBase;
- import org.apache.poi.ss.util.CellRangeAddressList;
- import org.apache.poi.ss.util.CellReference;
- import org.apache.poi.xssf.usermodel.XSSFSheet;
- import org.apache.poi.xssf.usermodel.XSSFWorkbook;
- import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDataValidation;
- import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDataValidations;
- import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import com.gh.mygreen.xlsmapper.CellFormatter;
- import com.gh.mygreen.xlsmapper.DefaultCellFormatter;
- import com.github.mygreen.cellformatter.lang.ExcelDateUtils;
- /**
- * Apache POIとJExcel APIの差を埋めるユーティリティクラス。
- *
- * @version 2.0
- * @author T.TSUCHIE
- *
- */
- public class POIUtils {
- private static final Logger logger = LoggerFactory.getLogger(POIUtils.class);
- /** 標準のセルフォーマッター */
- private static CellFormatter defaultCellFormatter = new DefaultCellFormatter();
- /**
- * シートの種類を判定する。
- *
- * @since 2.0
- * @param sheet 判定対象のオブジェクト
- * @return シートの種類。不明な場合はnullを返す。
- * @throws IllegalArgumentException {@literal sheet == null}
- */
- public static SpreadsheetVersion getVersion(final Sheet sheet) {
- ArgUtils.notNull(sheet, "sheet");
- if(sheet instanceof HSSFSheet) {
- return SpreadsheetVersion.EXCEL97;
- } else if(sheet instanceof XSSFSheet) {
- return SpreadsheetVersion.EXCEL2007;
- }
- return null;
- }
- /**
- * シートの最大列数を取得する。
- * <p>{@literal jxl.Sheet.getColumns()}</p>
- * @param sheet シートオブジェクト
- * @return 最大列数
- * @throws IllegalArgumentException {@literal sheet == null.}
- */
- public static int getColumns(final Sheet sheet) {
- ArgUtils.notNull(sheet, "sheet");
- int minRowIndex = sheet.getFirstRowNum();
- int maxRowIndex = sheet.getLastRowNum();
- int maxColumnsIndex = 0;
- for(int i = minRowIndex; i <= maxRowIndex; i++) {
- final Row row = sheet.getRow(i);
- if(row == null) {
- continue;
- }
- final int column = row.getLastCellNum();
- if(column > maxColumnsIndex) {
- maxColumnsIndex = column;
- }
- }
- return maxColumnsIndex;
- }
- /**
- * シートの最大行数を取得する
- *
- * <p>{@literal jxl.Sheet.getRows()}</p>
- * @param sheet シートオブジェクト
- * @return 最大行数
- * @throws IllegalArgumentException {@literal sheet == null.}
- */
- public static int getRows(final Sheet sheet) {
- ArgUtils.notNull(sheet, "sheet");
- return sheet.getLastRowNum() + 1;
- }
- /**
- * シートから任意アドレスのセルを取得する。
- * @since 0.5
- * @param sheet シートオブジェクト
- * @param address アドレス(Point.x=column, Point.y=row)
- * @return セル
- * @throws IllegalArgumentException {@literal sheet == null or address == null.}
- */
- public static Cell getCell(final Sheet sheet, final Point address) {
- ArgUtils.notNull(sheet, "sheet");
- ArgUtils.notNull(address, "address");
- return getCell(sheet, address.x, address.y);
- }
- /**
- * シートから任意アドレスのセルを取得する。
- * @since 1.4
- * @param sheet シートオブジェクト
- * @param address セルのアドレス
- * @return セル
- * @throws IllegalArgumentException {@literal sheet == null or address == null.}
- */
- public static Cell getCell(final Sheet sheet, final CellPosition address) {
- ArgUtils.notNull(sheet, "sheet");
- ArgUtils.notNull(address, "address");
- return getCell(sheet, address.getColumn(), address.getRow());
- }
- /**
- * シートから任意アドレスのセルを取得する。
- *
- * <p>{@literal jxl.Sheet.getCell(int column, int row)}</p>
- * @param sheet シートオブジェクト
- * @param column 列番号(0から始まる)
- * @param row 行番号(0から始まる)
- * @return セル
- * @throws IllegalArgumentException {@literal sheet == null}
- */
- public static Cell getCell(final Sheet sheet, final int column, final int row) {
- ArgUtils.notNull(sheet, "sheet");
- Row rows = sheet.getRow(row);
- if(rows == null) {
- rows = sheet.createRow(row);
- }
- Cell cell = rows.getCell(column);
- if(cell == null) {
- cell = rows.createCell(column, CellType.BLANK);
- }
- return cell;
- }
- /**
- * 任意の行のセルを全て取得する。
- * <p> {@literal jxl.Seet.getRow(int row)}</p>
- * @param sheet シートオブジェクト
- * @param row 行番号(0から始まる)
- * @return 行レコード(カラムの集合)。
- * ただし、シートの最大列数以下の場合、空のセルを補完する。
- * @throws IllegalArgumentException {@literal sheet == null}
- */
- public static Cell[] getRow(final Sheet sheet, final int row) {
- ArgUtils.notNull(sheet, "sheet");
- Row rows = sheet.getRow(row);
- if(rows == null) {
- rows = sheet.createRow(row);
- }
- int maxColumn = getColumns(sheet);
- Cell[] cells = new Cell[maxColumn];
- for(int i=0; i < maxColumn; i++) {
- Cell cell = rows.getCell(i);
- if(cell == null) {
- cell = rows.createCell(i, CellType.BLANK);
- }
- cells[i] = cell;
- }
- return cells;
- }
- /**
- * 任意の列のセルを全て取得する。
- * <p> {@literal jxl.Seet.getColumn(int col)}</p>
- * @param sheet
- * @param col 列番号(0から始まる)
- * @return 列レコード(行の集合)。
- * ただし、シートの最大行数以下の場合、空のセルを補完する。
- * @throws IllegalArgumentException {@literal sheet == null}
- */
- public static Cell[] getColumn(final Sheet sheet, final int col) {
- ArgUtils.notNull(sheet, "sheet");
- int maxRow = getRows(sheet);
- Cell[] cells = new Cell[maxRow];
- for(int i=0; i < maxRow; i++) {
- Row rows = sheet.getRow(i);
- if(rows == null) {
- rows = sheet.createRow(i);
- }
- Cell cell = rows.getCell(col);
- if(cell == null) {
- cell = rows.createCell(col, CellType.BLANK);
- }
- cells[i] = cell;
- }
- return cells;
- }
- /**
- * フォーマッターを指定してセルの値を取得する
- *
- * @param cell
- * @param cellFormatter
- * @return フォーマットした文字列
- * @throws IllegalArgumentException {@literal cell or cellFormatter is null.}
- */
- public static String getCellContents(final Cell cell, final CellFormatter cellFormatter) {
- ArgUtils.notNull(cell, "cell");
- ArgUtils.notNull(cellFormatter, "cellFormatter");
- return cellFormatter.format(cell);
- }
- /**
- * 指定してセルの値が空かどうか判定する。
- * <p>ブランクセルなどの判定は優先的に行う。</p>
- * @param cell
- * @return
- */
- public static boolean isEmptyCellContents(final Cell cell) {
- return isEmptyCellContents(cell, defaultCellFormatter);
- }
- /**
- * フォーマッターを指定してセルの値が空かどうか判定する。
- * <p>ブランクセルなどの判定は優先的に行う。</p>
- * @param cell セル
- * @param cellFormatter セルのフォーマッタ
- * @throws IllegalArgumentException {@literal sheet == null or cellFormatter == null.}
- * @return
- */
- public static boolean isEmptyCellContents(final Cell cell, final CellFormatter cellFormatter) {
- ArgUtils.notNull(cell, "cell");
- ArgUtils.notNull(cellFormatter, "cellFormatter");
- return getCellContents(cell, cellFormatter).isEmpty();
- }
- /**
- * 指定した書式のインデックス番号を取得する。シートに存在しない場合は、新しく作成する。
- * @param sheet シート
- * @param pattern 作成する書式のパターン
- * @return 書式のインデックス番号。
- * @throws IllegalArgumentException {@literal sheet == null.}
- * @throws IllegalArgumentException {@literal pattern == null || pattern.isEmpty().}
- */
- public static short getDataFormatIndex(final Sheet sheet, final String pattern) {
- ArgUtils.notNull(sheet, "sheet");
- ArgUtils.notEmpty(pattern, "pattern");
- return sheet.getWorkbook().getCreationHelper().createDataFormat().getFormat(pattern);
- }
- /**
- * セルに設定されている書式を取得する。
- * @since 1.1
- * @param cell セルのインスタンス。
- * @param cellFormatter セルのフォーマッタ
- * @return 書式が設定されていない場合は、空文字を返す。
- * cellがnullの場合も空文字を返す。
- * 標準の書式の場合も空文字を返す。
- */
- public static String getCellFormatPattern(final Cell cell, final CellFormatter cellFormatter) {
- if(cell == null) {
- return "";
- }
- String pattern = cellFormatter.getPattern(cell);
- if(pattern.equalsIgnoreCase("general")) {
- return "";
- }
- return pattern;
- }
- /**
- * 指定した範囲のセルを結合する。
- * @param sheet
- * @param startCol
- * @param startRow
- * @param endCol
- * @param endRow
- * @return 結合した範囲のアドレス情報
- * @throws IllegalArgumentException {@literal sheet == null}
- */
- public static CellRangeAddress mergeCells(final Sheet sheet, int startCol, int startRow, int endCol, int endRow) {
- ArgUtils.notNull(sheet, "sheet");
- // 結合先のセルの値を空に設定する
- for(int r=startRow; r <= endRow; r++) {
- for(int c=startCol; c <= endCol; c++) {
- if(r == startRow && c == startCol) {
- continue;
- }
- Cell cell = getCell(sheet, c, r);
- cell.setBlank();
- }
- }
- final CellRangeAddress range = new CellRangeAddress(startRow, endRow, startCol, endCol);
- sheet.addMergedRegion(range);
- return range;
- }
- /**
- * 指定したセルのアドレスの結合情報を取得する。
- * @since 0.5
- * @param sheet シート情報
- * @param rowIdx 行番号
- * @param colIdx 列番号
- * @return 結合していない場合nullを返す。
- */
- public static CellRangeAddress getMergedRegion(final Sheet sheet, final int rowIdx, final int colIdx) {
- ArgUtils.notNull(sheet, "sheet");
- final int num = sheet.getNumMergedRegions();
- for(int i=0; i < num; i ++) {
- final CellRangeAddress range = sheet.getMergedRegion(i);
- if(range.isInRange(rowIdx, colIdx)) {
- return range;
- }
- }
- return null;
- }
- /**
- * 指定した範囲の結合を解除する。
- * @param sheet
- * @param mergedRange
- * @return 引数で指定した結合が見つからない場合。
- */
- public static boolean removeMergedRange(final Sheet sheet, final CellRangeAddress mergedRange) {
- ArgUtils.notNull(sheet, "sheet");
- ArgUtils.notNull(mergedRange, "mergedRange");
- final String mergedAddress = mergedRange.formatAsString(sheet.getSheetName(), true);
- final int num = sheet.getNumMergedRegions();
- for(int i=0; i < num; i ++) {
- final CellRangeAddress range = sheet.getMergedRegion(i);
- final String rangeAddress = range.formatAsString(sheet.getSheetName(), true);
- if(rangeAddress.equals(mergedAddress)) {
- sheet.removeMergedRegion(i);
- return true;
- }
- }
- return false;
- }
- /**
- * 領域の列サイズ(横セル数)を計算します。
- *
- * @since 2.0
- * @param region 領域
- * @return 列サイズ(横セル数)。引数がnullの時は、0を返します。
- */
- public static int getColumnSize(final CellRangeAddress region) {
- if(region == null) {
- return 0;
- }
- return region.getLastColumn() - region.getFirstColumn() + 1;
- }
- /**
- * 領域の行サイズ(行セル数)を計算します。
- *
- * @since 2.0
- * @param region 領域
- * @return 行サイズ(行セル数)。引数がnullの時は、0を返します。
- */
- public static int getRowSize(final CellRangeAddress region) {
- if(region == null) {
- return 0;
- }
- return region.getLastRow() - region.getFirstRow() + 1;
- }
- /**
- * 指定した行の下に行を1行追加する
- * @param sheet
- * @param rowIndex 追加する行数
- * @return 追加した行を返す。
- */
- public static Row insertRow(final Sheet sheet, final int rowIndex) {
- ArgUtils.notNull(sheet, "sheet");
- ArgUtils.notMin(rowIndex, 0, "rowIndex");
- // 最終行を取得する
- int lastRow = sheet.getLastRowNum();
- if(lastRow < rowIndex) {
- // データが定義されている範囲害の場合は、行を新たに作成して返す。
- return sheet.createRow(rowIndex);
- }
- sheet.shiftRows(rowIndex, lastRow+1, 1);
- return sheet.createRow(rowIndex);
- }
- /**
- * 指定した行を削除する。
- * <p>削除した行は上に詰める。
- * @since 0.5
- * @param sheet
- * @param rowIndex 削除する行数
- * @return 削除した行
- */
- public static Row removeRow(final Sheet sheet, final int rowIndex) {
- ArgUtils.notNull(sheet, "cell");
- ArgUtils.notMin(rowIndex, 0, "rowIndex");
- final Row row = sheet.getRow(rowIndex);
- if(row == null) {
- // 削除対象の行にデータが何もない場合
- return row;
- }
- sheet.removeRow(row);
- // 上に1つ行をずらす
- int lastRow = sheet.getLastRowNum();
- if(rowIndex +1 > lastRow) {
- return row;
- }
- sheet.shiftRows(rowIndex+1, lastRow, -1);
- return row;
- }
- /**
- * 座標をExcelのアドレス形式'A1'などに変換する
- * @param rowIndex 行インデックス
- * @param colIndex 列インデックス
- * @return
- */
- public static String formatCellAddress(final int rowIndex, final int colIndex) {
- return CellReference.convertNumToColString(colIndex) + String.valueOf(rowIndex+1);
- }
- /**
- * 座標をExcelのアドレス形式'A1'になどに変換する。
- * @param cellAddress セルの位置情報
- * @return
- * @throws IllegalArgumentException address == null.
- */
- public static String formatCellAddress(final Point cellAddress) {
- ArgUtils.notNull(cellAddress, "cellAddress");
- return formatCellAddress(cellAddress.y, cellAddress.x);
- }
- /**
- * セルのアドレス'A1'を取得する。
- * @param cell セル情報
- * @return IllegalArgumentException cell == null.
- */
- public static String formatCellAddress(final Cell cell) {
- ArgUtils.notNull(cell, "cell");
- return CellReference.convertNumToColString(cell.getColumnIndex()) + String.valueOf(cell.getRowIndex()+1);
- }
- /**
- * リンクのアドレスを判定する。
- * @param linkAddress リンクのアドレス(URL)
- * @return 不明な場合は{@link HyperlinkType#NONE}を返す。
- * @throws IllegalArgumentException linkAddress が空文字の場合。
- */
- public static HyperlinkType judgeLinkType(final String linkAddress) {
- ArgUtils.notEmpty(linkAddress, "linkAddress");
- if(linkAddress.matches(".*![\\p{Alnum}]+")) {
- // !A1のアドレスを含むかどうか
- return HyperlinkType.DOCUMENT;
- } else if(linkAddress.matches("[\\p{Alpha}]+[0-9]+")) {
- // A1の通常のアドレスの形式
- return HyperlinkType.DOCUMENT;
- } else if(linkAddress.matches(".+@.+")) {
- // @を含むかどうか
- return HyperlinkType.EMAIL;
- } else if(linkAddress.matches("[\\p{Alpha}]+://.+")) {
- // プロトコル付きかどうか
- return HyperlinkType.URL;
- } else if(linkAddress.matches(".+\\.[\\p{Alnum}]+")) {
- // 拡張子付きかどうか
- return HyperlinkType.FILE;
- } else {
- return HyperlinkType.NONE;
- }
- }
- /**
- * 入力規則の範囲を更新する。
- * @since 0.5
- * @param sheet シート
- * @param oldRegion 更新対象の範囲。
- * @param newRegion 新しい範囲。
- * @return true:更新完了。false:指定した範囲を持つ入力規則が見つからなかった場合。
- */
- public static boolean updateDataValidationRegion(final Sheet sheet,
- final CellRangeAddressList oldRegion, final CellRangeAddressList newRegion) {
- ArgUtils.notNull(sheet, "sheet");
- ArgUtils.notNull(oldRegion, "oldRegion");
- ArgUtils.notNull(newRegion, "newRegion");
- if(sheet instanceof XSSFSheet) {
- final List<String> oldSqref = convertSqref(oldRegion);
- try {
- final XSSFSheet xssfSheet = (XSSFSheet) sheet;
- Field fWorksheet = XSSFSheet.class.getDeclaredField("worksheet");
- fWorksheet.setAccessible(true);
- CTWorksheet worksheet = (CTWorksheet) fWorksheet.get(xssfSheet);
- CTDataValidations dataValidations = worksheet.getDataValidations();
- if(dataValidations == null) {
- return false;
- }
- for(int i=0; i < dataValidations.getCount(); i++) {
- CTDataValidation dv = dataValidations.getDataValidationArray(i);
- // 規則の範囲を比較し、同じならば範囲を書き換える。
- @SuppressWarnings("unchecked")
- List<String> sqref = new ArrayList<>(dv.getSqref());
- if(equalsSqref(sqref, oldSqref)) {
- List<String> newSqref = convertSqref(newRegion);
- dv.setSqref(newSqref);
- // 設定し直す
- dataValidations.setDataValidationArray(i, dv);
- return true;
- }
- }
- return false;
- } catch(Exception e) {
- throw new RuntimeException("fail update DataValidation's Regsion.", e);
- }
- } else if(sheet instanceof HSSFSheet) {
- final HSSFSheet hssfSheet = (HSSFSheet) sheet;
- try {
- Field fWorksheet = HSSFSheet.class.getDeclaredField("_sheet");
- fWorksheet.setAccessible(true);
- InternalSheet worksheet = (InternalSheet) fWorksheet.get(hssfSheet);
- DataValidityTable dvt = worksheet.getOrCreateDataValidityTable();
- // シート内の入力規則のデータを検索して、一致するものがあれば書き換える。
- final AtomicBoolean updated = new AtomicBoolean(false);
- RecordVisitor visitor = new RecordVisitor() {
- @Override
- public void visitRecord(final Record r) {
- if (!(r instanceof DVRecord)) {
- return;
- }
- final DVRecord dvRecord = (DVRecord) r;
- final CellRangeAddressList region = dvRecord.getCellRangeAddress();
- if(equalsRegion(region, oldRegion)) {
- // 一旦既存の範囲を削除する。
- while(region.countRanges() != 0) {
- region.remove(0);
- }
- // 新しい範囲を追加する。
- for(CellRangeAddress newRange : newRegion.getCellRangeAddresses()) {
- region.addCellRangeAddress(newRange);
- }
- updated.set(true);
- return;
- }
- }
- };
- dvt.visitContainedRecords(visitor);
- return updated.get();
- } catch(Exception e) {
- throw new RuntimeException("fail update DataValidation's Regsion.", e);
- }
- } else {
- throw new UnsupportedOperationException("not supported update dava validation's region for type " + sheet.getClass().getName());
- }
- }
- /**
- * CellRangeAddressを文字列形式のリストに変換する。
- * @since 0.5
- * @param region
- * @return
- */
- private static List<String> convertSqref(final CellRangeAddressList region) {
- List<String> sqref = new ArrayList<>();
- for(CellRangeAddress range : region.getCellRangeAddresses()) {
- sqref.add(range.formatAsString());
- }
- return sqref;
- }
- /**
- * 文字列形式のセルの範囲が同じかどうか比較する。
- * @since 0.5
- * @param sqref1
- * @param sqref2
- * @return
- */
- public static boolean equalsSqref(final List<String> sqref1, final List<String> sqref2) {
- if(sqref1.size() != sqref2.size()) {
- return false;
- }
- Collections.sort(sqref1);
- Collections.sort(sqref2);
- final int size = sqref1.size();
- for(int i=0; i < size; i++) {
- if(!sqref1.get(i).equals(sqref2.get(i))) {
- return false;
- }
- }
- return true;
- }
- /**
- * 文字列形式のセルの範囲が同じかどうか比較する。
- * @since 0.5
- * @param region1
- * @param region2
- * @return
- */
- public static boolean equalsRegion(final CellRangeAddressList region1, final CellRangeAddressList region2) {
- return equalsSqref(convertSqref(region1), convertSqref(region2));
- }
- /**
- * テンプレートの入力規則の制約「リスト」を追加する。
- * <p>POI-3.7以上が必要。
- * @param sheet シート
- * @param constraints 制約とするコレクションの中身
- * @param startPosition 開始位置
- * @param endPosition 終了位置
- */
- public static void setupExplicitListConstaint(final Sheet sheet, final Collection<String> constraints,
- final Point startPosition, final Point endPosition) {
- ArgUtils.notNull(sheet, "sheet");
- ArgUtils.notEmpty(constraints, "constraints");
- ArgUtils.notNull(startPosition, "startPosition");
- ArgUtils.notNull(endPosition, "endPosition");
- setupExplicitListConstaint(sheet, constraints.toArray(new String[constraints.size()]),
- startPosition, endPosition);
- }
- /**
- * テンプレートの入力規則の制約「リスト」を追加する。
- * <p>POI-3.7以上が必要。
- * @param sheet シート
- * @param constraints 制約とするリストの中身
- * @param startPosition 開始位置
- * @param endPosition 終了位置
- */
- public static void setupExplicitListConstaint(final Sheet sheet, final String[] constraints,
- final Point startPosition, final Point endPosition) {
- ArgUtils.notNull(sheet, "sheet");
- ArgUtils.notEmpty(constraints, "constraints");
- ArgUtils.notNull(startPosition, "startPosition");
- ArgUtils.notNull(endPosition, "endPosition");
- final DataValidationHelper helper = sheet.getDataValidationHelper();
- final DataValidationConstraint constraint = helper.createExplicitListConstraint(constraints);
- setupConstaint(sheet, constraint, startPosition, endPosition);
- }
- /**
- * テンプレートの入力規則の制約「リスト」を式形式で追加する。
- * <p>POI-3.7以上が必要。
- * @param sheet シート
- * @param listFormula 入力規則の式('='は含まない)
- * @param startPosition 設定するセルの開始位置
- * @param endPosition 設定するセルの終了位置
- */
- public static void setupFormulaListConstaint(final Sheet sheet, final String listFormula,
- final Point startPosition, final Point endPosition) {
- ArgUtils.notNull(sheet, "sheet");
- ArgUtils.notEmpty(listFormula, "listFormula");
- ArgUtils.notNull(startPosition, "startPosition");
- ArgUtils.notNull(endPosition, "endPosition");
- final DataValidationHelper helper = sheet.getDataValidationHelper();
- final DataValidationConstraint constraint = helper.createFormulaListConstraint("=" + listFormula);
- setupConstaint(sheet, constraint, startPosition, endPosition);
- }
- /**
- * 指定した範囲のセルに制約を追加する。
- * <p>POI-3.7以上が必要。
- * @param sheet シート
- * @param constraint 制約
- * @param startPosition 設定するセルの開始位置
- * @param endPosition 設定するセルの終了位置
- */
- public static void setupConstaint(final Sheet sheet, final DataValidationConstraint constraint,
- final Point startPosition, final Point endPosition) {
- ArgUtils.notNull(sheet, "sheet");
- ArgUtils.notNull(constraint, "constraint");
- ArgUtils.notNull(startPosition, "startPosition");
- ArgUtils.notNull(endPosition, "endPosition");
- final DataValidationHelper helper = sheet.getDataValidationHelper();
- final CellRangeAddressList region = new CellRangeAddressList(
- startPosition.y, endPosition.y,
- startPosition.x, endPosition.x
- );
- final DataValidation dataValidation = helper.createValidation(constraint, region);
- sheet.addValidationData(dataValidation);
- }
- /**
- * 指定した範囲の名前を登録する。
- * <p>POI-3.7以上が必要。
- * <p>指定した名前が既に存在する場合は、新しい範囲に書き換える。
- * @param sheet シート
- * @param name 名前
- * @param startPosition 設定するセルの開始位置
- * @param endPosition 設定するセルの終了位置
- * @return
- */
- public static Name defineName(final Sheet sheet, final String name,
- final Point startPosition, final Point endPosition) {
- ArgUtils.notNull(sheet, "sheet");
- ArgUtils.notEmpty(name, "name");
- ArgUtils.notNull(startPosition, "startPosition");
- ArgUtils.notNull(endPosition, "endPosition");
- final Workbook workbook = sheet.getWorkbook();
- Name nameObj = workbook.getName(name);
- if(nameObj == null) {
- nameObj = workbook.createName();
- nameObj.setNameName(name);
- }
- final AreaReference areaRef = buildNameArea(sheet.getSheetName(), startPosition, endPosition,
- sheet.getWorkbook().getSpreadsheetVersion());
- nameObj.setRefersToFormula(areaRef.formatAsString());
- return nameObj;
- }
- /**
- * 名前の範囲の形式を組み立てる。
- * <code>シート名!$A$1:$A:$5</code>
- * @param sheetName シート名
- * @param startPosition 設定するセルの開始位置
- * @param endPosition 設定するセルの終了位置
- * @param sheetVersion シートの形式
- * @return
- */
- public static AreaReference buildNameArea(final String sheetName,
- final Point startPosition, final Point endPosition, SpreadsheetVersion sheetVersion) {
- ArgUtils.notEmpty(sheetName, "sheetName");
- ArgUtils.notNull(startPosition, "startPosition");
- ArgUtils.notNull(endPosition, "endPosition");
- final CellReference firstRefs = new CellReference(sheetName, startPosition.y, startPosition.x, true, true);
- final CellReference lastRefs = new CellReference(sheetName, endPosition.y, endPosition.x, true, true);
- return new AreaReference(firstRefs, lastRefs, sheetVersion);
- }
- /**
- * セルの範囲が重複(交錯)しているかどうか判定する。
- * <p>このメソッドは、POI-3.14で追加されたメソッド{@literal Sheet#intersects(...)}と後方互換性を保つためのもの。</p>
- *
- * @param my
- * @param other
- * @return trueの場合、1つでもセルの範囲が重複している。
- */
- public static boolean intersectsRegion(final CellRangeAddressBase my, final CellRangeAddressBase other) {
- return my.getFirstRow() <= other.getLastRow() &&
- my.getFirstColumn() <= other.getLastColumn() &&
- other.getFirstRow() <= my.getLastRow() &&
- other.getFirstColumn() <= my.getLastColumn();
- }
- /**
- * 日時の開始日が1904年かどうか。
- * 通常は、1900年始まり。
- * @param workbook ワークブック
- * @return trueの場合は、1904年始まり。falseの場合は、1900年始まり。
- */
- public static boolean isDateStart1904(final Workbook workbook) {
- if(workbook instanceof HSSFWorkbook) {
- try {
- Method method = HSSFWorkbook.class.getDeclaredMethod("getWorkbook");
- method.setAccessible(true);
- InternalWorkbook iw = (InternalWorkbook) method.invoke(workbook);
- return iw.isUsing1904DateWindowing();
- } catch(NoSuchMethodException | SecurityException e) {
- logger.warn("fail access method HSSFWorkbook.getWorkbook.", e);
- return false;
- } catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
- logger.warn("fail invoke method HSSFWorkbook.getWorkbook.", e);
- return false;
- }
- } else if(workbook instanceof XSSFWorkbook) {
- try {
- Method method = XSSFWorkbook.class.getDeclaredMethod("isDate1904");
- method.setAccessible(true);
- boolean value = (boolean) method.invoke(workbook);
- return value;
- } catch(NoSuchMethodException | SecurityException e) {
- logger.warn("fail access method XSSFWorkbook.isDate1904.", e);
- return false;
- } catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
- logger.warn("fail invoke method XSSFWorkbook.isDate1904.", e);
- return false;
- }
- } else {
- logger.warn("unknown workbook type.", workbook.getClass().getName());
- }
- return false;
- }
- /**
- * 結合を考慮してセルの罫線(上部)を取得する。
- *
- * @param cell セル
- * @return {@literal BorderStyle}
- * @throws IllegalArgumentException {@literal cell is null.}
- */
- public static BorderStyle getBorderTop(final Cell cell) {
- ArgUtils.notNull(cell, "cell");
- final Sheet sheet = cell.getSheet();
- CellRangeAddress mergedRegion = getMergedRegion(sheet, cell.getRowIndex(), cell.getColumnIndex());
- final Cell target;
- if(mergedRegion == null) {
- // 結合されていない場合
- target = cell;
- } else {
- if(mergedRegion.getFirstRow() == cell.getRowIndex()) {
- // 引数のCellが上部のセルの場合
- target = cell;
- } else {
- target = getCell(sheet, cell.getColumnIndex(), mergedRegion.getFirstRow());
- }
- }
- final CellStyle style = target.getCellStyle();
- if(style == null) {
- return BorderStyle.NONE;
- } else {
- return style.getBorderTop();
- }
- }
- /**
- * 結合を考慮してセルの罫線(下部)を取得する。
- *
- * @param cell セル
- * @return {@literal BorderStyle}
- * @throws IllegalArgumentException {@literal cell is null.}
- */
- public static BorderStyle getBorderBottom(final Cell cell) {
- ArgUtils.notNull(cell, "cell");
- final Sheet sheet = cell.getSheet();
- CellRangeAddress mergedRegion = getMergedRegion(sheet, cell.getRowIndex(), cell.getColumnIndex());
- final Cell target;
- if(mergedRegion == null) {
- // 結合されていない場合
- target = cell;
- } else {
- if(mergedRegion.getLastRow() == cell.getRowIndex()) {
- // 引数のCellが下部のセルの場合
- target = cell;
- } else {
- target = getCell(sheet, cell.getColumnIndex(), mergedRegion.getLastRow());
- }
- }
- final CellStyle style = target.getCellStyle();
- if(style == null) {
- return BorderStyle.NONE;
- } else {
- return style.getBorderBottom();
- }
- }
- /**
- * 結合を考慮してセルの罫線(左部)を取得する。
- *
- * @param cell セル
- * @return {@literal BorderStyle}
- * @throws IllegalArgumentException {@literal cell is null.}
- */
- public static BorderStyle getBorderRight(final Cell cell) {
- ArgUtils.notNull(cell, "cell");
- final Sheet sheet = cell.getSheet();
- CellRangeAddress mergedRegion = getMergedRegion(sheet, cell.getRowIndex(), cell.getColumnIndex());
- final Cell target;
- if(mergedRegion == null) {
- // 結合されていない場合
- target = cell;
- } else {
- if(mergedRegion.getLastColumn() == cell.getColumnIndex()) {
- // 引数のCellが右部のセルの場合
- target = cell;
- } else {
- target = getCell(sheet, mergedRegion.getLastColumn(), cell.getRowIndex());
- }
- }
- final CellStyle style = target.getCellStyle();
- if(style == null) {
- return BorderStyle.NONE;
- } else {
- return style.getBorderRight();
- }
- }
- /**
- * 結合を考慮してセルの罫線(右部)を取得する。
- *
- * @param cell セル
- * @return {@literal BorderStyle}
- * @throws IllegalArgumentException {@literal cell is null.}
- */
- public static BorderStyle getBorderLeft(final Cell cell) {
- ArgUtils.notNull(cell, "cell");
- final Sheet sheet = cell.getSheet();
- CellRangeAddress mergedRegion = getMergedRegion(sheet, cell.getRowIndex(), cell.getColumnIndex());
- final Cell target;
- if(mergedRegion == null) {
- // 結合されていない場合
- target = cell;
- } else {
- if(mergedRegion.getFirstColumn() == cell.getColumnIndex()) {
- // 引数のCellが左部のセルの場合
- target = cell;
- } else {
- target = getCell(sheet, mergedRegion.getFirstColumn(), cell.getRowIndex());
- }
- }
- final CellStyle style = target.getCellStyle();
- if(style == null) {
- return BorderStyle.NONE;
- } else {
- return style.getBorderLeft();
- }
- }
- /**
- * ハイパーリンクを取得する。
- * <p>結合されているセルの場合にも対応。
- * @param cell
- * @return 見つからない場合は、nullを返す。
- * @throws IllegalArgumentException {@literal cell is null.}
- */
- public static Hyperlink getHyperlink(final Cell cell) {
- ArgUtils.notNull(cell, "cell");
- Hyperlink link = cell.getHyperlink();
- if(link != null) {
- return link;
- }
- final Sheet sheet = cell.getSheet();
- CellRangeAddress mergedRange = getMergedRegion(sheet, cell.getRowIndex(), cell.getColumnIndex());
- if(mergedRange == null) {
- return null;
- }
- for(Hyperlink item : sheet.getHyperlinkList()) {
- if(item.getFirstRow() == mergedRange.getFirstRow()
- && item.getFirstColumn() == mergedRange.getFirstColumn()) {
- return item;
- }
- }
- return null;
- }
- /**
- * {@literal 1900-01-01 00:00:00.000}の時間(単位はミリ秒)。
- * <p>Excelは設定により、1900年始まりか1904年始まりか指定できるため、その基準値として利用する。
- */
- public static final long MILLISECONDS_19000101_END = ExcelDateUtils.parseDate("1900-01-01 23:59:54.999").getTime();
- /**
- * セルに日時を設定する。
- * <p>1900年1月0日となる経過時間指定の場合は、POIのバグにより設定できあいため、数値として設定する。</p>
- *
- * @param cell 設定するセル
- * @param date セルに設定する日時
- * @param dateStart1904 1904年始まりの設定のシートかどうか
- */
- public static void setCellValueAsDate(Cell cell, Date date, boolean dateStart1904) {
- ArgUtils.notNull(cell, "cell");
- ArgUtils.notNull(date, "date");
- if(dateStart1904) {
- // 1904年始まりの場合は、そのまま設定する
- cell.setCellValue(date);
- } else {
- long timemills = date.getTime();
- if(timemills <= MILLISECONDS_19000101_END) {
- // 1900年1月0日の場合は、数値に変換してから設定する
- // タイムゾーンを除去する
- Date strip = new Date(date.getTime() + TimeZone.getDefault().getRawOffset());
- double num = ExcelDateUtils.convertExcelNumber(strip, dateStart1904);
- cell.setCellValue(num);
- } else {
- cell.setCellValue(date);
- }
- }
- }
- }