]> review.fuel-infra Code Review - packages/trusty/mysql-wsrep-5.6.git/blob
5625deee2e0447fd605137e5cf62e7401747ef96
[packages/trusty/mysql-wsrep-5.6.git] /
1 /*
2    Copyright 2010 Sun Microsystems, Inc.
3    All rights reserved. Use is subject to license terms.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; version 2 of the License.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
17 */
18
19 package com.mysql.clusterj.core.util;
20
21 import java.util.*;
22 import java.text.MessageFormat;
23 import java.security.AccessController;
24 import java.security.PrivilegedAction;
25
26 import com.mysql.clusterj.ClusterJFatalInternalException;
27
28 /** Helper class for constructing messages from bundles.  The intended usage
29  * of this class is to construct a new instance bound to a bundle, as in
30  * <P>
31  * <code>I18NHelper local = 
32  *  I18NHelper.getInstance("com.mysql.clusterj.core.Bundle");</code>
33  * <P>
34  * This call uses the class loader that loaded the I18NHelper class to find
35  * the specified Bundle. The class provides two overloaded getInstance
36  * methods allowing to specify a different class loader: 
37  * {@link #getInstance(Class cls)} looks for a bundle
38  * called "Bundle.properties" located in the package of the specified class 
39  * object and {@link #getInstance(String bundleName,ClassLoader loader)} 
40  * uses the specified class loader to find the bundle.
41  * <P>
42  * Subsequently, instance methods can be used to format message strings 
43  * using the text from the bundle, as in 
44  * <P>
45  * <code>throw new JDOFatalInternalException (local.message("ERR_NoMetadata", 
46  * cls.getName()));</code>
47  */        
48 public class I18NHelper {
49
50     /** The logger */
51     private static Logger logger = LoggerFactoryService.getFactory()
52             .getInstance(I18NHelper.class);
53
54     /** Bundles that have already been loaded 
55      */
56     private static Hashtable<String, ResourceBundle> bundles = new Hashtable<String, ResourceBundle>();
57     
58     /** Helper instances that have already been created 
59      */
60     private static Hashtable<String, I18NHelper> helpers = new Hashtable<String, I18NHelper>();
61     
62     /** The default locale for this VM.
63      */
64     private static Locale       locale = Locale.getDefault();
65
66     /** The name of the bundle used by this instance of the helper.
67      */
68     private final String        bundleName;
69
70     /** The bundle used by this instance of the helper.
71      */
72     private ResourceBundle      bundle = null;
73
74     /** Throwable if ResourceBundle couldn't be loaded
75      */
76     private Throwable           failure = null;
77
78     /** The unqualified standard name of a bundle. */
79     private static final String bundleSuffix = ".Bundle";    // NOI18N
80
81     /** Constructor */
82     private I18NHelper() {
83         this.bundleName = null;
84     }
85
86     /** Constructor for an instance bound to a bundle.
87      * @param bundleName the name of the resource bundle
88      * @param loader the class loader from which to load the resource
89      * bundle
90      */
91     private I18NHelper (String bundleName, ClassLoader loader) {
92         this.bundleName = bundleName;
93         try {
94             bundle = loadBundle (bundleName, bundleName, loader);
95         }
96         catch (Throwable e) {
97             failure = e;
98         }
99     }
100     
101     /** An instance bound to a bundle. This method uses the current class 
102      * loader to find the bundle.
103      * @param bundleName the name of the bundle
104      * @return the helper instance bound to the bundle
105      */
106     public static I18NHelper getInstance (String bundleName) {
107         return getInstance (bundleName, I18NHelper.class.getClassLoader());
108     }
109
110     /** An instance bound to a bundle. This method figures out the bundle name
111      * for the class object's package and uses the class' class loader to
112      * find the bundle. Note, the specified class object must not be
113      * <code>null</code>.
114      * @param cls the class object from which to load the resource bundle
115      * @return the helper instance bound to the bundle
116      */
117     public static I18NHelper getInstance (final Class<?> cls) {
118         ClassLoader classLoader = AccessController.doPrivileged (
119             new PrivilegedAction<ClassLoader> () {
120                 public ClassLoader run () {
121                     return cls.getClassLoader();
122                 }
123             }
124             );
125         String bundle = getPackageName (cls.getName()) + bundleSuffix;
126         return getInstance (bundle, classLoader);
127     }
128
129     /** An instance bound to a bundle. This method uses the specified class
130      * loader to find the bundle. Note, the specified class loader must not
131      * be <code>null</code>.
132      * @param bundleName the name of the bundle
133      * @param loader the class loader from which to load the resource
134      * bundle
135      * @return the helper instance bound to the bundle
136      */
137     public static I18NHelper getInstance (String bundleName, 
138                                           ClassLoader loader) {
139         I18NHelper helper = helpers.get (bundleName);
140         if (helper != null) {
141             return helper;
142         }
143         helper = new I18NHelper(bundleName, loader);
144         helpers.put (bundleName, helper);
145         // if two threads simultaneously create the same helper, return the first
146         // one to be put into the Hashtable.  The other will be garbage collected.
147         return helpers.get (bundleName);
148     }
149
150     /** Message formatter
151      * @param messageKey the message key
152      * @return the resolved message text
153      */
154     public String message (String messageKey) {
155         assertBundle (messageKey);
156         return getMessage (bundle, messageKey);
157     }
158
159     /** Message formatter
160      * @param messageKey the message key
161      * @param arg1 the first argument
162      * @return the resolved message text
163      */
164     public String message (String messageKey, Object arg1) {
165         assertBundle (messageKey);
166         return getMessage (bundle, messageKey, arg1);
167     }
168
169     /** Message formatter
170      * @param messageKey the message key
171      * @param arg1 the first argument
172      * @param arg2 the second argument
173      * @return the resolved message text
174      */
175     public String message (String messageKey, Object arg1, Object arg2) {
176         assertBundle (messageKey);
177         return getMessage (bundle, messageKey, arg1, arg2);
178     }
179
180     /** Message formatter
181      * @param messageKey the message key
182      * @param args the array of arguments
183      * @return the resolved message text
184      */
185     public String message (String messageKey, Object... args) {
186         assertBundle (messageKey);
187         return getMessage (bundle, messageKey, args);
188     }
189
190     /** Message formatter
191      * @param messageKey the message key
192      * @param arg the argument
193      * @return the resolved message text
194      */
195     public String message (String messageKey, int arg) {
196         assertBundle (messageKey);
197         return getMessage(bundle, messageKey, arg);
198     }
199     
200     /** Message formatter
201      * @param messageKey the message key
202      * @param arg the argument
203      * @return the resolved message text
204      */
205     public String message (String messageKey, boolean arg) {
206         assertBundle (messageKey);
207         return getMessage(bundle, messageKey, arg);
208     }
209     
210     /** Returns the resource bundle used by this I18NHelper.
211      * @return the associated resource bundle
212      */
213     public ResourceBundle getResourceBundle () {
214         assertBundle ();
215         return bundle;
216     }
217     
218     //========= Internal helper methods ==========
219
220     /**
221      * Load ResourceBundle by bundle name
222      * @param bundleName the name of the bundle
223      * @param loader the class loader from which to load the resource bundle
224      * @return  the ResourceBundle
225      */
226     final private static ResourceBundle loadBundle(
227                 String original, String bundleName, ClassLoader loader) {
228         ResourceBundle messages = bundles.get(bundleName);
229
230         if (messages == null) //not found as loaded - add
231         {
232             try {
233                 if (loader != null) {
234                     messages = ResourceBundle.getBundle(bundleName, locale, loader);
235                 } else {
236                     // the library was loaded by the boostrap class loader
237                     messages = ResourceBundle.getBundle(bundleName, locale,
238                             getSystemClassLoaderPrivileged());
239                 }
240                 bundles.put(bundleName, messages);
241             } catch (java.util.MissingResourceException ex) {
242                 // recursively try to find the Bundle in the next higher package
243                 String superBundleName = removeDirectoryName(bundleName);
244                 if (superBundleName == null) {
245                     throw new ClusterJFatalInternalException(
246                             "Missing resource bundle " + original);
247                 }
248                 messages = loadBundle(original, superBundleName, loader);
249             }
250         }
251         return messages;
252     }
253
254     /** Assert resources available
255      * @throws JDOFatalInternalException if the resource bundle could not
256      * be loaded during construction.
257      */
258     private void assertBundle () {
259         if (failure != null)
260             throw new ClusterJFatalInternalException (
261                 "No resources could be found for bundle:\"" + 
262                 bundleName + "\" ", failure);
263     }
264     
265     /** Assert resources available
266      * @param key the message key 
267      * @throws JDOFatalInternalException if the resource bundle could not
268      * be loaded during construction.
269      */
270     private void assertBundle (String key) {
271         if (failure != null)
272             throw new ClusterJFatalInternalException (
273                 "No resources could be found for bundle: " + bundleName
274                 + " to annotate error message key:\""
275                 + key + "\"", failure);
276     }
277
278     /**
279      * Returns message as <code>String</code>
280      * @param messages the resource bundle
281      * @param messageKey the message key
282      * @return the resolved message text
283      */
284     final private static String getMessage(ResourceBundle messages, String messageKey) 
285     {
286         return messages.getString(messageKey);
287     }
288
289     /**
290      * Formats message by adding array of arguments
291      * @param messages the resource bundle
292      * @param messageKey the message key
293      * @param msgArgs an array of arguments to substitute into the message
294      * @return the resolved message text
295      */
296     final private static String getMessage(ResourceBundle messages, 
297             String messageKey, Object[] msgArgs) 
298     {
299         for (int i=0; i<msgArgs.length; i++) {
300             if (msgArgs[i] == null) msgArgs[i] = ""; // NOI18N
301         }
302         MessageFormat formatter = new MessageFormat(messages.getString(messageKey));
303         return formatter.format(msgArgs);
304     }
305     
306     /**
307      * Formats message by adding an <code>Object</code> argument.
308      * @param messages the resource bundle
309      * @param messageKey the message key
310      * @param arg the argument
311      * @return the resolved message text
312      */
313     final private static String getMessage(ResourceBundle messages, 
314             String messageKey, Object arg) 
315     {
316         Object []args = {arg};
317         return getMessage(messages, messageKey, args);
318     }
319     
320     /**
321      * Formats message by adding two <code>Object</code> arguments.
322      * @param messages the resource bundle
323      * @param messageKey the message key
324      * @param arg1 the first argument
325      * @param arg2 the second argument
326      * @return the resolved message text
327      */
328     final private static String getMessage(ResourceBundle messages, 
329             String messageKey, Object arg1, Object arg2) 
330     {
331         Object []args = {arg1, arg2};
332         return getMessage(messages, messageKey, args);
333     }
334     
335     /**
336      * Formats message by adding an <code>int</code> as an argument.
337      * @param messages the resource bundle
338      * @param messageKey the message key
339      * @param arg the argument
340      * @return the resolved message text
341      */
342     final private static String getMessage(ResourceBundle messages, 
343             String messageKey, int arg) 
344     {
345         Object []args = {new Integer(arg)};
346         return getMessage(messages, messageKey, args);
347     }
348     
349     /**
350      * Formats message by adding a <code>boolean</code> as an argument.
351      * @param messages the resource bundle
352      * @param messageKey the message key
353      * @param arg the argument
354      * @return the resolved message text
355      */
356     final private static String getMessage(ResourceBundle messages, 
357             String messageKey, boolean arg) 
358     {
359         Object []args = {String.valueOf(arg)};
360         return getMessage(messages, messageKey, args);
361     }
362
363     /**  
364      * Returns the package portion of the specified class.
365      * @param className the name of the class from which to extract the 
366      * package 
367      * @return package portion of the specified class
368      */   
369     final private static String getPackageName(final String className)
370     { 
371         final int index = className.lastIndexOf('.');
372         return ((index != -1) ? className.substring(0, index) : ""); // NOI18N
373     }
374
375     /** Return the bundle name of the super package. For example,
376      * if the bundleName is com.mysql.cluster.util.deeper.Bundle,
377      * return com.mysql.cluster.util.Bundle.
378      * @param bundleName the bundle name
379      * @return the bundle name of the super package
380      */
381     private static String removeDirectoryName(String bundleName) {
382         String result;
383         int lastDot = bundleName.lastIndexOf(".");
384         String packageName = bundleName.substring(0, lastDot);
385         String suffix = bundleName.substring(lastDot);
386         int index = packageName.lastIndexOf(".");
387         if (index == -1) {
388             return null;
389         }
390         String superPackageName = packageName.substring(0, index);
391         result = superPackageName + suffix;
392
393         if (logger.isDebugEnabled()) {
394             logger.debug("bundleName is: " + bundleName + 
395                     "; superPackageName is: " + superPackageName + 
396                     "; suffix is: " + suffix + 
397                     "; packageName is: " + packageName + 
398                     "; returning: " + result);
399         }
400         return result;
401     }
402
403     /**
404      * Get the system class loader. This must be done in a doPrivileged 
405      * block because of security.
406      */
407     private static ClassLoader getSystemClassLoaderPrivileged() {
408         return AccessController.doPrivileged (
409             new PrivilegedAction<ClassLoader> () {
410                 public ClassLoader run () {
411                     return ClassLoader.getSystemClassLoader();
412                 }
413             }
414         );
415     }
416 }