SheetErrorFormatter.java

package com.gh.mygreen.xlsmapper.validation;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import com.gh.mygreen.xlsmapper.localization.MessageInterpolator;
import com.gh.mygreen.xlsmapper.localization.MessageResolver;
import com.gh.mygreen.xlsmapper.localization.ResourceBundleMessageResolver;
import com.gh.mygreen.xlsmapper.util.ArgUtils;
import com.gh.mygreen.xlsmapper.util.Utils;


/**
 * シート用のエラーオブジェクトを解釈して、メッセージに変換するクラス。
 * <p>オブジェクトの種類ごとに、デフォルトメッセージ変数が利用できます。</p>
 * <ul>
 *  <li>'fieldLabel':フィールドのラベル。メッセージ用のプロパティファイルにフィールド名を定義している場合、自動的に設定されます。</li>
 *  <li>'objectLabel':オブジェクトのラベル。メッセージ用のプロパティファイルにオブジェクト名を定義している場合、自動的に設定されます。</li>
 *  <li>'label':ラベル変数'label'が指定されていない場合、エラー対象のオブジェクト名またはフィールド名から自動的に決定されます。</li>
 *  <li>'sheetName':シート用のエラーの場合、シート名が設定されます。</li>
 *  <li>'cellAddress':シート用のフィールドエラーの場合、セルのアドレスが設定されます。'A1'のような形式になります。</li>
 * </ul>
 * 
 * @version 2.0
 * @author T.TSUCHIE
 *
 */
public class SheetErrorFormatter {
    
    private MessageResolver messageResolver = new ResourceBundleMessageResolver();
    
    private MessageInterpolator messageInterporlator = new MessageInterpolator();
    
    private MessageCodeGenerator messageCodeGenerator = new MessageCodeGenerator();
    
    public SheetErrorFormatter() {
        
    }
    
    /**
     * エラーオブジェクトのリストをメッセージにフォーマットする。
     * @param errors 変換対象のエラーオブジェクト。
     * @return
     * @throws IllegalArgumentException errors == null.
     */
    public List<String> format(final Collection<ObjectError> errors) {
        ArgUtils.notNull(errors, "errors");
        
        final List<String> messageList = new ArrayList<>();
        for(ObjectError error : errors) {
            messageList.add(format(error));
        }
        
        return messageList;
    }
    
    /**
     * エラーオブジェクトをメッセージにフォーマットする。
     * @param error エラーオブジェクト
     * @return メッセージ
     * @throws IllegalArgumentException {@literal error == null.}
     */
    public String format(final ObjectError error) {
        ArgUtils.notNull(error, "error");
        
        final Map<String, Object> vars = new HashMap<>();
        vars.putAll(error.getVariables());
        
        if(error instanceof FieldError) {
            // フィールドエラーのメッセージを処理する
            
            final FieldError fieldError = (FieldError) error;
            final String[] labelCode = messageCodeGenerator.generateFieldNameCodes(fieldError.getObjectName(), fieldError.getField());
            
            try {
                vars.put("fieldLabel", getMessage(labelCode, null));
            } catch(Throwable e) {
            }
            
            final Optional<String> label = error.getLabelAsOptional();
            if(label.isPresent()) {
                vars.put("label", label.get());
            } else {
                try {
                    vars.put("label", getMessage(labelCode, null));
                } catch(Throwable e) {
                }
            }
            
            try {
                // 親のラベル名を取得する
                String[] parentCode = messageCodeGenerator.generateParentNameCodes(fieldError.getObjectName(), fieldError.getField());
                vars.put("parentLabel", getMessage(parentCode, null));
            } catch(Throwable e) {
            }
            
            try {
                String[] objectCode = messageCodeGenerator.generateObjectNameCodes(fieldError.getObjectName());
                vars.put("objectLabel", getMessage(objectCode, null));
            } catch(Throwable e) {
                
            }
            
            fieldError.getSheetName().ifPresent(s -> vars.put("sheetName", s));
            fieldError.getAddressAsOptional().ifPresent(a -> vars.put("cellAddress", a));
            
        } else {
            // オブジェクトエラーのメッセージを処理する。
            
            final String[] labelCode = messageCodeGenerator.generateObjectNameCodes(error.getObjectName());
            
            final Optional<String> label = error.getLabelAsOptional();
            if(label.isPresent()) {
                vars.put("label", label.get());
            } else {
                try {
                    vars.put("label", getMessage(labelCode, null));
                } catch(Throwable e) {
                }
            }
            
            try {
                String[] objectCode = messageCodeGenerator.generateObjectNameCodes(error.getObjectName());
                vars.put("objectLabel", getMessage(objectCode, null));
            } catch(Throwable e) {
                
            }
            
            error.getSheetName().ifPresent(s -> vars.put("sheetName", s));
            
        }
        
        final String message = getMessage(error.getCodes(), error.getDefaultMessage());
        return messageInterporlator.interpolate(message, vars, true, messageResolver);
        
    }
    
    /**
     * 指定した引数の候補からメッセージを取得する。
     * @param codes メッセージコードの候補
     * @param defaultMessage メッセージコードが見つからない場合のメッセージ
     * @return メッセージ
     * @throws RuntimeException メッセージコード 'codes' で指定したメッセージキーが見つからない場合。
     */
    private String getMessage(final String[] codes, final Optional<String> defaultMessage) {
        for(String code : codes) {
            try {
                final Optional<String> message = messageResolver.getMessage(code);
                if(message.isPresent()) {
                    return message.get();
                }
                
            } catch(Throwable e) {
                continue;
            }
        }
        
        if(defaultMessage.isPresent()) {
            return defaultMessage.get();
        }
        
        throw new RuntimeException(String.format("not found message code [%s].", Utils.join(codes, ",")));
    }
    
    public MessageResolver getMessageResolver() {
        return messageResolver;
    }
    
    public void setMessageResolver(MessageResolver messageResolver) {
        this.messageResolver = messageResolver;
    }
    
    public MessageInterpolator getMessageInterporlator() {
        return messageInterporlator;
    }
    
    public void setMessageInterporlator(MessageInterpolator messageInterporlator) {
        this.messageInterporlator = messageInterporlator;
    }
    
    public MessageCodeGenerator getMessageCodeGenerator() {
        return messageCodeGenerator;
    }
    
    public void setMessageCodeGenerator(MessageCodeGenerator messageCodeGenerator) {
        this.messageCodeGenerator = messageCodeGenerator;
    }
}