ArrayCellField.java

package com.gh.mygreen.xlsmapper.validation.fieldvalidation;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import com.gh.mygreen.xlsmapper.localization.MessageBuilder;
import com.gh.mygreen.xlsmapper.util.ArgUtils;
import com.gh.mygreen.xlsmapper.util.Utils;
import com.gh.mygreen.xlsmapper.validation.SheetBindingErrors;

/**
 * 配列や{@link Collection}型に対する入力値チェックをするためのクラス。
 *
 * @since 2.0
 * @author T.TSUCHIE
 *
 */
public class ArrayCellField<E> extends CellField<E> {

    /**
     * 要素が必須かどうか
     */
    private boolean requiredElement;

    /**
     * 配列のフィールドに対するValidator
     */
    private List<ArrayFieldValidator<E>> arrayValidators;

    /**
     * 指定されたフィールドの名称に対応するオブジェクトを構築します。
     *
     * @param fieldName フィールド名
     * @param errors エラー情報
     */
    public ArrayCellField(String fieldName, SheetBindingErrors<?> errors) {
        super(fieldName, errors);

        this.requiredElement = false;
        this.arrayValidators = new ArrayList<>();
    }

    /**
     * 要素の値が必須かの設定を行う。
     * @param requiredElement 必須チェックを行いたい場合、「true」を設定する。
     * @return 自身のインスタンス。メソッドチェーンで記述する。
     */
    public ArrayCellField<E> setRequiredElement(final boolean requiredElement) {
        this.requiredElement = requiredElement;
        return this;
    }

    /**
     * 要素の値が必須かチェックを行うかどうか。
     * @return true: 必須入力チェックを行う。
     *               初期値は、非必須(オプション)です。
     */
    public boolean isRequiredElement() {
        return requiredElement;
    }

    /**
     * フィールドの値をリストに変換して取得する。
     * @return
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public List<E> getValueAsList() {

        final Object fieldValue = getValue();
        final Class<?> fieldType = getType();

        if(Collection.class.isAssignableFrom(fieldType)) {
            final Collection<Object> value = (fieldValue == null ? new ArrayList<Object>() : (Collection<Object>) fieldValue);
            final List<Object> list = Utils.convertCollectionToList(value);
            return (List)list;

        } else if(fieldType.isArray()) {
            Class<?> componentType = fieldType.getComponentType();
            final List<Object> list = Utils.asList(fieldValue, componentType);
            return (List)list;

        } else {
            throw new IllegalStateException(MessageBuilder.create("validation.notSupportType")
                    .var("property", getFieldPath())
                    .varWithClass("type", fieldType)
                    .format());
        }

    }

    /**
     * フィールドの値が空かどうか。
     * <p>値がnullまたは、文字列の場合空文字のとき、空と判定する。
     *  <br>配列や{@link Collection}型のときは、要素の数が0のとき空と判定する。
     * </p>
     * @return trueの場合、値は空。
     */
    @SuppressWarnings("rawtypes")
    public boolean isInputEmpty() {

        final Object fieldValue = getValue();
        final Class<?> fieldType = getType();

        if(fieldValue == null) {
            return true;

        } else if(Collection.class.isAssignableFrom(fieldType) && ((Collection) fieldValue).isEmpty()) {
            return true;

        } else if(fieldType.isArray()) {

            Class<?> componentType = fieldType.getComponentType();
            int length = Utils.getArraySize(fieldValue, componentType);
            if(length > 0) {
                return false;
            }

            return true;

        }

        return super.isInputEmpty();
    }

    @Override
    public ArrayCellField<E> validate(final Class<?>... groups) {

        // 既に型変換エラーなどがある場合、値が設定されていないため処理を終了します。
        if(hasErrors()) {
            return this;
        }

        // 必須チェック
        if(!validateForRequired()) {
            return this;
        }

        final List<Class<?>> hints = Arrays.asList(groups);

        // 配列用のValidatorの実行
        if(getArrayValidators() != null && !getArrayValidators().isEmpty()) {
            for(ArrayFieldValidator<E> validator : getArrayValidators()) {
                if(!validator.validate(this, hints)) {
                    return this;
                }
            }
        }

        // 各要素に対するValidatorの実行
        int size = getValueAsList().size();
        if(size > 0) {
            for(int i=0; i < size; i++) {
                final String elementName = String.format("%s[%d]", getFieldName(), i);

                CellField<E> elementField = new CellField<>(elementName, getBindingErrors());
                elementField.setRequired(isRequiredElement())
                    .add(getValidators())
                    .validate(groups);
            }

        }

        return this;
    }

    /**
     * {@link ArrayFieldValidator} を追加する。
     * @param validator validatorのインスタンス。
     * @return 自身のインスタンス。
     * @throws NullPointerException validator is null.
     */
    public ArrayCellField<E> add(final ArrayFieldValidator<E> validator) {
        ArgUtils.notNull(validator, "validator");
        this.arrayValidators.add(validator);

        return this;
    }

    /**
     * 現在の{@link ArrayFieldValidator}を取得する。
     * @return 現在設定されている{@link ArrayFieldValidator}。
     */
    public List<ArrayFieldValidator<E>> getArrayValidators() {
        return arrayValidators;
    }

}