MessageCodeGenerator.java
package com.gh.mygreen.xlsmapper.validation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.gh.mygreen.xlsmapper.util.ArgUtils;
import com.gh.mygreen.xlsmapper.util.Utils;
/**
* メッセージのコードを生成するクラス。
* <p>Stringの「DefaultMessageCodeResolver」を参照。
*
* @version 2.0
* @author T.TSUCHIE
*
*/
public class MessageCodeGenerator {
public static final String CODE_SEPARATOR = ".";
/** メッセージの接頭語 */
private String prefix = "";
/** 型変換エラー時のコード */
private String typeMismatchCode = "cellTypeMismatch";
/**
* コードの候補を生成する。
* @param code
* @param objectName
* @return
*/
public String[] generateCodes(final String code, final String objectName) {
return generateCodes(code, objectName, null, null);
}
/**
* 型変換エラーコードの候補を生成する。
* @param objectName
* @param field
* @param filedType
* @return
*/
public String[] generateTypeMismatchCodes(final String objectName, final String field, final Class<?> filedType) {
return generateCodes(getTypeMismatchCode(), objectName, field, filedType);
}
/**
* オブジェクト名のキーの候補を生成する。
* <p>クラスパスの区切り文字(.)がある場合、それらを除いたものを候補とする。</p>
* @param objectName クラス名など。
* @return キーの候補
*/
public String[] generateObjectNameCodes(final String objectName) {
ArgUtils.notNull(objectName, "objectName");
final List<String> codeList = new ArrayList<String>();
codeList.add(objectName);
// オブジェクト名の最後の値を取得する
final int dotIndex = objectName.lastIndexOf('.');
if(dotIndex > 0) {
final String subName = objectName.substring(dotIndex + 1);
codeList.add(subName);
}
return codeList.toArray(new String[codeList.size()]);
}
/**
* フィールド名のキーの候補を生成する。
* @param objectName
* @param field
* @return
*/
public String[] generateFieldNameCodes(final String objectName, final String field) {
final List<String> codeList = new ArrayList<String>();
codeList.addAll(Arrays.asList(generateCodes(null, objectName, field, null)));
// オブジェクト名の最後の値を取得する
final int dotIndex = objectName.lastIndexOf('.');
if(dotIndex > 0) {
final String subName = objectName.substring(dotIndex + 1);
for(String code : generateCodes(null, subName, field, null)) {
if(!codeList.contains(code)) {
codeList.add(code);
}
}
}
return codeList.toArray(new String[codeList.size()]);
}
/**
* フィールドの親のキーの候補を生成する。
* @param objectName オブジェクト名
* @param field フィールド
* @return
*/
public String[] generateParentNameCodes(final String objectName, final String field) {
if(Utils.isEmpty(field) || !field.contains(".")) {
return generateObjectNameCodes(objectName);
}
// フィールド名の前の値を取得する
final int dotIndex = field.lastIndexOf('.');
final String subName = field.substring(0, dotIndex);
return generateFieldNameCodes(objectName, subName);
}
/**
* キーの候補を生成する。
* <p>コンテキストのキーの形式として、次の優先順位に一致したものを返す。
*
* @param code 元となるメッセージのコード
* @param objectName オブジェクト名(クラスのフルパス)
* @param field フィールド名 (指定しない場合はnullを設定する)
* @param fieldType フィールドのクラスタイプ(指定しない場合はnullを設定する)
* @return
*/
public String[] generateCodes(final String code, final String objectName, final String field, final Class<?> fieldType) {
final String baseCode = getPrefix().isEmpty() ? code : getPrefix() + code;
final List<String> codeList = new ArrayList<>();
final List<String> objectNameList = Arrays.asList(generateObjectNameCodes(objectName));
final List<String> fieldList = new ArrayList<>();
buildFieldList(field, fieldList);
if(Utils.isNotEmpty(field)) {
// 最後のフィールド名のみを取得する
int dotIndex = field.lastIndexOf('.');
if(dotIndex > 0) {
buildFieldList(field.substring(dotIndex + 1), fieldList);
}
}
for(final String name : objectNameList) {
addCodes(codeList, baseCode, name, fieldList);
}
addCodes(codeList, code, null, fieldList);
if(fieldType != null) {
addCode(codeList, code, null, fieldType.getName());
if(Enum.class.isAssignableFrom(fieldType)) {
// 列挙型の場合は、java.lang.Enumとしてクラスタイプを追加する。
addCode(codeList, code, null, Enum.class.getName());
} else if(fieldType.isArray()) {
// 配列の場合は、"配列のクラスタイプ+[]"を追加する。
Class<?> componentType = fieldType.getComponentType();
addCode(codeList, code, null, componentType.getName() + "[]");
addCode(codeList, code, null, "java.lang.Object[]");
}
}
addCode(codeList, code, null, null);
return codeList.toArray(new String[codeList.size()]);
}
/**
* フィールドのパスを分解して、パスの候補を作成する。
* <p>インデックスを示す'[0]'を除いたりして組み立てる。
* @param field
* @param fieldList
*/
protected void buildFieldList(final String field, final List<String> fieldList) {
if(Utils.isEmpty(field)) {
return;
}
if(!fieldList.contains(field)) {
fieldList.add(field);
}
String plainField = String.valueOf(field);
int keyIndex = plainField.lastIndexOf('[');
while(keyIndex >= 0) {
int endKeyIndex = plainField.indexOf(']', keyIndex);
if(endKeyIndex >= 0) {
plainField = plainField.substring(0, keyIndex) + plainField.substring(endKeyIndex + 1);
if(!fieldList.contains(plainField)) {
fieldList.add(plainField);
}
keyIndex = plainField.lastIndexOf('[');
} else {
keyIndex = -1;
}
}
}
private void addCodes(final List<String> codeList, final String code, final String objectName, final List<String> fieldList) {
for(String field : fieldList) {
addCode(codeList, code, objectName, field);
}
}
private void addCode(final List<String> codeList, final String code, final String objectName, final String field) {
final String formattedCode = formatCode(code, objectName, field);
if(!codeList.contains(formattedCode)) {
codeList.add(formattedCode);
}
}
private String formatCode(final String... elements) {
// エラーコードを前に付ける場合
StringBuilder code = new StringBuilder();
for(String element : elements) {
if(Utils.isNotEmpty(element)) {
code.append(code.length() == 0 ? "" : CODE_SEPARATOR);
code.append(element);
}
}
return code.toString();
}
/**
* メッセージの接頭語を取得する。
* @return 接頭語を返す。デフォルトは、空文字。
*/
public String getPrefix() {
return prefix;
}
/**
* メッセージの接頭語を設定する。
* @param prefix 接頭語
*/
public void setPrefix(String prefix) {
this.prefix = prefix;
}
/**
* 型変換エラー時のエラーコードを取得する。
* @return デフォルトは、'{@literal cellTypeMismatch}'。
*/
public String getTypeMismatchCode() {
return typeMismatchCode;
}
/**
* 型変換エラー時のエラーコードを設定する。
* @param typeMismatchCode 型変換エラー時のエラーコード
*/
public void setTypeMismatchCode(String typeMismatchCode) {
this.typeMismatchCode = typeMismatchCode;
}
}