FieldFormatterRegistry.java

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

  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.List;
  5. import java.util.Map;

  6. import com.gh.mygreen.xlsmapper.util.ArgUtils;


  7. /**
  8.  * {@link FieldFormatter}を管理するためのクラス。
  9.  * <p>フィールドの値を検証する際に、フィールドの値をフォーマットしてエラーメッセージに埋め込むために使用します。</p>
  10.  *
  11.  * @since 2.0
  12.  * @author T.TSUCHIE
  13.  *
  14.  */
  15. public class FieldFormatterRegistry {
  16.    
  17.     /**
  18.      * クラスタイプで関連づけられたフォーマッタ
  19.      */
  20.     private Map<Class<?>, FieldFormatter<?>> typeMap = new HashMap<>();
  21.    
  22.     /**
  23.      * フィールドパスで関連づけられたフォーマッタ
  24.      */
  25.     private Map<String, FormatterHolder> pathMap = new HashMap<>();
  26.    
  27.     /**
  28.      * 登録されているフォーマッタを初期化する。
  29.      */
  30.     public void init() {
  31.         this.typeMap.clear();
  32.         this.pathMap.clear();
  33.        
  34.        
  35.     }
  36.    
  37.     /**
  38.      * クラスタイプを指定してフォーマッタを登録する。
  39.      * @param <T> 処理対象のタイプ
  40.      * @param requiredType クラスタイプ
  41.      * @param formatter 登録対象のフォーマッタ
  42.      * @throws IllegalArgumentException {@literal requiredType == null or formatter == null}
  43.      */
  44.     public <T> void registerFormatter(final Class<T> requiredType, FieldFormatter<T> formatter) {
  45.         ArgUtils.notNull(requiredType, "requiredType");
  46.         ArgUtils.notNull(formatter, "formatter");
  47.        
  48.         this.typeMap.put(requiredType, formatter);
  49.        
  50.     }
  51.    
  52.     /**
  53.      * フィールドパスとクラスタイプを指定してフォーマッタを登録する。
  54.      * @param <T> 処理対象のタイプ
  55.      * @param fieldPath
  56.      * @param requiredType クラスタイプ
  57.      * @param formatter 登録対象のフォーマッタ
  58.      */
  59.     public <T> void registerFormatter(final String fieldPath, final Class<T> requiredType, FieldFormatter<T> formatter) {
  60.         ArgUtils.notEmpty(fieldPath, "fieldPath");
  61.         ArgUtils.notNull(requiredType, "requiredType");
  62.         ArgUtils.notNull(formatter, "formatter");
  63.        
  64.         this.pathMap.put(fieldPath, new FormatterHolder(requiredType, formatter));
  65.        
  66.     }
  67.    
  68.     /**
  69.      * 指定したクラスタイプに対する{@link FieldFormatter}を取得する。
  70.      * @param <T> クラスタイプ
  71.      * @param requiredType 取得したいクラスタイプ
  72.      * @return 見つからない場合は、nullを返す。
  73.      */
  74.     @SuppressWarnings("unchecked")
  75.     public <T> FieldFormatter<T> getFormatter(final Class<T> requiredType) {
  76.         return (FieldFormatter<T>)typeMap.get(requiredType);
  77.     }
  78.    
  79.     /**
  80.      * 指定したパスとクラスタイプに対する{@link FieldFormatter}を取得する。
  81.      * <p>引数「fieldPath」に完全に一致するフォーマッタを取得します。</p>
  82.      * @param fieldPath フィールドパス。
  83.      * @param requiredType 取得したいクラスタイプ
  84.      * @return 見つからない場合は、nullを返す。
  85.      */
  86.     public <T> FieldFormatter<T> getFormatter(final String fieldPath, final Class<T> requiredType) {
  87.         FormatterHolder holder = pathMap.get(fieldPath);
  88.         return holder != null ? holder.getFormatter(requiredType) : null;
  89.     }
  90.    
  91.     /**
  92.      * 指定したパスとクラスタイプに対する{@link FieldFormatter}を取得する。
  93.      * <p>ただし、リストのインデックスやキーが含まれている場合、引数「fieldPath」からそれらを取り除いた値で比較する。
  94.      *  <br>一致するフィールドに対するフォーマッタが見つからない場合は、タイプのみで比較する。
  95.      * </p>
  96.      * @param fieldPath フィールドパス。
  97.      * @param requiredType 取得したいクラスタイプ
  98.      * @return 見つからない場合は、nullを返す。
  99.      */
  100.     public <T> FieldFormatter<T> findFormatter(final String fieldPath, final Class<T> requiredType) {
  101.        
  102.         // 完全なパスで比較
  103.         FieldFormatter<T> formatter = getFormatter(fieldPath, requiredType);
  104.         if(formatter != null) {
  105.             return formatter;
  106.         }
  107.        
  108.         // インデックスを除去した形式で比較
  109.         final List<String> strippedPaths = new ArrayList<>();
  110.         addStrippedPropertyPaths(strippedPaths, "", fieldPath);
  111.        
  112.         for(String strippedPath : strippedPaths) {
  113.             formatter = getFormatter(strippedPath, requiredType);
  114.             if(formatter != null) {
  115.                 return formatter;
  116.             }
  117.            
  118.         }
  119.        
  120.         // 見つからない場合は、タイプのみで比較した物を取得する
  121.         return getFormatter(requiredType);
  122.        
  123.        
  124.     }
  125.    
  126.     /**
  127.      * パスからリストのインデックス([1])やマップのキー([key])を除去したものを構成する。
  128.      * <p>SpringFrameworkの「PropertyEditorRegistrySupport#addStrippedPropertyPaths(...)」の処理</p>
  129.      * @param strippedPaths 除去したパス
  130.      * @param nestedPath 現在のネストしたパス
  131.      * @param propertyPath 処理対象のパス
  132.      */
  133.     public void addStrippedPropertyPaths(List<String> strippedPaths, String nestedPath, String propertyPath) {
  134.        
  135.         final int startIndex = propertyPath.indexOf('[');
  136.         if (startIndex != -1) {
  137.             final int endIndex = propertyPath.indexOf(']');
  138.             if (endIndex != -1) {
  139.                 final String prefix = propertyPath.substring(0, startIndex);
  140.                 final String key = propertyPath.substring(startIndex, endIndex + 1);
  141.                 final String suffix = propertyPath.substring(endIndex + 1, propertyPath.length());
  142.                
  143.                 // Strip the first key.
  144.                 strippedPaths.add(nestedPath + prefix + suffix);
  145.                
  146.                 // Search for further keys to strip, with the first key stripped.
  147.                 addStrippedPropertyPaths(strippedPaths, nestedPath + prefix, suffix);
  148.                
  149.                 // Search for further keys to strip, with the first key not stripped.
  150.                 addStrippedPropertyPaths(strippedPaths, nestedPath + prefix + key, suffix);
  151.             }
  152.         }
  153.     }
  154.    
  155.     private static class FormatterHolder {
  156.        
  157.         private final Class<?> registerdType;
  158.        
  159.         private final FieldFormatter<?> formatter;
  160.        
  161.         private FormatterHolder(Class<?> registerdType, FieldFormatter<?> formatter) {
  162.             this.registerdType = registerdType;
  163.             this.formatter = formatter;
  164.         }
  165.        
  166.         @SuppressWarnings("unchecked")
  167.         private <T> FieldFormatter<T> getFormatter(final Class<T> requiredType) {
  168.             if(registerdType.isAssignableFrom(requiredType)) {
  169.                 return (FieldFormatter<T>)formatter;
  170.             } else {
  171.                 return null;
  172.             }
  173.         }
  174.     }
  175.    
  176. }