ConstraintProcessorHandler.java

package com.github.mygreen.supercsv.cellprocessor;

import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.supercsv.cellprocessor.ift.CellProcessor;

import com.github.mygreen.supercsv.annotation.constraint.CsvConstraint;
import com.github.mygreen.supercsv.builder.BuildCase;
import com.github.mygreen.supercsv.builder.Configuration;
import com.github.mygreen.supercsv.builder.FieldAccessor;
import com.github.mygreen.supercsv.cellprocessor.format.TextFormatter;
import com.github.mygreen.supercsv.util.Utils;

/**
 * フィールドに設定されている制約のアノテーションをハンドリングして、{@link CellProcessor}を作成する。
 * 
 * @since 2.0
 * @author T.TSUCHIE
 *
 */
public class ConstraintProcessorHandler implements ProcessorFactory {
    
    private static Logger logger = LoggerFactory.getLogger(ConstraintProcessorHandler.class);
    
    private final Map<Class<? extends Annotation>, ConstraintProcessorFactory<?>> factoryMap = new HashMap<>();
    
    @SuppressWarnings({"rawtypes", "unchecked"})
    @Override
    public Optional<CellProcessor> create(final Optional<CellProcessor> processor, final FieldAccessor field,
            final TextFormatter<?> formatter, final Configuration config, final BuildCase buildCase, final Class<?>[] groups) {
        
        // 制約のアノテーションを取得する
        final List<Annotation> annos = field.getAnnotationsByGroup(groups).stream()
                .filter(anno -> anno.annotationType().getAnnotation(CsvConstraint.class) != null)
                .filter(anno -> Utils.containsBuildCase(anno, buildCase))
                .collect(Collectors.toList());
        
        Collections.reverse(annos);
        
        Optional<CellProcessor> cp = processor;
        
        for(Annotation anno : annos) {
            
            final CsvConstraint constraintAnno = anno.annotationType().getAnnotation(CsvConstraint.class);
            
            if(factoryMap.containsKey(anno.annotationType())) {
                // 登録済みのものから取得する。
                final ConstraintProcessorFactory factory = factoryMap.get(anno.annotationType());
                cp = factory.create(anno, cp, field, formatter, config);
                
            } else if(constraintAnno.value().length > 0) {
                /*
                 * アノテーション「@CsvConstraint」が指定されている場合、クラスのインスタンスを作成する。
                 * ・定義上、複数指定可能になっているたが、先頭のクラスのみインスタンス化する。
                 */
                for(Class<? extends ConstraintProcessorFactory> factoryClass : constraintAnno.value()) {
                    final ConstraintProcessorFactory factory = 
                            (ConstraintProcessorFactory) config.getBeanFactory().create(factoryClass);
                    cp = factory.create(anno, cp, field, formatter, config);
                }
                
            } else {
                // factoryが見つからない場合
                logger.warn("Not found {} with the annotation {}.", ConstraintProcessorFactory.class.getSimpleName(), anno.getClass());
            }
            
        }
        
        return cp;
    }
    
    /**
     * アノテーションに対する{@link ConstraintProcessorFactory}を登録する。
     *
     * @param <A> アノテーションのタイプ
     * @param anno 関連づけるアノテーション
     * @param factory 制約の{@link CellProcessor}を作成する{@link ConstraintProcessorFactory}の実装。
     */
    public <A extends Annotation> void register(final Class<A> anno, final ConstraintProcessorFactory<A> factory) {
        factoryMap.put(anno, factory);
    }
    
    /**
     * 登録されている{@link ConstraintProcessorFactory}情報を取得する。
     * @return アノテーションと対応する{@link ConstraintProcessorFactory}のマップ。
     */
    public Set<Map.Entry<Class<? extends Annotation>, ConstraintProcessorFactory<?>>> getEntrySet() {
        return factoryMap.entrySet();
    }
    
}