ResourceBundleMessageResolver.java

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

  2. import java.util.ArrayList;
  3. import java.util.Enumeration;
  4. import java.util.HashMap;
  5. import java.util.LinkedList;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.MissingResourceException;
  9. import java.util.Objects;
  10. import java.util.Optional;
  11. import java.util.ResourceBundle;

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

  13. /**
  14.  * {@link ResourceBundle}を元にメッセージを解決するクラス。
  15.  * <p>クラスパスのルートにリソース名が{@literal SheetValidationMessages}のプロパティファイルを配置していると自動的に読み込みます。</p>
  16.  * <p>デフォルトでは、{@link ResourceBundleMessageResolver#DEFAULT_MESSAGE}に配置されているリソースファイルを読み込みます。</p>
  17.  *
  18.  * @version 2.0
  19.  * @author T.TSUCHIE
  20.  *
  21.  */
  22. public class ResourceBundleMessageResolver implements MessageResolver {
  23.    
  24.     /**
  25.      * デフォルトのメッセージソースのパス
  26.      */
  27.     public static final String DEFAULT_MESSAGE = "com.gh.mygreen.xlsmapper.localization.SheetValidationMessages";
  28.    
  29.     private final Map<ResourceBundle, List<String>> messageBundleKeys = new HashMap<ResourceBundle, List<String>>(8);
  30.    
  31.     private final LinkedList<ResourceBundle> messageBundles = new LinkedList<ResourceBundle>();
  32.    
  33.     /**
  34.      * メッセージリソースのパスを指定して、インスタンスを作成します。
  35.      * @param baseName メッセージリソースのパス。
  36.      * @param appendUserResource クラスパスのルートにあるユーザ定義のメッセージソースも読み込むかどうか指定します。
  37.      *      引数baseNameの値が {@literal sample.SampleMessages}のとき、クラスパスのルート上にある「SampleMessages」を読み込みます。
  38.      * @throws IllegalArgumentException {@literal baseName is null or empty.}
  39.      */
  40.     public ResourceBundleMessageResolver(final String baseName, final boolean appendUserResource) {
  41.         ArgUtils.notEmpty(baseName, "baseName");
  42.        
  43.         addResourceBundle(ResourceBundle.getBundle(baseName, new EncodingControl("UTF-8")));
  44.        
  45.         // ユーザ定義のリソースを読み込む
  46.         if(appendUserResource) {
  47.             // リソース名の切り出し
  48.             final int index = baseName.lastIndexOf(".");
  49.             if(index > 0) {
  50.                 final String userName = baseName.substring(index+1);
  51.                 try {
  52.                     addResourceBundle(ResourceBundle.getBundle(userName, new EncodingControl("UTF-8")));
  53.                 } catch(Throwable e) { }
  54.             }
  55.         }
  56.        
  57.     }
  58.    
  59.     /**
  60.      * デフォルトのコンストラクタ。
  61.      * <p>デフォルトのメッセージソース{@link #DEFAULT_MESSAGE}が自動的に読み込まれます。</p>
  62.      *
  63.      */
  64.     public ResourceBundleMessageResolver() {
  65.         this(DEFAULT_MESSAGE, true);
  66.     }
  67.    
  68.     /**
  69.      * 独自のメッセージソースを指定してインスタンスを作成する。
  70.      * <p>デフォルトのメッセージソース{@link #DEFAULT_MESSAGE}が自動的に読み込まれます。</p>
  71.      * @param resourceBundle 独自のメッセージメース
  72.      * @throws IllegalArgumentException resourceBundle is null.
  73.      */
  74.     public ResourceBundleMessageResolver(final ResourceBundle resourceBundle) {
  75.         this();
  76.         Objects.requireNonNull(resourceBundle, "resourceBundle should no be null");
  77.         addResourceBundle(resourceBundle);
  78.     }
  79.    
  80.     /**
  81.      * {@inheritDoc}
  82.      */
  83.     public Optional<String> getMessage(final String code) {
  84.         for(final ResourceBundle bundle : messageBundles) {
  85.             final List<String> keys = messageBundleKeys.get(bundle);
  86.             if(keys.contains(code)) {
  87.                 try {
  88.                     return Optional.of(bundle.getString(code));
  89.                 } catch(MissingResourceException e) {
  90.                     return Optional.empty();
  91.                 }
  92.             }
  93.         }
  94.        
  95.         return Optional.empty();
  96.     }
  97.    
  98.     /**
  99.      * メッセージソースを追加します。
  100.      * @param resourceBundle 追加するメッセージソース。
  101.      * @return 既に追加済みの場合はfalseを返します。
  102.      * @throws IllegalArgumentException resourceBundle is null.
  103.      */
  104.     public final boolean addResourceBundle(final ResourceBundle resourceBundle) {
  105.         Objects.requireNonNull(resourceBundle, "resourceBundle should not be null.");
  106.        
  107.         if(messageBundles.contains(resourceBundle)) {
  108.             return false;
  109.         }
  110.        
  111.         // 後から追加したリソースを優先するために、先頭に追加する。
  112.         messageBundles.addFirst(resourceBundle);
  113.         final List<String> keys = new ArrayList<String>();
  114.        
  115.         for(final Enumeration<String> keysEnum = resourceBundle.getKeys(); keysEnum.hasMoreElements();) {
  116.             keys.add(keysEnum.nextElement());
  117.         }
  118.        
  119.         messageBundleKeys.put(resourceBundle, keys);
  120.        
  121.         return true;
  122.     }
  123.    
  124.    /**
  125.      * メッセージソースを削除する。
  126.      * @param resourceBundle 削除対象のメッセージソース
  127.      * @return 登録されているメッセージソースがある場合はtrueを返します。
  128.      * @throws IllegalArgumentException resourceBundle is null.
  129.      */
  130.     public boolean removeResourceBundle(final ResourceBundle resourceBundle) {
  131.         Objects.requireNonNull(resourceBundle, "resourceBundle should not be null.");
  132.        
  133.         if(!messageBundles.contains(resourceBundle)) {
  134.             return false;
  135.         }
  136.        
  137.         messageBundles.remove(resourceBundle);
  138.         return true;
  139.     }
  140. }