View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software 
12   * distributed under the License is distributed on an "AS IS" BASIS, 
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
14   * See the License for the specific language governing permissions and 
15   * limitations under the License.
16   */
17  
18  package org.apache.jdo.util;
19  
20  import java.util.*;
21  import java.text.MessageFormat;
22  import java.security.AccessController;
23  import java.security.PrivilegedAction;
24  
25  /*** Helper class for constructing messages from bundles.  The intended usage
26   * of this class is to construct a new instance bound to a bundle, as in
27   * <P>
28   * <code>I18NHelper msg = I18NHelper.getInstance("org.apache.jdo.util.jdo.Bundle");</code>
29   * <P>
30   * This call uses the class loader that loaded the I18NHelper class to find
31   * the specified Bundle. The class provides two overloaded getInstance
32   * methods allowing to specify a different class loader: 
33   * {@link #getInstance(Class cls)} looks for a bundle
34   * called "Bundle.properties" located in the package of the specified class 
35   * object and {@link #getInstance(String bundleName,ClassLoader loader)} 
36   * uses the specified class loader to find the bundle.
37   * <P>
38   * Subsequently, instance methods can be used to format message strings 
39   * using the text from the bundle, as in 
40   * <P>
41   * <code>throw new JDOFatalInternalException (msg.msg("ERR_NoMetadata", cls.getName()));</code>
42   * @since 1.0.1
43   * @version 1.1
44   */        
45  public class I18NHelper {
46  
47      /*** Bundles that have already been loaded 
48       */
49      private static Hashtable    bundles = new Hashtable();
50      
51      /*** Helper instances that have already been created 
52       */
53      private static Hashtable    helpers = new Hashtable();
54      
55      /*** The default locale for this VM.
56       */
57      private static Locale       locale = Locale.getDefault();
58  
59      /*** The name of the bundle used by this instance of the helper.
60       */
61      private final String        bundleName;
62  
63      /*** The bundle used by this instance of the helper.
64       */
65      private ResourceBundle      bundle = null;
66  
67      /*** Throwable if ResourceBundle couldn't be loaded
68       */
69      private Throwable           failure = null;
70  
71      /*** The unqualified standard name of a bundle. */
72      private static final String bundleSuffix = ".Bundle";    // NOI18N
73  
74      /*** Constructor */
75      private I18NHelper() {
76          this.bundleName = null;
77      }
78  
79      /*** Constructor for an instance bound to a bundle.
80       * @param bundleName the name of the resource bundle
81       * @param loader the class loader from which to load the resource
82       * bundle
83       */
84      private I18NHelper (String bundleName, ClassLoader loader) {
85          this.bundleName = bundleName;
86          try {
87              bundle = loadBundle (bundleName, loader);
88          }
89          catch (Throwable e) {
90              failure = e;
91          }
92      }
93      
94      /*** An instance bound to a bundle. This method uses the current class 
95       * loader to find the bundle.
96       * @param bundleName the name of the bundle
97       * @return the helper instance bound to the bundle
98       */
99      public static I18NHelper getInstance (String bundleName) {
100         return getInstance (bundleName, I18NHelper.class.getClassLoader());
101     }
102 
103     /*** An instance bound to a bundle. This method figures out the bundle name
104      * for the class object's package and uses the class' class loader to
105      * find the bundle. Note, the specified class object must not be
106      * <code>null</code>.
107      * @param cls the class object from which to load the resource bundle
108      * @return the helper instance bound to the bundle
109      */
110     public static I18NHelper getInstance (final Class cls) {
111         ClassLoader classLoader = (ClassLoader) AccessController.doPrivileged (
112             new PrivilegedAction () {
113                 public Object run () {
114                     return cls.getClassLoader();
115                 }
116             }
117             );
118         String bundle = getPackageName (cls.getName()) + bundleSuffix;
119         return getInstance (bundle, classLoader);
120     }
121 
122     /*** An instance bound to a bundle. This method uses the specified class
123      * loader to find the bundle. Note, the specified class loader must not
124      * be <code>null</code>.
125      * @param bundleName the name of the bundle
126      * @param loader the class loader from which to load the resource
127      * bundle
128      * @return the helper instance bound to the bundle
129      */
130     public static I18NHelper getInstance (String bundleName, 
131                                           ClassLoader loader) {
132         I18NHelper helper = (I18NHelper) helpers.get (bundleName);
133         if (helper != null) {
134             return helper;
135         }
136         helper = new I18NHelper(bundleName, loader);
137         helpers.put (bundleName, helper);
138         // if two threads simultaneously create the same helper, return the first
139         // one to be put into the Hashtable.  The other will be garbage collected.
140         return (I18NHelper) helpers.get (bundleName);
141     }
142 
143     /*** Message formatter
144      * @param messageKey the message key
145      * @return the resolved message text
146      */
147     public String msg (String messageKey) {
148         assertBundle (messageKey);
149         return getMessage (bundle, messageKey);
150     }
151 
152     /*** Message formatter
153      * @param messageKey the message key
154      * @param arg1 the first argument
155      * @return the resolved message text
156      */
157     public String msg (String messageKey, Object arg1) {
158         assertBundle (messageKey);
159         return getMessage (bundle, messageKey, arg1);
160     }
161 
162     /*** Message formatter
163      * @param messageKey the message key
164      * @param arg1 the first argument
165      * @param arg2 the second argument
166      * @return the resolved message text
167      */
168     public String msg (String messageKey, Object arg1, Object arg2) {
169         assertBundle (messageKey);
170         return getMessage (bundle, messageKey, arg1, arg2);
171     }
172 
173     /*** Message formatter
174      * @param messageKey the message key
175      * @param arg1 the first argument
176      * @param arg2 the second argument
177      * @param arg3 the third argument
178      * @return the resolved message text
179      */
180     public String msg (String messageKey, Object arg1, Object arg2, Object arg3) {
181         assertBundle (messageKey);
182         return getMessage (bundle, messageKey, arg1, arg2, arg3);
183     }
184 
185     /*** Message formatter
186      * @param messageKey the message key
187      * @param args the array of arguments
188      * @return the resolved message text
189      */
190     public String msg (String messageKey, Object[] args) {
191         assertBundle (messageKey);
192         return getMessage (bundle, messageKey, args);
193     }
194 
195     /*** Message formatter
196      * @param messageKey the message key
197      * @param arg the argument
198      * @return the resolved message text
199      */
200     public String msg (String messageKey, int arg) {
201         assertBundle (messageKey);
202         return getMessage(bundle, messageKey, arg);
203     }
204     
205     /*** Message formatter
206      * @param messageKey the message key
207      * @param arg the argument
208      * @return the resolved message text
209      */
210     public String msg (String messageKey, boolean arg) {
211         assertBundle (messageKey);
212         return getMessage(bundle, messageKey, arg);
213     }
214     
215     /*** Returns the resource bundle used by this I18NHelper.
216      * @return the associated resource bundle
217      * @since 1.1
218      */
219     public ResourceBundle getResourceBundle () {
220         assertBundle ();
221         return bundle;
222     }
223     
224     //========= Internal helper methods ==========
225 
226     /***
227      * Load ResourceBundle by bundle name
228      * @param bundleName the name of the bundle
229      * @param loader the class loader from which to load the resource bundle
230      * @return  the ResourceBundle
231      */
232     final private static ResourceBundle loadBundle(
233         String bundleName, ClassLoader loader) {
234         ResourceBundle messages = (ResourceBundle)bundles.get(bundleName);
235 
236         if (messages == null) //not found as loaded - add
237         {
238             messages = ResourceBundle.getBundle(bundleName, locale, loader);
239             bundles.put(bundleName, messages);
240         }
241         return messages;
242     }
243 
244     /*** Assert resources available
245      * @since 1.1
246      * @throws RuntimeException if the resource bundle could not
247      * be loaded during construction.
248      */
249     private void assertBundle () {
250         if (failure != null)
251             throw new RuntimeException (
252                 "No resources could be found for bundle:\"" + //NOI18N
253                 bundle + "\" " + failure); //NOI18N
254     }
255     
256     /*** Assert resources available
257      * @param key the message key 
258      * @since 1.0.2
259      * @throws RuntimeException if the resource bundle could not
260      * be loaded during construction.
261      */
262     private void assertBundle (String key) {
263         if (failure != null)
264             throw new RuntimeException (
265                 "No resources could be found to annotate error message key:\"" + //NOI18N
266                 key + "\" " + failure); //NOI18N
267     }
268 
269     /***
270      * Returns message as <code>String</code>
271      * @param messages the resource bundle
272      * @param messageKey the message key
273      * @return the resolved message text
274      */
275     final private static String getMessage(ResourceBundle messages, String messageKey) 
276     {
277         return messages.getString(messageKey);
278     }
279 
280     /***
281      * Formats message by adding array of arguments
282      * @param messages the resource bundle
283      * @param messageKey the message key
284      * @param msgArgs an array of arguments to substitute into the message
285      * @return the resolved message text
286      */
287     final private static String getMessage(ResourceBundle messages, String messageKey, Object msgArgs[]) 
288     {
289         for (int i=0; i<msgArgs.length; i++) {
290             if (msgArgs[i] == null) msgArgs[i] = ""; // NOI18N
291         }
292         MessageFormat formatter = new MessageFormat(messages.getString(messageKey));
293         return formatter.format(msgArgs);
294     }
295     
296     /***
297      * Formats message by adding an <code>Object</code> argument.
298      * @param messages the resource bundle
299      * @param messageKey the message key
300      * @param arg the argument
301      * @return the resolved message text
302      */
303     final private static String getMessage(ResourceBundle messages, String messageKey, Object arg) 
304     {
305         Object []args = {arg};
306         return getMessage(messages, messageKey, args);
307     }
308     
309     /***
310      * Formats message by adding two <code>Object</code> arguments.
311      * @param messages the resource bundle
312      * @param messageKey the message key
313      * @param arg1 the first argument
314      * @param arg2 the second argument
315      * @return the resolved message text
316      */
317     final private static String getMessage(ResourceBundle messages, String messageKey, Object arg1,
318                                    Object arg2) 
319     {
320         Object []args = {arg1, arg2};
321         return getMessage(messages, messageKey, args);
322     }
323     
324     /***
325      * Formats message by adding three <code>Object</code> arguments.
326      * @param messages the resource bundle
327      * @param messageKey the message key
328      * @param arg1 the first argument
329      * @param arg2 the second argument
330      * @param arg3 the third argument
331      * @return the resolved message text
332      */
333     final private static String getMessage(ResourceBundle messages, String messageKey, Object arg1,
334                                    Object arg2, Object arg3) 
335     {
336         Object []args = {arg1, arg2, arg3};
337         return getMessage(messages, messageKey, args);
338     }
339 
340     /***
341      * Formats message by adding an <code>int</code> as an argument.
342      * @param messages the resource bundle
343      * @param messageKey the message key
344      * @param arg the argument
345      * @return the resolved message text
346      */
347     final private static String getMessage(ResourceBundle messages, String messageKey, int arg) 
348     {
349         Object []args = {new Integer(arg)};
350         return getMessage(messages, messageKey, args);
351     }
352     
353     /***
354      * Formats message by adding a <code>boolean</code> as an argument.
355      * @param messages the resource bundle
356      * @param messageKey the message key
357      * @param arg the argument
358      * @return the resolved message text
359      */
360     final private static String getMessage(ResourceBundle messages, String messageKey, boolean arg) 
361     {
362         Object []args = {String.valueOf(arg)};
363         return getMessage(messages, messageKey, args);
364     }
365 
366     /***  
367      * Returns the package portion of the specified class.
368      * @param className the name of the class from which to extract the 
369      * package 
370      * @return package portion of the specified class
371      */   
372     final private static String getPackageName(final String className)
373     { 
374         final int index = className.lastIndexOf('.');
375         return ((index != -1) ? className.substring(0, index) : ""); // NOI18N
376     }
377 }