AbstractTemporalProcessorBuilder.java

package com.github.mygreen.supercsv.builder.time;

import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.time.temporal.TemporalAccessor;
import java.util.Locale;
import java.util.Optional;
import java.util.TimeZone;

import com.github.mygreen.supercsv.annotation.constraint.CsvDateTimeRange;
import com.github.mygreen.supercsv.annotation.constraint.CsvDateTimeMin;
import com.github.mygreen.supercsv.annotation.constraint.CsvDateTimeMax;
import com.github.mygreen.supercsv.annotation.format.CsvDateTimeFormat;
import com.github.mygreen.supercsv.builder.AbstractProcessorBuilder;
import com.github.mygreen.supercsv.builder.Configuration;
import com.github.mygreen.supercsv.builder.FieldAccessor;
import com.github.mygreen.supercsv.cellprocessor.constraint.DateTimeRangeFactory;
import com.github.mygreen.supercsv.cellprocessor.constraint.DateTimeMinFactory;
import com.github.mygreen.supercsv.cellprocessor.constraint.DateTimeMaxFactory;
import com.github.mygreen.supercsv.cellprocessor.format.TemporalFormatWrapper;
import com.github.mygreen.supercsv.cellprocessor.format.TextFormatter;
import com.github.mygreen.supercsv.util.Utils;


/**
 * JSR-310 'Date and Time API' の{@link TemporalAccessor}のテンプレートクラス。
 * <p>基本的に、{@link TemporalAccessor}のサブクラスのビルダは、このクラスを継承して作成する。</p>
 * 
 * @version 2.0
 * @since 1.2
 * @author T.TSUCHIE
 *
 */
public abstract class AbstractTemporalProcessorBuilder<T extends TemporalAccessor & Comparable<? super T>>
        extends AbstractProcessorBuilder<T> {
    
    public AbstractTemporalProcessorBuilder() {
        super();
    }
    
    @Override
    protected void init() {
        super.init();
        
        // 制約のアノテーションの追加
        registerForConstraint(CsvDateTimeRange.class, new DateTimeRangeFactory<>());
        registerForConstraint(CsvDateTimeMin.class, new DateTimeMinFactory<>());
        registerForConstraint(CsvDateTimeMax.class, new DateTimeMaxFactory<>());
        
    }
    
    /**
     * 変換規則から、{@link DateTimeFormatter}のインスタンスを作成する。
     * <p>アノテーション{@link CsvDateTimeFormat}が付与されていない場合は、各種タイプごとの標準の書式で作成する。</p>
     * @param field フィールド情報
     * @param config システム設定
     * @return {@link DateTimeFormatter}のインスタンス。
     */
    protected DateTimeFormatter createFormatter(final FieldAccessor field, final Configuration config) {
        
        final Optional<CsvDateTimeFormat> formatAnno = field.getAnnotation(CsvDateTimeFormat.class);
        if(!formatAnno.isPresent()) {
            return DateTimeFormatter.ofPattern(getDefaultPattern());
        }
        
        String pattern = formatAnno.get().pattern();
        if(pattern.isEmpty()) {
            pattern = getDefaultPattern();
        }
        
        final ResolverStyle style = formatAnno.get().lenient() ? ResolverStyle.LENIENT : ResolverStyle.STRICT;
        final Locale locale = Utils.getLocale(formatAnno.get().locale());
        final ZoneId zone = formatAnno.get().timezone().isEmpty() ? ZoneId.systemDefault()
                : TimeZone.getTimeZone(formatAnno.get().timezone()).toZoneId();
        
        return DateTimeFormatter.ofPattern(pattern, locale)
                .withResolverStyle(style)
                .withZone(zone);
        
    }
    
    @SuppressWarnings("unchecked")
    @Override
    protected TextFormatter<T> getDefaultFormatter(final FieldAccessor field, final Configuration config) {
        
        final Optional<CsvDateTimeFormat> formatAnno = field.getAnnotation(CsvDateTimeFormat.class);
        final String pattern = getPattern(field);
        final DateTimeFormatter formatter = createFormatter(field, config);
        
        final TemporalFormatWrapper<T> wrapper = new TemporalFormatWrapper<>(formatter, (Class<T>)field.getType());
        wrapper.setPattern(pattern);
        formatAnno.ifPresent(a -> wrapper.setValidationMessage(a.message()));
        return wrapper;
        
    }
    
    /**
     * アノテーション{@link CsvDateTimeFormat}が付与されている場合は、パターンを取得する。
     * <p>ただし、アノテーションが付与されていない場合は、{@link #getDefaultPattern()}の値を返す。</p>
     * <p>{@link DateTimeFormatter}のインスタンスからは、元となったパターンは直接取得できないため、
     *    別途取得して利用するために使用する。</p>
     * @param field フィールド情報。
     * @return 日時の書式パターン。
     */
    protected String getPattern(final FieldAccessor field) {
        return field.getAnnotation(CsvDateTimeFormat.class)
                .map(a -> a.pattern())
                .filter(p -> !p.isEmpty())
                .orElse(getDefaultPattern());
    }
    
    /**
     * 変換規則用のアノテーションが定義されていないときの標準の書式を取得する。
     * 
     * @return {@link DateTimeFormatter}で解析可能な日時の書式。
     */
    protected abstract String getDefaultPattern();
    
}