ResourceBundleMessageResolver.java
package com.gh.mygreen.xlsmapper.localization;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Objects;
import java.util.Optional;
import java.util.ResourceBundle;
import com.gh.mygreen.xlsmapper.util.ArgUtils;
/**
* {@link ResourceBundle}を元にメッセージを解決するクラス。
* <p>クラスパスのルートにリソース名が{@literal SheetValidationMessages}のプロパティファイルを配置していると自動的に読み込みます。</p>
* <p>デフォルトでは、{@link ResourceBundleMessageResolver#DEFAULT_MESSAGE}に配置されているリソースファイルを読み込みます。</p>
*
* @version 2.0
* @author T.TSUCHIE
*
*/
public class ResourceBundleMessageResolver implements MessageResolver {
/**
* デフォルトのメッセージソースのパス
*/
public static final String DEFAULT_MESSAGE = "com.gh.mygreen.xlsmapper.localization.SheetValidationMessages";
private final Map<ResourceBundle, List<String>> messageBundleKeys = new HashMap<ResourceBundle, List<String>>(8);
private final LinkedList<ResourceBundle> messageBundles = new LinkedList<ResourceBundle>();
/**
* メッセージリソースのパスを指定して、インスタンスを作成します。
* @param baseName メッセージリソースのパス。
* @param appendUserResource クラスパスのルートにあるユーザ定義のメッセージソースも読み込むかどうか指定します。
* 引数baseNameの値が {@literal sample.SampleMessages}のとき、クラスパスのルート上にある「SampleMessages」を読み込みます。
* @throws IllegalArgumentException {@literal baseName is null or empty.}
*/
public ResourceBundleMessageResolver(final String baseName, final boolean appendUserResource) {
ArgUtils.notEmpty(baseName, "baseName");
addResourceBundle(ResourceBundle.getBundle(baseName, new EncodingControl("UTF-8")));
// ユーザ定義のリソースを読み込む
if(appendUserResource) {
// リソース名の切り出し
final int index = baseName.lastIndexOf(".");
if(index > 0) {
final String userName = baseName.substring(index+1);
try {
addResourceBundle(ResourceBundle.getBundle(userName, new EncodingControl("UTF-8")));
} catch(Throwable e) { }
}
}
}
/**
* デフォルトのコンストラクタ。
* <p>デフォルトのメッセージソース{@link #DEFAULT_MESSAGE}が自動的に読み込まれます。</p>
*
*/
public ResourceBundleMessageResolver() {
this(DEFAULT_MESSAGE, true);
}
/**
* 独自のメッセージソースを指定してインスタンスを作成する。
* <p>デフォルトのメッセージソース{@link #DEFAULT_MESSAGE}が自動的に読み込まれます。</p>
* @param resourceBundle 独自のメッセージメース
* @throws IllegalArgumentException resourceBundle is null.
*/
public ResourceBundleMessageResolver(final ResourceBundle resourceBundle) {
this();
Objects.requireNonNull(resourceBundle, "resourceBundle should no be null");
addResourceBundle(resourceBundle);
}
/**
* {@inheritDoc}
*/
public Optional<String> getMessage(final String code) {
for(final ResourceBundle bundle : messageBundles) {
final List<String> keys = messageBundleKeys.get(bundle);
if(keys.contains(code)) {
try {
return Optional.of(bundle.getString(code));
} catch(MissingResourceException e) {
return Optional.empty();
}
}
}
return Optional.empty();
}
/**
* メッセージソースを追加します。
* @param resourceBundle 追加するメッセージソース。
* @return 既に追加済みの場合はfalseを返します。
* @throws IllegalArgumentException resourceBundle is null.
*/
public final boolean addResourceBundle(final ResourceBundle resourceBundle) {
Objects.requireNonNull(resourceBundle, "resourceBundle should not be null.");
if(messageBundles.contains(resourceBundle)) {
return false;
}
messageBundles.addFirst(resourceBundle);
final List<String> keys = new ArrayList<String>();
for(final Enumeration<String> keysEnum = resourceBundle.getKeys(); keysEnum.hasMoreElements();) {
keys.add(keysEnum.nextElement());
}
messageBundleKeys.put(resourceBundle, keys);
return true;
}
/**
* メッセージソースを削除する。
* @param resourceBundle 削除対象のメッセージソース
* @return 登録されているメッセージソースがある場合はtrueを返します。
* @throws IllegalArgumentException resourceBundle is null.
*/
public boolean removeResourceBundle(final ResourceBundle resourceBundle) {
Objects.requireNonNull(resourceBundle, "resourceBundle should not be null.");
if(!messageBundles.contains(resourceBundle)) {
return false;
}
messageBundles.remove(resourceBundle);
return true;
}
}