CellFinder.java
package com.gh.mygreen.xlsmapper.util;
import java.util.Optional;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.usermodel.Sheet;
import com.gh.mygreen.xlsmapper.Configuration;
import com.gh.mygreen.xlsmapper.fieldprocessor.CellNotFoundException;
/**
* 指定したラベルを持つセルを検索するクラス。
*
* @since 2.0
* @author T.TSUCHIE
*
*/
public class CellFinder {
/**
* シート情報
*/
private final Sheet sheet;
/**
* システム設定
*/
private final Configuration config;
/**
* 検索対象のラベル
*/
private final String label;
/**
* 起点となる行番号
* ・指定しない場合は、-1を指定する
*/
private int startRow = -1;
/**
* 起点となる列番号
* ・指定しない場合は、-1を指定する
*/
private int startColumn = -1;
/**
* 起点となる位置を除外するかどうか
*/
private boolean excludeStartPoisition = false;
/**
* 検索する際の条件を組み立てる
* @param sheet 検索対象のシート
* @param label 検索するセルのラベル
* @param config システム設定。
* 設定値 {@link Configuration#isNormalizeLabelText()}、{@link Configuration#isRegexLabelText()}の値によって、
* 検索する際にラベルを正規化、または正規表現により一致するかで判定を行う。
*/
public static CellFinder query(final Sheet sheet, final String label, final Configuration config) {
return new CellFinder(sheet, label, config);
}
private CellFinder(final Sheet sheet, final String label, final Configuration config) {
ArgUtils.notNull(sheet, "sheet");
ArgUtils.notEmpty(label, "label");
ArgUtils.notNull(config, "config");
this.sheet = sheet;
this.label = label;
this.config = config;
}
/**
* 起点なる位置を指定する。
* @param column 列番号(0から始まる)
* @param row 行番号(0から始まる)
* @return 自身のインスタンス。メソッドチェーンとして続ける。
* @throws IllegalArgumentException {@literal column < 0 or row < 0}
*/
public CellFinder startPosition(final int column, final int row) {
ArgUtils.notMin(column, 0, "column");
ArgUtils.notMin(row, 0, "row");
this.startColumn = column;
this.startRow = row;
return this;
}
/**
* 起点なる位置を指定する。
* @param cell 起点とうなるセル
* @return 自身のインスタンス。メソッドチェーンとして続ける。
* @throws NullPointerException {@literal cell == null.}
*/
public CellFinder startPosition(final Cell cell) {
ArgUtils.notNull(cell, "cell");
return startPosition(cell.getColumnIndex(), cell.getRowIndex());
}
/**
* 起点なる位置を指定する。
* @param address セルのアドレス
* @return 自身のインスタンス。メソッドチェーンとして続ける。
* @throws NullPointerException {@literal address == null.}
*/
public CellFinder startPosition(final CellPosition address) {
ArgUtils.notNull(address, "address");
return startPosition(address.getColumn(), address.getRow());
}
/**
* 起点となる列を指定する。
* ただし、行は先頭から検索する。
* @param column 列番号(0から始まる)
* @return 自身のインスタンス。メソッドチェーンとして続ける。
* @throws IllegalArgumentException {@literal column < 0}
*/
public CellFinder startColumn(final int column) {
ArgUtils.notMin(column, 0, "column");
this.startColumn = column;
this.startRow = -1;
return this;
}
/**
* 起点となる行を指定する。
* ただし、列は先頭から検索する。
* @param row 行番号(0から始まる)
* @return 自身のインスタンス。メソッドチェーンとして続ける。
* @throws IllegalArgumentException {@literal row < 0}
*/
public CellFinder startRow(final int row) {
ArgUtils.notMin(row, 0, "row");
this.startColumn = -1;
this.startRow = row;
return this;
}
/**
* 起点となる位置を除外するかどうか。
* @param excludeStartPosition trueの場合、起点となる位置を除外する
* @return 自身のインスタンス。メソッドチェーンとして続ける。
*/
public CellFinder excludeStartPosition(final boolean excludeStartPosition) {
this.excludeStartPoisition = excludeStartPosition;
return this;
}
/**
* 一致する条件のセルを探す。
* @return 見つからない場合は、空を返す。
*/
public Optional<Cell> findOptional() {
return Optional.ofNullable(findCell());
}
/**
* 一致する条件のセルを探す。
* ただし、指定したセルが見つからない場合は、例外{@link CellNotFoundException}をスローする。
* @return
* @throws CellNotFoundException 指定したセルが見つからない場合
*/
public Cell findWhenNotFoundException() {
final Cell cell = findCell();
if(cell == null) {
throw new CellNotFoundException(sheet.getSheetName(), label);
}
return cell;
}
/**
* 一致する条件のセルを探す。
* @param optional セルが見つからない場合に、nullを返すかどうか。
* @return 一致したセルを返す。
* @throws CellNotFoundException 引数「optional=false」のときに、一致するセルが見つからない場合にスローする。
*/
public Cell find(final boolean optional) {
if(optional) {
return findOptional().orElse(null);
} else {
return findWhenNotFoundException();
}
}
/**
* 条件に一致するセルを探す
* @return 見つからない場合は、nullを返す。
*/
private Cell findCell() {
final int rowStart = startRow < 0 ? 0 : startRow;
final int columnStart = startColumn < 0 ? 0 : startColumn;
final int maxRow = POIUtils.getRows(sheet);
for(int i=rowStart; i < maxRow; i++) {
final Row row = sheet.getRow(i);
if(row == null) {
continue;
}
final int maxCol = row.getLastCellNum();;
for(int j=columnStart; j < maxCol; j++) {
if(excludeStartPoisition && includeInStartPosition(j, i)) {
// 開始位置を除外する場合
continue;
}
final Cell cell = row.getCell(j, MissingCellPolicy.CREATE_NULL_AS_BLANK);
final String cellValue = POIUtils.getCellContents(cell, config.getCellFormatter());
if(Utils.matches(cellValue, label, config)) {
return cell;
}
}
}
return null;
}
/**
* 現在の位置が検索対象の開始位置を含むかどうか判定します。
* @param currentColumn 現在の列番号
* @param currentRow 現在の行番号
* @return trueの場合含みます。
*/
private boolean includeInStartPosition(final int currentColumn, final int currentRow) {
if(startColumn >=0 && startRow >= 0
&& currentColumn == startColumn && currentRow == startRow) {
// 行と列の両方の指定がある場合
return true;
} else if(startColumn >= 0 && startRow < 0 && currentColumn == startColumn) {
// 列の指定のみ
return true;
} else if(startColumn < 0 && startRow >= 0 && currentRow == startRow) {
// 行の指定のみ
return true;
}
return false;
}
}