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