SheetBindingErrors.java

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

  2. import java.util.ArrayList;
  3. import java.util.Collection;
  4. import java.util.Collections;
  5. import java.util.List;
  6. import java.util.Optional;
  7. import java.util.Stack;
  8. import java.util.stream.Collectors;

  9. import com.gh.mygreen.xlsmapper.util.PropertyTypeNavigator;
  10. import com.gh.mygreen.xlsmapper.util.PropertyValueNavigator;
  11. import com.gh.mygreen.xlsmapper.util.Utils;
  12. import com.gh.mygreen.xlsmapper.validation.fieldvalidation.FieldFormatter;
  13. import com.gh.mygreen.xlsmapper.validation.fieldvalidation.FieldFormatterRegistry;
  14. import com.github.mygreen.cellformatter.lang.ArgUtils;


  15. /**
  16.  * 1シート分のエラー情報を管理するクラス。
  17.  *
  18.  * @param <P> シートにマッピングするクラスタイプ
  19.  * @version 2.0
  20.  * @author T.TSUCHIE
  21.  *
  22.  */
  23. public class SheetBindingErrors<P> {
  24.    
  25.     /** パスの区切り文字 */
  26.     public static final String PATH_SEPARATOR = ".";
  27.    
  28.     /**
  29.      * 検証対象のオブジェクト。
  30.      * ・ルートオブジェクト
  31.      */
  32.     private final P target;
  33.    
  34.     /**
  35.      * オブジェクト名
  36.      */
  37.     private final String objectName;
  38.    
  39.     /**
  40.      * シート名
  41.      */
  42.     private String sheetName;
  43.    
  44.     /**
  45.      * シートのインデックス
  46.      */
  47.     private int sheetIndex = -1;
  48.    
  49.     /**
  50.      * 現在のパス。
  51.      * キャッシュ用。
  52.      */
  53.     private String currentPath;
  54.    
  55.     /**
  56.      * 検証対象のオブジェクトの現在のパス
  57.      */
  58.     private Stack<String> nestedPathStack = new Stack<>();
  59.    
  60.     /**
  61.      * エラーオブジェクト
  62.      */
  63.     private final List<ObjectError> errors = new ArrayList<>();
  64.    
  65.     /**
  66.      * フィールドの値のフォーマッタの管理クラス
  67.      */
  68.     private FieldFormatterRegistry fieldFormatterRegistry = new FieldFormatterRegistry();
  69.    
  70.     /** エラーコードの候補を生成するクラス */
  71.     private MessageCodeGenerator messageCodeGenerator = new MessageCodeGenerator();
  72.    
  73.     /**
  74.      * プロパティ式から、値を取得する。
  75.      * ・private/protectedなどのフィールドにもアクセス可能にする。
  76.      */
  77.     private final PropertyValueNavigator propertyValueNavigator = new PropertyValueNavigator();
  78.     {
  79.         propertyValueNavigator.setAllowPrivate(true);
  80.         propertyValueNavigator.setIgnoreNull(true);
  81.         propertyValueNavigator.setIgnoreNotFoundKey(true);
  82.         propertyValueNavigator.setCacheWithPath(true);
  83.     }
  84.    
  85.     /**
  86.      * プロパティ式から、クラスタイプを取得する。
  87.      * ・private/protectedなどのフィールドにもアクセス可能にする。
  88.      */
  89.     private final PropertyTypeNavigator propertyTypeNavigator = new PropertyTypeNavigator();
  90.     {
  91.         propertyTypeNavigator.setAllowPrivate(true);
  92.         propertyTypeNavigator.setIgnoreNotResolveType(true);
  93.         propertyTypeNavigator.setCacheWithPath(true);
  94.     }
  95.    
  96.     /**
  97.      * オブジェクト名を指定しするコンストラクタ。
  98.      * <p>エラーメッセージを組み立てる際に、パスのルートとなる。
  99.      * @param target 検証対象のオブジェクト
  100.      * @param objectName オブジェクト名
  101.      */
  102.     public SheetBindingErrors(final P target, final String objectName) {
  103.        
  104.         this.target = target;
  105.         this.objectName = objectName;
  106.        
  107.         this.fieldFormatterRegistry.init();
  108.     }
  109.    
  110.     /**
  111.      * クラス名をオブジェクト名とするコンストラクタ。
  112.      * <p>オブジェクト名として、{@link Class#getCanonicalName()}を設定します。</p>
  113.      * @param target 検証対象のオブジェクト
  114.      * @throws IllegalArgumentException {@link target == null.}
  115.      */
  116.     public SheetBindingErrors(final P target) {
  117.         this(target, target.getClass().getCanonicalName());
  118.     }
  119.    
  120.     /**
  121.      * 検証対象のオブジェクトを取得する。
  122.      * @return
  123.      */
  124.     public P getTarget() {
  125.         return target;
  126.     }
  127.    
  128.     /**
  129.      * 現在のオブジェクト名称を取得する
  130.      * @return コンストラクタで設定したオブジェクト名称。
  131.      */
  132.     public String getObjectName() {
  133.         return objectName;
  134.     }
  135.    
  136.     /**
  137.      * 現在のシート名を取得する。
  138.      * @return シート名称
  139.      */
  140.     public String getSheetName() {
  141.         return sheetName;
  142.     }
  143.    
  144.     /**
  145.      * 現在のシート名を設定します。
  146.      * @param sheetName シートの名称
  147.      */
  148.     public void setSheetName(final String sheetName) {
  149.         this.sheetName = sheetName;
  150.     }
  151.    
  152.     /**
  153.      * シート番号を取得する
  154.      * @return 0から始まる。ただし、シートが設定されていない状態の時は、-1を返す。
  155.      */
  156.     public int getSheetIndex() {
  157.         return sheetIndex;
  158.     }
  159.    
  160.     /**
  161.      * シート番号を設定する
  162.      * @param sheetIndex シート番号(0から始まる)
  163.      */
  164.     public void setSheetIndex(int sheetIndex) {
  165.         this.sheetIndex = sheetIndex;
  166.     }
  167.    
  168.     /**
  169.      * 指定したパスのフィールドのクラスタイプを取得する。
  170.      * @since 2.0
  171.      * @param field フィールド名
  172.      * @return クラスタイプ。ただし、リストなどGenericsのタイプが指定されていない場合、クラスタイプもnullとなる。
  173.      */
  174.     public Class<?> getFieldType(final String field) {
  175.        
  176.         final String fieldPath = buildFieldPath(field);
  177.         Class<?> type = propertyTypeNavigator.getPropertyType(target.getClass(), fieldPath);
  178.         if(type != null) {
  179.             return type;
  180.         }
  181.        
  182.         return getActualFieldType(fieldPath);
  183.     }
  184.    
  185.     /**
  186.      * 指定したパスのフィールドのクラスタイプを取得する。
  187.      * <p>インスタンスを元に取得するため、サブクラスの可能性がある。</p>
  188.      * @since 2.0
  189.      * @param field フィールド名
  190.      * @return クラスタイプ。ただし、オブジェクトの値がnullの場合は、クラスタイプもnullとなる。
  191.      */
  192.     public Class<?> getActualFieldType(final String field) {
  193.        
  194.         final Object fieldValue = getFieldValue(field);
  195.         return fieldValue == null ? null : fieldValue.getClass();
  196.        
  197.     }
  198.    
  199.     /**
  200.      * 指定したパスのフィールドの値を取得する。
  201.      * <p>フィールドエラーにエラーが存在するときは、エラーオブジェクトから値を取得し、存在しない場合は、実際の値を取得する。</p>
  202.      * @param field フィールド名
  203.      * @return フィールドの値。
  204.      */
  205.     public Object getFieldValue(final String field) {
  206.        
  207.         final FieldError error = getFirstFieldError(field).orElse(null);
  208.         if(error != null && !error.isConversionFailure()) {
  209.             return error.getRejectedValue();
  210.         } else {
  211.             return getFieldActualValue(field);
  212.         }
  213.        
  214.     }
  215.    
  216.     /**
  217.      * 指定したパスのフィールドの値を取得する。
  218.      * @since 2.0
  219.      * @param field フィールド名
  220.      * @return フィールドの値。
  221.      */
  222.     public Object getFieldActualValue(final String field) {
  223.         final String fieldPath = buildFieldPath(field);
  224.         return propertyValueNavigator.getProperty(target, fieldPath);
  225.     }
  226.    
  227.     /**
  228.      * 現在のパス上のプロパティの値を取得します。
  229.      * <p>{@link #getTarget()}で取得できるルートオブジェクトに対して、{@link #getCurrentPath()}のパスで示された値を取得します。</p>
  230.      * @return 現在のパス上の値。
  231.      */
  232.     public Object getValue() {
  233.         final String currentPath = getCurrentPath();
  234.         if(Utils.isEmpty(currentPath)) {
  235.             return target;
  236.         } else {
  237.             return propertyValueNavigator.getProperty(target, currentPath);
  238.         }
  239.     }
  240.    
  241.     /**
  242.      * 指定したパスで現在のパスを初期化します。
  243.      * <p>nullまたは空文字を与えると、トップに移動します。
  244.      * @param nestedPath ネストするパス
  245.      */
  246.     public void setNestedPath(final String nestedPath) {
  247.         final String canonicalPath = normalizePath(nestedPath);
  248.         this.nestedPathStack.clear();
  249.         if(canonicalPath.isEmpty()) {
  250.             this.currentPath = buildPath();
  251.         } else {
  252.             pushNestedPath(canonicalPath);
  253.         }
  254.     }
  255.    
  256.     /**
  257.      * 現在のパスをルートに移動します。
  258.      */
  259.     public void setRootPath() {
  260.         setNestedPath(null);
  261.     }
  262.    
  263.     /**
  264.      * パスを正規化する。
  265.      * <ol>
  266.      *  <li>トリムする。</li>
  267.      *  <li>値がnullの場合は、空文字を返す。</li>
  268.      *  <li>最後に'.'がついている場合、除去する。</li>
  269.      * </ol>
  270.      * @param subPath
  271.      * @return
  272.      */
  273.     private String normalizePath(final String subPath) {
  274.         if(subPath == null) {
  275.             return "";
  276.         }
  277.        
  278.         String value = subPath.trim();
  279.         if(value.isEmpty()) {
  280.             return value;
  281.         }
  282.        
  283.         if(value.startsWith(PATH_SEPARATOR)) {
  284.             value = value.substring(1);
  285.         }
  286.        
  287.         if(value.endsWith(PATH_SEPARATOR)) {
  288.             value = value.substring(0, value.length()-1);
  289.         }
  290.        
  291.         return value;
  292.        
  293.     }
  294.    
  295.     /**
  296.      * パスを1つ下位に移動します。
  297.      * @param subPath ネストするパス
  298.      * @throws IllegalArgumentException subPath is empty.
  299.      */
  300.     public void pushNestedPath(final String subPath) {
  301.         final String canonicalPath = normalizePath(subPath);
  302.         ArgUtils.notEmpty(canonicalPath, "canonicalPath");
  303.        
  304.         this.nestedPathStack.push(canonicalPath);
  305.         this.currentPath = buildPath();
  306.     }
  307.    
  308.     /**
  309.      * 配列やリストなどのインデックス付きのパスを1つ下位に移動します。
  310.      * @param subPath ネストするパス
  311.      * @param index インデックス番号(0から始まります。)
  312.      * @throws IllegalArgumentException {@literal subPath is empty or index < 0}
  313.      */
  314.     public void pushNestedPath(final String subPath, final int index) {
  315.         final String canonicalPath = normalizePath(subPath);
  316.         ArgUtils.notEmpty(subPath, "subPath");
  317.         ArgUtils.notMin(index, -1, "index");
  318.        
  319.         pushNestedPath(String.format("%s[%d]", canonicalPath, index));
  320.     }
  321.    
  322.     /**
  323.      * マップなどのキー付きのパスを1つ下位に移動します。
  324.      * @param subPath ネストするパス
  325.      * @param key マップのキー
  326.      * @throws IllegalArgumentException {@literal subPath is empty or key is empty}
  327.      */
  328.     public void pushNestedPath(final String subPath, final String key) {
  329.         final String canonicalPath = normalizePath(subPath);
  330.         ArgUtils.notEmpty(subPath, "subPath");
  331.         ArgUtils.notEmpty(key, "key");
  332.        
  333.         pushNestedPath(String.format("%s[%s]", canonicalPath, key));
  334.     }
  335.    
  336.     /**
  337.      * パスを1つ上位に移動します。
  338.      * @return 現在のパスを返しまます。
  339.      * @throws IllegalStateException {@literal パスがこれ以上移動できない場合}
  340.      */
  341.     public String popNestedPath() {
  342.        
  343.         if(nestedPathStack.isEmpty()) {
  344.             throw new IllegalStateException("Cannot pop nested path: no nested path on stack");
  345.         }
  346.        
  347.         final String subPath = nestedPathStack.pop();
  348.         this.currentPath = buildPath();
  349.         return subPath;
  350.     }
  351.    
  352.     /**
  353.      * 現在パスのスタックに積まれているパスを結合し、1つに組み立てる。
  354.      * <p>ルートの時は空文字を返します。</p>
  355.      * @return 結合したパス
  356.      */
  357.     private String buildPath() {
  358.         return Utils.join(nestedPathStack, PATH_SEPARATOR);
  359.     }
  360.    
  361.     /**
  362.      * 現在のパスを取得します。
  363.      * <p>ルートの時は空文字を返します。</p>
  364.      * @return 現在のパス
  365.      */
  366.     public String getCurrentPath() {
  367.         return currentPath;
  368.     }
  369.    
  370.     /**
  371.      * 現在のパスに引数で指定したフィールド名を追加した値を返す。
  372.      * <p>現在のパスが空の場合は、フィールド名を返す。</p>
  373.      * @param fieldName フィールド名
  374.      * @return フィールド名を追加したパス
  375.      */
  376.     public String buildFieldPath(final String fieldName) {
  377.         if(Utils.isEmpty(getCurrentPath())) {
  378.             return fieldName;
  379.         } else {
  380.             return Utils.join(new String[]{getCurrentPath(), fieldName}, PATH_SEPARATOR);
  381.         }
  382.     }
  383.    
  384.     /**
  385.      * 全てのエラーをクリアする。
  386.      * @since 0.5
  387.      */
  388.     public void clearAllErrors() {
  389.         this.errors.clear();
  390.     }
  391.    
  392.     /**
  393.      * エラー情報を追加する
  394.      * @param error エラー情報
  395.      * @throws IllegalArgumentException {@literal error == null.}
  396.      */
  397.     public void addError(final ObjectError error) {
  398.         ArgUtils.notNull(error, "error");
  399.         this.errors.add(error);
  400.     }
  401.    
  402.     /**
  403.      * エラー情報を全て追加する。
  404.      * @param errors エラー情報
  405.      * @throws IllegalArgumentException {@literal errors == null.}
  406.      */
  407.     public void addAllErrors(final Collection<ObjectError> errors) {
  408.         ArgUtils.notNull(errors, "errors");
  409.         this.errors.addAll(errors);
  410.     }
  411.    
  412.     /**
  413.      * 全てのエラー情報を取得する
  414.      * @return 全てのエラー情報
  415.      */
  416.     public List<ObjectError> getAllErrors() {
  417.         return new ArrayList<>(errors);
  418.     }
  419.    
  420.     /**
  421.      * エラーがあるか確かめる。
  422.      * @return true:エラーがある。
  423.      */
  424.     public boolean hasErrors() {
  425.         return errors.size() > 0;
  426.     }
  427.    
  428.     /**
  429.      * グローバルエラーを取得する
  430.      * @return エラーがない場合は空のリストを返す
  431.      */
  432.     public List<ObjectError> getGlobalErrors() {
  433.         return errors.stream()
  434.                 .filter(e -> !(e instanceof FieldError))
  435.                 .collect(Collectors.toList());
  436.     }
  437.    
  438.     /**
  439.      * 先頭のグローバルエラーを取得する。
  440.      * @return 存在しない場合は、空を返す。
  441.      */
  442.     public Optional<ObjectError> getFirstGlobalError() {
  443.         return errors.stream()
  444.                 .filter(e -> !(e instanceof FieldError))
  445.                 .findFirst();
  446.        
  447.     }
  448.    
  449.     /**
  450.      * グローバルエラーがあるか確かめる。
  451.      * @return true:グローバルエラーがある。
  452.      */
  453.     public boolean hasGlobalErrors() {
  454.         return getFirstGlobalError().isPresent();
  455.     }
  456.    
  457.     /**
  458.      * グローバルエラーの件数を取得する
  459.      * @return エラーの件数
  460.      */
  461.     public int getGlobalErrorCount() {
  462.         return getGlobalErrors().size();
  463.     }
  464.    
  465.     /**
  466.      * フィールドエラーを取得する
  467.      * @return エラーがない場合は空のリストを返す
  468.      */
  469.     public List<FieldError> getFieldErrors() {
  470.         return errors.stream()
  471.                 .filter(e -> e instanceof FieldError)
  472.                 .map(e -> (FieldError)e)
  473.                 .collect(Collectors.toList());
  474.        
  475.     }
  476.    
  477.     /**
  478.      * 先頭のフィールドエラーを取得する
  479.      * @return エラーがない場合は空を返す
  480.      */
  481.     public Optional<FieldError> getFirstFieldError() {
  482.         return errors.stream()
  483.                 .filter(e -> e instanceof FieldError)
  484.                 .map(e -> (FieldError)e)
  485.                 .findFirst();
  486.        
  487.     }
  488.    
  489.     /**
  490.      * フィールドエラーが存在するか確かめる。
  491.      * @return true:フィールドエラーを持つ。
  492.      */
  493.     public boolean hasFieldErrors() {
  494.         return getFirstFieldError().isPresent();
  495.     }
  496.    
  497.     /**
  498.      * フィールドエラーの件数を取得する。
  499.      * @return フィールドエラーの件数
  500.      */
  501.     public int getFieldErrorCount() {
  502.         return getFieldErrors().size();
  503.     }
  504.    
  505.     /**
  506.      * パスを指定してフィールドエラーを取得する。
  507.      * <p>検索する際には、引数「path」に現在のパス({@link #getCurrentPath()})を付与して処理します。</p>
  508.      * @param path 最後に'*'を付けるとワイルドカードが指定可能。
  509.      * @return エラーがない場合は空のリストを返す
  510.      */
  511.     public List<FieldError> getFieldErrors(final String path) {
  512.         final String fullPath = buildFieldPath(path);
  513.        
  514.         return getFieldErrors().stream()
  515.                 .filter(e -> isMatchingFieldError(fullPath, e))
  516.                 .collect(Collectors.toList());
  517.        
  518.     }
  519.    
  520.     /**
  521.      * パスを指定して先頭のフィールドエラーを取得する。
  522.      * <p>検索する際には、引数「path」に現在のパス({@link #getCurrentPath()})を付与して処理します。</p>
  523.      * @param path 最後に'*'を付けるとワイルドカードが指定可能。
  524.      * @return エラーがない場合は空を返す
  525.      */
  526.     public Optional<FieldError> getFirstFieldError(final String path) {
  527.         final String fullPath = buildFieldPath(path);
  528.         return getFieldErrors().stream()
  529.                 .filter(e -> isMatchingFieldError(fullPath, e))
  530.                 .findFirst();
  531.        
  532.     }
  533.    
  534.     /**
  535.      * 指定したパスのフィィールドエラーが存在するか確かめる。
  536.      * @param path 最後に'*'を付けるとワイルドカードが指定可能。
  537.      * @return true:エラーがある場合。
  538.      */
  539.     public boolean hasFieldErrors(final String path) {
  540.         return getFirstFieldError(path).isPresent();
  541.     }
  542.    
  543.     /**
  544.      * 指定したパスのフィィールドエラーの件数を取得する。
  545.      * @param path 最後に'*'を付けるとワイルドカードが指定可能。
  546.      * @return
  547.      */
  548.     public int getFieldErrorCount(final String path) {
  549.         return getFieldErrors(path).size();
  550.     }
  551.    
  552.     /**
  553.      * 指定したパスがフィールドエラーのパスと一致するかチェックするかどうか。
  554.      * @param path パス
  555.      * @param fieldError フィールドエラー
  556.      * @return true: 一致する場合。
  557.      */
  558.     private boolean isMatchingFieldError(final String path, final FieldError fieldError) {
  559.        
  560.         if (fieldError.getField().equals(path)) {
  561.             return true;
  562.         }
  563.        
  564.         if(path.endsWith("*")) {
  565.             String subPath = path.substring(0, path.length()-1);
  566.             return fieldError.getField().startsWith(subPath);
  567.         }
  568.        
  569.         return false;
  570.     }
  571.    
  572.     /**
  573.      * グローバルエラーのビルダーを作成します。
  574.      * @param errorCode エラーコード
  575.      * @return {@link ObjectError}のインスタンスを組み立てるビルダクラス。
  576.      */
  577.     public InternalObjectErrorBuilder createGlobalError(final String errorCode) {
  578.         return createGlobalError(new String[]{errorCode});
  579.     }
  580.    
  581.     /**
  582.      * グローバルエラーのビルダーを作成します。
  583.      * @param errorCodes エラーコード。先頭の要素が優先されます。
  584.      * @return {@link ObjectError}のインスタンスを組み立てるビルダクラス。
  585.      */
  586.     public InternalObjectErrorBuilder createGlobalError(final String[] errorCodes) {
  587.        
  588.         String[] codes = new String[0];
  589.         for(String errorCode : errorCodes) {
  590.             codes = Utils.concat(codes, generateMessageCodes(errorCode));
  591.         }
  592.        
  593.         return new InternalObjectErrorBuilder(this, getObjectName(), codes)
  594.                 .sheetName(getSheetName());
  595.     }
  596.    
  597.     /**
  598.      * フィールドエラーのビルダーを作成します。
  599.      * @param field フィールドパス。
  600.      * @param errorCode エラーコード
  601.      * @return {@link FieldError}のインスタンスを組み立てるビルダクラス。
  602.      */
  603.     public InternalFieldErrorBuilder createFieldError(final String field, final String errorCode) {
  604.        
  605.         return createFieldError(field, new String[]{errorCode});
  606.        
  607.     }
  608.    
  609.     /**
  610.      * フィールドエラーのビルダーを作成します。
  611.      * @param field フィールドパス。
  612.      * @param errorCodes エラーコード。先頭の要素が優先されます。
  613.      * @return {@link FieldError}のインスタンスを組み立てるビルダクラス。
  614.      */
  615.     public InternalFieldErrorBuilder createFieldError(final String field, final String[] errorCodes) {
  616.        
  617.         final String fieldPath = buildFieldPath(field);
  618.         final Class<?> fieldType = getFieldType(field);
  619.         final Object fieldValue = getFieldValue(field);
  620.        
  621.         String[] codes = new String[0];
  622.         for(String errorCode : errorCodes) {
  623.             codes = Utils.concat(codes, generateMessageCodes(errorCode, fieldPath, fieldType));
  624.         }
  625.        
  626.         return new InternalFieldErrorBuilder(this, getObjectName(), fieldPath, codes)
  627.                 .sheetName(getSheetName())
  628.                 .rejectedValue(fieldValue);
  629.                
  630.     }
  631.    
  632.     /**
  633.      * 型変換失敗時のフィールエラー用のビルダを作成します。
  634.      * @param field フィールドパス。
  635.      * @param fieldType フィールドのクラスタイプ
  636.      * @param rejectedValue 型変換に失敗した値
  637.      * @return {@link FieldError}のインスタンスを組み立てるビルダクラス。
  638.      */
  639.     public InternalFieldErrorBuilder createFieldConversionError(final String field, final Class<?> fieldType, final Object rejectedValue) {
  640.        
  641.         final String fieldPath = buildFieldPath(field);
  642.         final String[] codes = messageCodeGenerator.generateTypeMismatchCodes(getObjectName(), fieldPath, fieldType);
  643.        
  644.         return new InternalFieldErrorBuilder(this, getObjectName(), fieldPath, codes)
  645.                 .sheetName(getSheetName())
  646.                 .rejectedValue(rejectedValue)
  647.                 .conversionFailure(true);
  648.        
  649.        
  650.     }
  651.    
  652.     /**
  653.      * フィールドに対するフォーマッタを登録する。
  654.      * @since 2.0
  655.      * @param field フィールド名
  656.      * @param fieldType フィールドのクラスタイプ
  657.      * @param formatter フォーマッタ
  658.      */
  659.     public void registerFieldFormatter(final String field, final Class<?> fieldType, final FieldFormatter<?> formatter) {
  660.        
  661.         registerFieldFormatter(field, fieldType, formatter, false);
  662.        
  663.     }
  664.    
  665.     /**
  666.      * フィールドに対するフォーマッタを登録する。
  667.      * @since 2.0
  668.      * @param field フィールド名
  669.      * @param fieldType フィールドのクラスタイプ
  670.      * @param formatter フォーマッタ
  671.      * @param strippedIndex 登録するときにフィールドパスから、インデックス情報を除去するかどうか。
  672.      */
  673.     @SuppressWarnings({"unchecked", "rawtypes"})
  674.     public void registerFieldFormatter(final String field, final Class<?> fieldType, final FieldFormatter<?> formatter,
  675.             final boolean strippedIndex) {
  676.        
  677.         String fieldPath = buildFieldPath(field);
  678.        
  679.         if(strippedIndex) {
  680.             // パスからインデックスやキーを削除する
  681.             List<String> strippedPaths = new ArrayList<>();
  682.             fieldFormatterRegistry.addStrippedPropertyPaths(strippedPaths, "", fieldPath);
  683.            
  684.             if(strippedPaths.size() > 0) {
  685.                 // 辞書順位並び変えて先頭に来るのが、インデックスを全て削除されたパス
  686.                 Collections.sort(strippedPaths);
  687.                 fieldPath = strippedPaths.get(0);
  688.             }
  689.         }
  690.        
  691.         fieldFormatterRegistry.registerFormatter(fieldPath, (Class)fieldType, (FieldFormatter)formatter);
  692.        
  693.     }
  694.    
  695.     /**
  696.      * フィールドとクラスタイプを指定してフォーマッタを取得する。
  697.      * @since 2.0
  698.      * @param field フィールド名
  699.      * @param fieldType フィールドのクラスタイプ
  700.      * @return 見つからない場合は、nullを返す。
  701.      */
  702.     public <T> FieldFormatter<T> findFieldFormatter(final String field, final Class<T> fieldType) {
  703.         String fieldPath = buildFieldPath(field);
  704.         return fieldFormatterRegistry.findFormatter(fieldPath, fieldType);
  705.     }
  706.    
  707.     public String[] generateMessageCodes(final String code) {
  708.         return getMessageCodeGenerator().generateCodes(code, getObjectName());
  709.     }
  710.    
  711.     public String[] generateMessageCodes(final String code, final String field) {
  712.         return getMessageCodeGenerator().generateCodes(code, getObjectName(), field, null);
  713.     }
  714.    
  715.     public String[] generateMessageCodes(final String code, final String field, final Class<?> fieldType) {
  716.         return getMessageCodeGenerator().generateCodes(code, getObjectName(), field, fieldType);
  717.     }
  718.    
  719.     public MessageCodeGenerator getMessageCodeGenerator() {
  720.         return messageCodeGenerator;
  721.     }
  722.    
  723.     public void setMessageCodeGenerator(MessageCodeGenerator messageCodeGenerator) {
  724.         this.messageCodeGenerator = messageCodeGenerator;
  725.     }
  726.    
  727.     /**
  728.      * フィールドのフォーマッタの管理クラスを取得する。
  729.      * @return
  730.      */
  731.     public FieldFormatterRegistry getFieldFormatterRegistry() {
  732.         return fieldFormatterRegistry;
  733.     }
  734.    
  735.     /**
  736.      * フィールドのフォーマッタクラスを設定する。
  737.      * @param fieldFormatterRegistry フィールドのフォーマッタの管理クラス
  738.      */
  739.     public void setFieldFormatterRegistry(FieldFormatterRegistry fieldFormatterRegistry) {
  740.         this.fieldFormatterRegistry = fieldFormatterRegistry;
  741.     }
  742.    
  743. }