View Javadoc

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