PositionSetterFactory.java
package com.gh.mygreen.xlsmapper.fieldaccessor;
import java.awt.Point;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gh.mygreen.xlsmapper.util.ArgUtils;
import com.gh.mygreen.xlsmapper.util.CellPosition;
import com.gh.mygreen.xlsmapper.util.Utils;
/**
* {@link PositionSetter}のインスタンスを作成する
*
* @since 2.0
* @author T.TSUCHIE
*
*/
public class PositionSetterFactory {
private static final Logger log = LoggerFactory.getLogger(PositionSetterFactory.class);
/**
* フィールドの位置情報を設定するためのアクセッサを作成します。
* @param beanClass フィールドが定義されているクラス情報
* @param fieldName フィールドの名称
* @return 位置情報のsetterが存在しない場合は空を返す。
* @throws IllegalArgumentException {@literal beanClass == null or fieldName == null}
* @throws IllegalArgumentException {@literal fieldName.isEmpty() = true}
*/
public Optional<PositionSetter> create(final Class<?> beanClass, final String fieldName) {
ArgUtils.notNull(beanClass, "beanClass");
ArgUtils.notEmpty(fieldName, "fieldName");
// フィールド Map positionsの場合
Optional<PositionSetter> positionSetter = createMapField(beanClass, fieldName);
if(positionSetter.isPresent()) {
return positionSetter;
}
// setter メソッドの場合
positionSetter = createMethod(beanClass, fieldName);
if(positionSetter.isPresent()) {
return positionSetter;
}
// フィールド + positionの場合
positionSetter = createField(beanClass, fieldName);
if(positionSetter.isPresent()) {
return positionSetter;
}
return Optional.empty();
}
/**
* {@link Map}フィールドに位置情報が格納されている場合。
* <p>キーはフィールド名。</p>
* <p>マップの値は、{@link CellPosition}、{@link Point}、{@link org.apache.poi.ss.util.CellAddress}をサポートする。</p>
*
* @param beanClass フィールドが定義してあるクラスのインスタンス
* @param fieldName フィールド名
* @return 位置情報の設定用クラス
*/
private Optional<PositionSetter> createMapField(final Class<?> beanClass, final String fieldName) {
final Field positionsField;
try {
positionsField = beanClass.getDeclaredField("positions");
positionsField.setAccessible(true);
} catch (NoSuchFieldException | SecurityException e) {
// フィールドが見つからない場合は、何もしない。
return Optional.empty();
}
if(!Map.class.isAssignableFrom(positionsField.getType())) {
return Optional.empty();
}
final ParameterizedType type = (ParameterizedType) positionsField.getGenericType();
final Class<?> keyType = (Class<?>) type.getActualTypeArguments()[0];
final Class<?> valueType = (Class<?>) type.getActualTypeArguments()[1];
if(keyType.equals(String.class) && valueType.equals(CellPosition.class)) {
return Optional.of(new PositionSetter() {
@SuppressWarnings("unchecked")
@Override
public void set(final Object beanObj, final CellPosition position) {
ArgUtils.notNull(beanObj, "beanObj");
ArgUtils.notNull(position, "position");
try {
Map<String, CellPosition> positionsMapObj = (Map<String, CellPosition>) positionsField.get(beanObj);
if(positionsMapObj == null) {
positionsMapObj = new LinkedHashMap<>();
positionsField.set(beanObj, positionsMapObj);
}
positionsMapObj.put(fieldName, position);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException("fail access positions field.", e);
}
}
});
} else if(keyType.equals(String.class) && valueType.equals(Point.class)) {
return Optional.of(new PositionSetter() {
@SuppressWarnings("unchecked")
@Override
public void set(final Object beanObj, final CellPosition position) {
ArgUtils.notNull(beanObj, "beanObj");
ArgUtils.notNull(position, "position");
try {
Map<String, Point> positionsMapObj = (Map<String, Point>) positionsField.get(beanObj);
if(positionsMapObj == null) {
positionsMapObj = new LinkedHashMap<>();
positionsField.set(beanObj, positionsMapObj);
}
positionsMapObj.put(fieldName, position.toPoint());
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException("fail access positions field.", e);
}
}
});
} else if(keyType.equals(String.class) && valueType.equals(org.apache.poi.ss.util.CellAddress.class)) {
return Optional.of(new PositionSetter() {
@SuppressWarnings("unchecked")
@Override
public void set(final Object beanObj, final CellPosition position) {
ArgUtils.notNull(beanObj, "beanObj");
ArgUtils.notNull(position, "position");
try {
Map<String, org.apache.poi.ss.util.CellAddress> positionsMapObj = (Map<String, org.apache.poi.ss.util.CellAddress>) positionsField.get(beanObj);
if(positionsMapObj == null) {
positionsMapObj = new LinkedHashMap<>();
positionsField.set(beanObj, positionsMapObj);
}
positionsMapObj.put(fieldName, position.toCellAddress());
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException("fail access positions field.", e);
}
}
});
} else {
// タイプが一致しない場合
log.warn("not match generics type of positions. key type:{}, value type:{}.", keyType.getName(), valueType.getName());
return Optional.empty();
}
}
/**
* setterメソッドによる位置情報を格納する場合。
* <p>{@code set + <フィールド名> + Position}のメソッド名</p>
* <p>引数として、{@link CellPosition}、{@link Point}、{@code int(列番号), int(行番号)}、 {@link org.apache.poi.ss.util.CellAddress}をサポートする。</p>
*
* @param beanClass フィールドが定義してあるクラスのインスタンス
* @param fieldName フィールド名
* @return 位置情報の設定用クラス
*/
private Optional<PositionSetter> createMethod(final Class<?> beanClass, final String fieldName) {
final String positionMethodName = "set" + Utils.capitalize(fieldName) + "Position";
try {
final Method method = beanClass.getDeclaredMethod(positionMethodName, CellPosition.class);
method.setAccessible(true);
return Optional.of(new PositionSetter() {
@Override
public void set(final Object beanObj, final CellPosition position) {
ArgUtils.notNull(beanObj, "beanObj");
ArgUtils.notNull(position, "position");
try {
method.invoke(beanObj, position);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException("fail access position field.", e);
}
}
});
} catch (NoSuchMethodException | SecurityException e) {
}
try {
final Method method = beanClass.getDeclaredMethod(positionMethodName, Point.class);
method.setAccessible(true);
return Optional.of(new PositionSetter() {
@Override
public void set(final Object beanObj, final CellPosition position) {
try {
method.invoke(beanObj, position.toPoint());
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException("fail access position field.", e);
}
}
});
} catch (NoSuchMethodException | SecurityException e) {
}
try {
final Method method = beanClass.getDeclaredMethod(positionMethodName, org.apache.poi.ss.util.CellAddress.class);
method.setAccessible(true);
return Optional.of(new PositionSetter() {
@Override
public void set(final Object beanObj, final CellPosition position) {
ArgUtils.notNull(beanObj, "beanObj");
ArgUtils.notNull(position, "position");
try {
method.invoke(beanObj, position.toCellAddress());
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException("fail access position field.", e);
}
}
});
} catch (NoSuchMethodException | SecurityException e) {
}
try {
final Method method = beanClass.getDeclaredMethod(positionMethodName, Integer.TYPE, Integer.TYPE);
method.setAccessible(true);
return Optional.of(new PositionSetter() {
@Override
public void set(final Object beanObj, final CellPosition position) {
ArgUtils.notNull(beanObj, "beanObj");
ArgUtils.notNull(position, "position");
try {
method.invoke(beanObj, position.getColumn(), position.getRow());
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException("fail access position field.", e);
}
}
});
} catch (NoSuchMethodException | SecurityException e) {
}
return Optional.empty();
}
/**
* フィールドによる位置情報を格納する場合。
* <p>{@code <フィールド名> + Position}のメソッド名</p>
* <p>引数として、{@link CellPosition}、{@link Point}、 {@link org.apache.poi.ss.util.CellAddress}をサポートする。</p>
*
* @param beanClass フィールドが定義してあるクラスのインスタンス
* @param fieldName フィールド名
* @return 位置情報の設定用クラス
*/
private Optional<PositionSetter> createField(final Class<?> beanClass, final String fieldName) {
final String positionFieldName = fieldName + "Position";
final Field positionField;
try {
positionField = beanClass.getDeclaredField(positionFieldName);
positionField.setAccessible(true);
} catch (NoSuchFieldException | SecurityException e) {
return Optional.empty();
}
if(positionField.getType().equals(CellPosition.class)) {
return Optional.of(new PositionSetter() {
@Override
public void set(final Object beanObj, final CellPosition position) {
ArgUtils.notNull(beanObj, "beanObj");
ArgUtils.notNull(position, "position");
try {
positionField.set(beanObj, position);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException("fail access position field.", e);
}
}
});
} else if(positionField.getType().equals(Point.class)) {
return Optional.of(new PositionSetter() {
@Override
public void set(final Object beanObj, final CellPosition position) {
ArgUtils.notNull(beanObj, "beanObj");
ArgUtils.notNull(position, "position");
try {
positionField.set(beanObj, position.toPoint());
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException("fail access position field.", e);
}
}
});
} else if(positionField.getType().equals(org.apache.poi.ss.util.CellAddress.class)) {
return Optional.of(new PositionSetter() {
@Override
public void set(final Object beanObj, final CellPosition position) {
ArgUtils.notNull(beanObj, "beanObj");
ArgUtils.notNull(position, "position");
try {
positionField.set(beanObj, position.toCellAddress());
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException("fail access position field.", e);
}
}
});
}
return Optional.empty();
}
}