CellConverterFactorySupport.java

package com.gh.mygreen.xlsmapper.cellconverter;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Optional;

import com.gh.mygreen.xlsmapper.AnnotationInvalidException;
import com.gh.mygreen.xlsmapper.Configuration;
import com.gh.mygreen.xlsmapper.annotation.XlsCellOption;
import com.gh.mygreen.xlsmapper.annotation.XlsDefaultValue;
import com.gh.mygreen.xlsmapper.annotation.XlsFormula;
import com.gh.mygreen.xlsmapper.annotation.XlsTrim;
import com.gh.mygreen.xlsmapper.expression.ExpressionEvaluationException;
import com.gh.mygreen.xlsmapper.fieldaccessor.FieldAccessor;
import com.gh.mygreen.xlsmapper.localization.MessageBuilder;
import com.gh.mygreen.xlsmapper.textformatter.TextFormatter;
import com.gh.mygreen.xlsmapper.textformatter.TextParseException;
import com.gh.mygreen.xlsmapper.util.Utils;

/**
 * {@link CellConverter}を作成するための抽象クラス。
 *
 * @since 2.0
 * @author T.TSUCHIE
 *
 */
public abstract class CellConverterFactorySupport<T>  {

    /**
     * 引数で指定したCellConverterに対して、トリムなどの共通の設定を行う。
     * @param cellConverter 設定を行うCellConverter
     * @param field フィールド情報
     * @param config システム設定情報
     */
    protected void setupCellConverter(final BaseCellConverter<T> cellConverter, final FieldAccessor field, final Configuration config) {

        final TextFormatter<T> textFormatter = createTextFormatter(field, config);
        cellConverter.setTextFormatter(textFormatter);

        // トリムの設定
        final Optional<XlsTrim> trimAnno = field.getAnnotation(XlsTrim.class);
        final boolean trimmed = trimAnno.map(anno -> true).orElse(false);

        cellConverter.setTrimmed(trimmed);

        // 初期値の設定
        final Optional<XlsDefaultValue> defaultValueAnno = field.getAnnotation(XlsDefaultValue.class);
        defaultValueAnno.ifPresent(anno -> {
            String text = Utils.trim(anno.value(), trimmed);

            try {
                T defaultValue = textFormatter.parse(text);
                cellConverter.setDefaultValue(defaultValue, anno.cases());

            } catch(TextParseException e) {
                throw new AnnotationInvalidException(anno, MessageBuilder.create("anno.XlsDefaultValue.failParse")
                        .var("property", field.getNameWithClass())
                        .var("defaultValue", text)
                        .varWithClass("type", field.getType())
                        .format(), e);
            }
        });

        // セルの書式の設定
        final Optional<XlsCellOption> cellOptionAnno = field.getAnnotation(XlsCellOption.class);
        cellOptionAnno.ifPresent(anno -> {
            cellConverter.setShrinktToFit(anno.shrinkToFit());
            cellConverter.setWrapText(anno.wrapText());
            cellConverter.setIndent(anno.indent());
            cellConverter.setHorizontalAlignment(anno.horizontalAlign().poiAlignType());
            cellConverter.setVerticalAlignment(anno.verticalAlign().poiAlignType());
        });

        // 数式の設定
        final Optional<XlsFormula> formulaAnno = field.getAnnotation(XlsFormula.class);
        formulaAnno.ifPresent(anno -> {
            CellFormulaHandler formulaHandler = createCellFormulaHandler(anno, field, config);
            cellConverter.setFormulaHandler(formulaHandler);

        });

        // 各個別の設定
        setupCustom(cellConverter, field, config);

    }

    /**
     * 各個別に、Converterの設定を行う。
     * @param cellConverter 組み立てるCellConverterのインスタンス
     * @param field フィールド情報
     * @param config システム情報
     */
    protected abstract void setupCustom(BaseCellConverter<T> cellConverter, FieldAccessor field, Configuration config);

    /**
     * {@link TextFormatter}のインスタンスを作成する。
     * @param field フィールド情報
     * @param config システム情報
     * @return {@link TextFormatter}のインスタンス
     */
    protected abstract TextFormatter<T> createTextFormatter(FieldAccessor field, Configuration config);

    /**
     * 数式を処理する{@link CellFormulaHandler}を作成する。
     * @param formulaAnno 数式のアノテーション
     * @param field フィールド情報
     * @param config システム情報
     * @return {@link CellFormulaHandler}のインスタンス
     */
    protected CellFormulaHandler createCellFormulaHandler(final XlsFormula formulaAnno, final FieldAccessor field, final Configuration config) {

        if(!formulaAnno.value().isEmpty()) {
            final String formulaExpression = formulaAnno.value();
            try {
                // EL式として正しいか検証する
                config.getFormulaFormatter().interpolate(formulaExpression, Collections.emptyMap());

            } catch(ExpressionEvaluationException e) {
                throw new AnnotationInvalidException(formulaAnno, MessageBuilder.create("anno.attr.invalidEL")
                        .var("property", field.getNameWithClass())
                        .varWithAnno("anno", XlsFormula.class)
                        .var("attrName", "value")
                        .var("attrValue", formulaExpression)
                        .format());
            }

            CellFormulaHandler handler = new CellFormulaHandler(formulaExpression);
            handler.setPrimaryFormula(formulaAnno.primary());
            return handler;

        } else if(!formulaAnno.methodName().isEmpty()) {
            // 戻り値が文字列の数式を返すメソッドを探す
            final Class<?> targetClass = field.getDeclaringClass();
            Method method = null;
            for(Method m : targetClass.getDeclaredMethods()) {
                if(m.getName().equals(formulaAnno.methodName())
                        && m.getReturnType().equals(String.class)) {
                    method = m;
                    break;
                }
            }

            if(method == null) {
                throw new AnnotationInvalidException(formulaAnno, MessageBuilder.create("anno.attr.notFoundMethod")
                        .var("property", field.getNameWithClass())
                        .varWithAnno("anno", XlsFormula.class)
                        .var("attrName", "methodName")
                        .var("attrValue", formulaAnno.methodName())
                        .varWithClass("definedClass", targetClass)
                        .format());
            }

            method.setAccessible(true);
            CellFormulaHandler handler = new CellFormulaHandler(method);
            handler.setPrimaryFormula(formulaAnno.primary());
            return handler;

        } else {
            throw new AnnotationInvalidException(formulaAnno, MessageBuilder.create("anno.attr.required.any")
                    .var("property", field.getNameWithClass())
                    .varWithAnno("anno", XlsFormula.class)
                    .varWithArrays("attrNames", "value", "methodName")
                    .format());
        }

    }

}