ArrayCellsHandler.java
package com.gh.mygreen.xlsmapper.fieldprocessor.impl;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import com.gh.mygreen.xlsmapper.AnnotationInvalidException;
import com.gh.mygreen.xlsmapper.Configuration;
import com.gh.mygreen.xlsmapper.LoadingWorkObject;
import com.gh.mygreen.xlsmapper.SavingWorkObject;
import com.gh.mygreen.xlsmapper.annotation.ArrayDirection;
import com.gh.mygreen.xlsmapper.annotation.XlsArrayOption;
import com.gh.mygreen.xlsmapper.cellconverter.CellConverter;
import com.gh.mygreen.xlsmapper.cellconverter.TypeBindException;
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.ClassUtils;
import com.gh.mygreen.xlsmapper.util.POIUtils;
import com.gh.mygreen.xlsmapper.util.Utils;
/**
* 配列やリスト形式の要素を処理するためのクラス。
*
* @version 2.1
* @since 2.0
* @author T.TSUCHIE
*
*/
public class ArrayCellsHandler {
private final FieldAccessor field;
private final Object beansObj;
/**
* リストや配列の要素のクラスタイプ
*/
private final Class<?> elementClass;
private final Sheet sheet;
private final Configuration config;
/**
* 見出しの設定 - 見出しがある場合のみ設定する
*/
private String label;
public ArrayCellsHandler(final FieldAccessor field, final Object beansObj, final Class<?> elementClass,
final Sheet sheet, final Configuration config) {
this.field = field;
this.beansObj = beansObj;
this.elementClass = elementClass;
this.sheet = sheet;
this.config = config;
}
/**
* 汎用的にアノテーションの属性にアクセスするためのクラス。
*
*/
private static class AnnotationProxy {
private final Annotation target;
public AnnotationProxy(final Annotation target) {
ArgUtils.notNull(target, "target");
this.target = target;
}
/**
* 対象となるアノテーションを取得する
* @return アノテーションのインスタンス。
*/
public Annotation getTarget() {
return target;
}
/**
* アノテーションのクラスタイプを取得する。
* @return アノテーションのクラスタイプ
*/
public Class<? extends Annotation> annotationType() {
return target.annotationType();
}
/**
* 連続するセルの個数を指定します。
* @return アノテーションの属性「size」の値。存在しない場合、{@literal -1} を返す。
*/
public int size() {
return ClassUtils.getAnnotationAttribute(target, "size", int.class).orElse(-1);
}
/**
* 値のセルが結合しているかどうか考慮するかどうか指定します。
* @return アノテーションの属性「elementMerged」の値
*/
public boolean elementMerged() {
return ClassUtils.getAnnotationAttribute(target, "elementMerged", boolean.class).orElse(true);
}
}
public List<Object> handleOnLoading(final Annotation anno, final CellPosition initPosition, final CellConverter<?> converter,
final LoadingWorkObject work, final ArrayDirection direction) {
final AnnotationProxy annoProxy = new AnnotationProxy(anno);
// 属性sizeの値のチェック
if(annoProxy.size() <= 0) {
throw new AnnotationInvalidException(annoProxy.getTarget(), MessageBuilder.create("anno.attr.min")
.var("property", field.getNameWithClass())
.varWithAnno("anno", annoProxy.annotationType())
.var("attrName", "size")
.var("attrValue", annoProxy.size())
.var("min", 1)
.format());
}
final List<Object> result = new ArrayList<>();
if(direction.equals(ArrayDirection.Horizon)) {
int column = initPosition.getColumn();
int row = initPosition.getRow();
for(int i=0; i < annoProxy.size(); i++) {
final CellPosition cellAddress = CellPosition.of(row, column);
final Cell cell = POIUtils.getCell(sheet, cellAddress);
field.setArrayPosition(beansObj, cellAddress, i);
if(Utils.isNotEmpty(label)) {
field.setArrayLabel(beansObj, label, i);
}
final int comemntIndex = i;
field.getArrayCommentSetter().ifPresent(setter ->
config.getCommentOperator().loadArrayCellComment(setter, cell, beansObj, comemntIndex, field, config));
try {
final Object value = converter.toObject(cell);
result.add(value);
} catch(TypeBindException e) {
work.addTypeBindError(e, cellAddress, field.getName(), label);
if(!config.isContinueTypeBindFailure()) {
throw e;
} else {
// 処理を続ける場合は、nullなどを入れる
result.add(Utils.getPrimitiveDefaultValue(elementClass));
}
}
if(annoProxy.elementMerged()) {
// 結合を考慮する場合
final CellRangeAddress mergedRegion = POIUtils.getMergedRegion(sheet, row, column);
if(mergedRegion != null) {
column += POIUtils.getColumnSize(mergedRegion);
} else {
column++;
}
} else {
column++;
}
}
} else if(direction.equals(ArrayDirection.Vertical)) {
int column = initPosition.getColumn();
int row = initPosition.getRow();
for(int i=0; i < annoProxy.size(); i++) {
final CellPosition cellAddress = CellPosition.of(row, column);
final Cell cell = POIUtils.getCell(sheet, cellAddress);
field.setArrayPosition(beansObj, cellAddress, i);
if(Utils.isNotEmpty(label)) {
field.setArrayLabel(beansObj, label, i);
}
final int comemntIndex = i;
field.getArrayCommentSetter().ifPresent(setter ->
config.getCommentOperator().loadArrayCellComment(setter, cell, beansObj, comemntIndex, field, config));
try {
final Object value = converter.toObject(cell);
result.add(value);
} catch(TypeBindException e) {
work.addTypeBindError(e, cellAddress, field.getName(), label);
if(!config.isContinueTypeBindFailure()) {
throw e;
} else {
// 処理を続ける場合は、nullなどを入れる
result.add(Utils.getPrimitiveDefaultValue(elementClass));
}
}
if(annoProxy.elementMerged()) {
CellRangeAddress mergedRegion = POIUtils.getMergedRegion(sheet, row, column);
if(mergedRegion != null) {
// 結合を考慮する場合
row += POIUtils.getRowSize(mergedRegion);
} else {
row++;
}
} else {
row++;
}
}
} else {
throw new AnnotationInvalidException(annoProxy.getTarget(), MessageBuilder.create("anno.attr.notSupportValue")
.var("property", field.getNameWithClass())
.varWithAnno("anno", annoProxy.annotationType())
.var("attrName", "direction")
.varWithEnum("attrValue", direction)
.format());
}
return result;
}
@SuppressWarnings({"rawtypes", "unchecked"})
public void handleOnSaving(final List<Object> dataList, final Annotation anno, final CellPosition initPosition,
final CellConverter converter, final SavingWorkObject work, final ArrayDirection direction) {
final AnnotationProxy annoProxy = new AnnotationProxy(anno);
final Optional<XlsArrayOption> arrayOption = field.getAnnotation(XlsArrayOption.class);
final XlsArrayOption.OverOperation overOp = arrayOption.map(op -> op.overOpration())
.orElse(XlsArrayOption.OverOperation.Break);
final XlsArrayOption.RemainedOperation remainedOp = arrayOption.map(op -> op.remainedOperation())
.orElse(XlsArrayOption.RemainedOperation.None);
// 属性sizeの値のチェック
if(annoProxy.size() <= 0) {
throw new AnnotationInvalidException(annoProxy.getTarget(), MessageBuilder.create("anno.attr.min")
.var("property", field.getNameWithClass())
.varWithAnno("anno", annoProxy.annotationType())
.var("attrName", "size")
.var("attrValue", annoProxy.size())
.var("min", 1)
.format());
} else if(annoProxy.size() < dataList.size()) {
// 書き込むデータサイズが、アノテーションの指定よりも多く、テンプレート側が不足している場合
if(overOp.equals(XlsArrayOption.OverOperation.Error)) {
throw new AnnotationInvalidException(annoProxy.getTarget(), MessageBuilder.create("anno.attr.arraySizeOver")
.var("property", field.getNameWithClass())
.varWithAnno("anno", annoProxy.annotationType())
.var("attrName", "size")
.var("attrValue", annoProxy.size())
.var("dataSize", dataList.size())
.format());
}
}
if(direction.equals(ArrayDirection.Horizon)) {
int column = initPosition.getColumn();
int row = initPosition.getRow();
for(int i=0; i < annoProxy.size(); i++) {
final CellPosition cellAddress = CellPosition.of(row, column);
field.setArrayPosition(beansObj, cellAddress, i);
if(Utils.isNotEmpty(label)) {
field.setArrayLabel(beansObj, label, i);
}
final int commentIndex = i;
final Cell tempCellComment = POIUtils.getCell(sheet, cellAddress);
field.getArrayCommentGetter().ifPresent(getter -> config.getCommentOperator().saveArrayCellComment(
getter, tempCellComment, beansObj, commentIndex, field, config));
if(i < dataList.size()) {
final Object elementValue = dataList.get(i);
try {
converter.toCell(elementValue, beansObj, sheet, cellAddress);
} catch(TypeBindException e) {
work.addTypeBindError(e, cellAddress, field.getName(), label);
if(!config.isContinueTypeBindFailure()) {
throw e;
}
}
} else {
// 書き込むリストのサイズを超えている場合、値をクリアする
final Cell cell = POIUtils.getCell(sheet, cellAddress);
cell.setBlank();
if(cell.getCellComment() != null) {
cell.removeCellComment();
}
}
final CellRangeAddress mergedRegion = POIUtils.getMergedRegion(sheet, row, column);
if(annoProxy.elementMerged() && mergedRegion != null) {
// 結合を考慮する場合
column += POIUtils.getColumnSize(mergedRegion);
} else if(mergedRegion != null) {
// 結合を考慮しないで、結合されている場合は、解除する
POIUtils.removeMergedRange(sheet, mergedRegion);
column++;
} else {
// 結合されていない場合
column++;
}
if(i >= dataList.size()-1) {
// 書き込むデータサイズが少なく、テンプレート側が余っている場合
if(remainedOp.equals(XlsArrayOption.RemainedOperation.None)) {
// 処理を終了する場合
break;
}
}
}
} else if(direction.equals(ArrayDirection.Vertical)) {
int column = initPosition.getColumn();
int row = initPosition.getRow();
for(int i=0; i < annoProxy.size(); i++) {
final CellPosition cellAddress = CellPosition.of(row, column);
field.setArrayPosition(beansObj, cellAddress, i);
if(Utils.isNotEmpty(label)) {
field.setArrayLabel(beansObj, label, i);
}
final int commentIndex = i;
final Cell tempCellComment = POIUtils.getCell(sheet, cellAddress);
field.getArrayCommentGetter().ifPresent(getter -> config.getCommentOperator().saveArrayCellComment(
getter, tempCellComment, beansObj, commentIndex, field, config));
if(i < dataList.size()) {
final Object elementValue = dataList.get(i);
try {
converter.toCell(elementValue, beansObj, sheet, cellAddress);
} catch(TypeBindException e) {
work.addTypeBindError(e, cellAddress, field.getName(), label);
if(!config.isContinueTypeBindFailure()) {
throw e;
}
}
} else {
// 書き込むリストのサイズを超えている場合、値をクリアする
final Cell cell = POIUtils.getCell(sheet, cellAddress);
cell.setBlank();
if(cell.getCellComment() != null) {
cell.removeCellComment();
}
}
final CellRangeAddress mergedRegion = POIUtils.getMergedRegion(sheet, row, column);
if(annoProxy.elementMerged() && mergedRegion != null) {
// 結合を考慮する場合
row += POIUtils.getRowSize(mergedRegion);
} else if(mergedRegion != null) {
// 結合を考慮しないで、結合されている場合は、解除する
POIUtils.removeMergedRange(sheet, mergedRegion);
row++;
} else {
// 結合されていない場合
row++;
}
if(i >= dataList.size()-1) {
// 書き込むデータサイズが少なく、テンプレート側が余っている場合
if(remainedOp.equals(XlsArrayOption.RemainedOperation.None)) {
// 処理を終了する場合
break;
}
}
}
} else {
throw new AnnotationInvalidException(annoProxy.getTarget(), MessageBuilder.create("anno.attr.notSupportValue")
.var("property", field.getNameWithClass())
.varWithAnno("anno", annoProxy.annotationType())
.var("attrName", "direction")
.varWithEnum("attrValue", direction)
.format());
}
}
/**
* ラベルを設定する
* @param label ラベル
*/
public void setLabel(String label) {
this.label = label;
}
}