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