CellFormulaHandler.java
package com.gh.mygreen.xlsmapper.cellconverter;
import java.awt.Point;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.apache.poi.ss.formula.FormulaParseException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellReference;
import com.gh.mygreen.xlsmapper.Configuration;
import com.gh.mygreen.xlsmapper.XlsMapperException;
import com.gh.mygreen.xlsmapper.fieldaccessor.FieldAccessor;
import com.gh.mygreen.xlsmapper.localization.MessageBuilder;
import com.gh.mygreen.xlsmapper.util.ArgUtils;
import com.gh.mygreen.xlsmapper.util.CellPosition;
import com.gh.mygreen.xlsmapper.util.Utils;
/**
* セルの数式を処理する
*
* @since 2.0
* @author T.TSUCHIE
*
*/
public class CellFormulaHandler {
/**
* 数式を直接指定している場合
*/
private Optional<String> formula = Optional.empty();
/**
* 数式を取得するメソッドを指定している場合
*/
private Optional<Method> method = Optional.empty();
/**
* セルの値が設定済みの時に、数式の設定を優先するかどうか。
*/
private boolean primaryFormula;
/**
* 数式を直接指定する場合
* @param formula 数式。EL式で評価可能な形式。
* @throws IllegalArgumentException {@literal formula == null or empty.}
*/
public CellFormulaHandler(final String formula) {
ArgUtils.notEmpty(formula, "formula");
this.formula = Optional.of(formula);
}
/**
* 数式を取得するメソッドを指定する場合
* @param method 数式を取得するためのメソッド
* @throws IllegalArgumentException {@literal method == null.}
*/
public CellFormulaHandler(final Method method) {
ArgUtils.notNull(method, "method");
this.method = Optional.of(method);
}
/**
* セルに数式を設定する
* @param field フィールド情報
* @param config システム情報
* @param cell セル情報
* @param targetBean 処理対象のフィールドが定義されているクラスのインスタンス。
* @throws ConversionException 数式の解析に失敗した場合。
*/
public void handleFormula(final FieldAccessor field, final Configuration config, final Cell cell, final Object targetBean) {
ArgUtils.notNull(field, "field");
ArgUtils.notNull(config, "config");
ArgUtils.notNull(cell, "cell");
final String evaluatedFormula = createFormulaValue(config, cell, targetBean);
if(Utils.isEmpty(evaluatedFormula)) {
cell.setBlank();
return;
}
try {
cell.setCellFormula(evaluatedFormula);
} catch(FormulaParseException e) {
// 数式の解析に失敗した場合
String message = MessageBuilder.create("cell.failParseFormula")
.var("property", field.getNameWithClass())
.var("cellAddress", CellPosition.of(cell).toString())
.var("formula", evaluatedFormula)
.format();
throw new ConversionException(message, e, field.getType());
}
}
/**
* Excelの式を組み立てる。
* @param config システム情報設定
* @param cell セル情報
* @param targetBean 処理対象のフィールドが定義されているクラスのインスタンス。
* @return 組み立てた数式
*/
public String createFormulaValue(final Configuration config, final Cell cell, final Object targetBean) {
if(formula.isPresent()) {
final Map<String, Object> vars = new HashMap<>();
vars.put("rowIndex", cell.getRowIndex());
vars.put("columnIndex", cell.getColumnIndex());
vars.put("rowNumber", cell.getRowIndex()+1);
vars.put("columnNumber", cell.getColumnIndex()+1);
vars.put("columnAlpha", CellReference.convertNumToColString(cell.getColumnIndex()));
vars.put("address", CellPosition.of(cell).formatAsString());
vars.put("targetBean", targetBean);
vars.put("cell", cell);
return config.getFormulaFormatter().interpolate(formula.get(), vars);
} else if(method.isPresent()) {
// メソッドの引数の組み立て
final Class<?>[] paramTypes = method.get().getParameterTypes();
final Object[] paramValues = new Object[paramTypes.length];
for(int i=0; i < paramTypes.length; i++) {
if(Cell.class.isAssignableFrom(paramTypes[i])) {
paramValues[i] = cell;
} else if(CellPosition.class.isAssignableFrom(paramTypes[i])) {
paramValues[i] = CellPosition.of(cell);
} else if(Point.class.isAssignableFrom(paramTypes[i])) {
paramValues[i] = CellPosition.of(cell).toPoint();
} else if(org.apache.poi.ss.util.CellAddress.class.isAssignableFrom(paramTypes[i])) {
paramValues[i] = CellPosition.of(cell).toCellAddress();
} else if(Sheet.class.isAssignableFrom(paramTypes[i])) {
paramValues[i] = cell.getSheet();
} else if(Configuration.class.isAssignableFrom(paramTypes[i])) {
paramValues[i] = config;
} else {
paramValues[i] = null;
}
}
try {
return (String) method.get().invoke(targetBean, paramValues);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
final Class<?> targetClass = targetBean.getClass();
final Throwable t = e.getCause() == null ? e : e.getCause();
throw new XlsMapperException(
String.format("Fail execute method '%s#%s'.", targetClass.getName(), method.get().getName()),
t);
}
} else {
// 数式や対応するメソッドがない場合
throw new IllegalStateException("not found for formula or method.");
}
}
/**
* セルの値が設定済みの時に、数式の設定を優先するかどうか。
*/
public boolean isPrimaryFormula() {
return primaryFormula;
}
/**
* セルの値が設定済みの時に、数式の設定を優先するかどうか。
* @param primaryFormula 数式の設定を優先するかどうか
*/
public void setPrimaryFormula(boolean primaryFormula) {
this.primaryFormula = primaryFormula;
}
}