View Javadoc
1   package com.github.mygreen.supercsv.validation;
2   
3   import java.io.Serializable;
4   import java.util.ArrayList;
5   import java.util.Arrays;
6   import java.util.List;
7   import java.util.Objects;
8   
9   import com.github.mygreen.supercsv.util.Utils;
10  
11  
12  /**
13   * メッセージのコードを生成するクラス。
14   * <p>Stringの「DefaultMessageCodeResolver」を参照。</p>
15   * 
16   * @author T.TSUCHIE
17   *
18   */
19  public class MessageCodeGenerator implements Serializable {
20      
21      /** serialVersionUID */
22      private static final long serialVersionUID = 1L;
23      
24      public static final String CODE_SEPARATOR = ".";
25      
26      /** メッセージの接頭語 */
27      private String prefix = "";
28      
29      /** 型変換エラー時のコード */
30      private String typeMismatchCode = "typeMismatch";
31      
32      /**
33       * コードの候補を生成する。
34       * @param code
35       * @param objectName
36       * @return
37       */
38      public String[] generateCodes(final String code, final String objectName) {
39          return generateCodes(code, objectName, null, null);
40      }
41      
42      /**
43       * 型変換エラーコードの候補を生成する。
44       * @param objectName
45       * @param field
46       * @param fileType
47       * @return
48       */
49      public String[] generateTypeMismatchCodes(final String objectName, final String field, final Class<?> fileType) {
50          return generateCodes(getTypeMismatchCode(), objectName, field, fileType);
51      }
52      
53      /**
54       * オブジェクト名のキーの候補を生成する。
55       * @param objectName
56       * @return
57       */
58      public String[] generateObjectNameCodes(final String objectName) {
59          Objects.requireNonNull(objectName, "objectName should not be null");
60          
61          final List<String> codeList = new ArrayList<String>();
62          codeList.add(objectName);
63          
64          // オブジェクト名の最後の値を取得する
65          final int dotIndex = objectName.lastIndexOf('.');
66          if(dotIndex > 0) {
67              final String subName = objectName.substring(dotIndex + 1);
68              codeList.add(subName);
69              
70          }
71          
72          return codeList.toArray(new String[codeList.size()]);
73      }
74      
75      /**
76       * フィールド名のキーの候補を生成する。
77       * @param objectName
78       * @param field
79       * @return
80       */
81      public String[] generateFieldNameCodes(final String objectName, final String field) {
82          
83          final List<String> codeList = new ArrayList<String>();
84          codeList.addAll(Arrays.asList(generateCodes(null, objectName, field, null)));
85          
86          // オブジェクト名の最後の値を取得する
87          final int dotIndex = objectName.lastIndexOf('.');
88          if(dotIndex > 0) {
89              final String subName = objectName.substring(dotIndex + 1);
90              for(String code : generateCodes(null, subName, field, null)) {
91                  if(!codeList.contains(code)) {
92                      codeList.add(code);
93                  }
94              }
95              
96          }
97          
98          return codeList.toArray(new String[codeList.size()]);
99      }
100     
101     /**
102      * フィールドの親のキーの候補を生成する。
103      * @param objectName オブジェクト名
104      * @param field フィールド
105      * @return
106      */
107     public String[] generateParentNameCodes(final String objectName, final String field) {
108         
109         if(Utils.isEmpty(field) || !field.contains(".")) {
110             return generateObjectNameCodes(objectName);
111         }
112         
113         // フィールド名の前の値を取得する
114         final int dotIndex = field.lastIndexOf('.');
115         final String subName = field.substring(0, dotIndex);
116         return generateFieldNameCodes(objectName, subName);
117         
118     }
119     
120     /**
121      * キーの候補を生成する。
122      * <p>コンテキストのキーの形式として、次の優先順位に一致したものを返す。
123      * 
124      * @param code 元となるメッセージのコード
125      * @param objectName オブジェクト名(クラスのフルパス)
126      * @param field フィールド名 (指定しない場合はnullを設定する)
127      * @param fieldType フィールドのクラスタイプ(指定しない場合はnullを設定する)
128      * @return
129      */
130     public String[] generateCodes(final String code, final String objectName, final String field, final Class<?> fieldType) {
131         
132         final String baseCode = getPrefix().isEmpty() ? code : getPrefix() + code;
133         final List<String> codeList = new ArrayList<>();
134         final List<String> fieldList = new ArrayList<>();
135         buildFieldList(field, fieldList);
136         
137         addCodes(codeList, baseCode, objectName, fieldList);
138         
139         if(Utils.isNotEmpty(field)) {
140             int dotIndex = field.lastIndexOf('.');
141             if(dotIndex > 0) {
142                 buildFieldList(field.substring(dotIndex + 1), fieldList);
143             }
144         }
145         
146         addCodes(codeList, code, null, fieldList);
147         
148         if(fieldType != null) {
149             addCode(codeList, code, null, fieldType.getName());
150             
151             // 列挙型の場合は、java.lang.Enumとしてクラスタイプを追加する。
152             if(Enum.class.isAssignableFrom(fieldType)) {
153                 addCode(codeList, code, null, Enum.class.getName());
154             }
155             
156             // 数値型の場合は、java.lang.Numberとしてクラスタイプを追加する。
157             if(Number.class.isAssignableFrom(fieldType)) {
158                 addCode(codeList, code, null, Number.class.getName());
159             }
160         }
161         
162         addCode(codeList, code, null, null);
163         
164         return codeList.toArray(new String[codeList.size()]);
165     }
166     
167     /**
168      * フィールドのパスを分解して、パスの候補を作成する。
169      * <p>インデックスを示す'[0]'を除いたりして組み立てる。
170      * @param field
171      * @param fieldList
172      */
173     protected void buildFieldList(final String field, final List<String> fieldList) {
174         
175         if(Utils.isEmpty(field)) {
176             return;
177         }
178         
179         if(!fieldList.contains(field)) {
180             fieldList.add(field);
181         }
182         
183         String plainField = String.valueOf(field);
184         int keyIndex = plainField.lastIndexOf('[');
185         while(keyIndex >= 0) {
186             int endKeyIndex = plainField.indexOf(']', keyIndex);
187             if(endKeyIndex >= 0) {
188                 plainField = plainField.substring(0, keyIndex) + plainField.substring(endKeyIndex + 1);
189                 
190                 if(!fieldList.contains(plainField)) {
191                     fieldList.add(plainField);
192                 }
193                 keyIndex = plainField.lastIndexOf('[');
194             } else {
195                 keyIndex = -1;
196             }
197         }
198     }
199     
200     private void addCodes(final List<String> codeList, final String code, final String objectName, final List<String> fieldList) {
201         for(String field : fieldList) {
202             addCode(codeList, code, objectName, field);
203         }
204     }
205     
206     private void addCode(final List<String> codeList, final String code, final String objectName, final String field) {
207         final String formattedCode = formatCode(code, objectName, field);
208         if(!codeList.contains(formattedCode)) {
209             codeList.add(formattedCode);
210         }
211     }
212     
213     private String formatCode(final String... elements) {
214         
215         // エラーコードを前に付ける場合
216         StringBuilder code = new StringBuilder();
217         for(String element : elements) {
218             if(Utils.isNotEmpty(element)) {
219                 code.append(code.length() == 0 ? "" : CODE_SEPARATOR);
220                 code.append(element);
221             }
222         }
223         
224         return code.toString();
225         
226     }
227     
228     public String getPrefix() {
229         return prefix;
230     }
231     
232     public void setPrefix(String prefix) {
233         this.prefix = prefix;
234     }
235     
236     public String getTypeMismatchCode() {
237         return typeMismatchCode;
238     }
239     
240     public void setTypeMismatchCode(String typeMismatchCode) {
241         this.typeMismatchCode = typeMismatchCode;
242     }
243 }