2
Copyright 2010 Sun Microsystems, Inc.
3
All rights reserved. Use is subject to license terms.
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.
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.
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
19
package com.mysql.clusterj.core.util;
22
import java.text.MessageFormat;
23
import java.security.AccessController;
24
import java.security.PrivilegedAction;
26
import com.mysql.clusterj.ClusterJFatalInternalException;
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
31
* <code>I18NHelper local =
32
* I18NHelper.getInstance("com.mysql.clusterj.core.Bundle");</code>
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.
42
* Subsequently, instance methods can be used to format message strings
43
* using the text from the bundle, as in
45
* <code>throw new JDOFatalInternalException (local.message("ERR_NoMetadata",
46
* cls.getName()));</code>
48
public class I18NHelper {
51
private static Logger logger = LoggerFactoryService.getFactory()
52
.getInstance(I18NHelper.class);
54
/** Bundles that have already been loaded
56
private static Hashtable<String, ResourceBundle> bundles = new Hashtable<String, ResourceBundle>();
58
/** Helper instances that have already been created
60
private static Hashtable<String, I18NHelper> helpers = new Hashtable<String, I18NHelper>();
62
/** The default locale for this VM.
64
private static Locale locale = Locale.getDefault();
66
/** The name of the bundle used by this instance of the helper.
68
private final String bundleName;
70
/** The bundle used by this instance of the helper.
72
private ResourceBundle bundle = null;
74
/** Throwable if ResourceBundle couldn't be loaded
76
private Throwable failure = null;
78
/** The unqualified standard name of a bundle. */
79
private static final String bundleSuffix = ".Bundle"; // NOI18N
82
private I18NHelper() {
83
this.bundleName = null;
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
91
private I18NHelper (String bundleName, ClassLoader loader) {
92
this.bundleName = bundleName;
94
bundle = loadBundle (bundleName, bundleName, loader);
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
106
public static I18NHelper getInstance (String bundleName) {
107
return getInstance (bundleName, I18NHelper.class.getClassLoader());
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
114
* @param cls the class object from which to load the resource bundle
115
* @return the helper instance bound to the bundle
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();
125
String bundle = getPackageName (cls.getName()) + bundleSuffix;
126
return getInstance (bundle, classLoader);
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
135
* @return the helper instance bound to the bundle
137
public static I18NHelper getInstance (String bundleName,
138
ClassLoader loader) {
139
I18NHelper helper = helpers.get (bundleName);
140
if (helper != null) {
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);
150
/** Message formatter
151
* @param messageKey the message key
152
* @return the resolved message text
154
public String message (String messageKey) {
155
assertBundle (messageKey);
156
return getMessage (bundle, messageKey);
159
/** Message formatter
160
* @param messageKey the message key
161
* @param arg1 the first argument
162
* @return the resolved message text
164
public String message (String messageKey, Object arg1) {
165
assertBundle (messageKey);
166
return getMessage (bundle, messageKey, arg1);
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
175
public String message (String messageKey, Object arg1, Object arg2) {
176
assertBundle (messageKey);
177
return getMessage (bundle, messageKey, arg1, arg2);
180
/** Message formatter
181
* @param messageKey the message key
182
* @param args the array of arguments
183
* @return the resolved message text
185
public String message (String messageKey, Object... args) {
186
assertBundle (messageKey);
187
return getMessage (bundle, messageKey, args);
190
/** Message formatter
191
* @param messageKey the message key
192
* @param arg the argument
193
* @return the resolved message text
195
public String message (String messageKey, int arg) {
196
assertBundle (messageKey);
197
return getMessage(bundle, messageKey, arg);
200
/** Message formatter
201
* @param messageKey the message key
202
* @param arg the argument
203
* @return the resolved message text
205
public String message (String messageKey, boolean arg) {
206
assertBundle (messageKey);
207
return getMessage(bundle, messageKey, arg);
210
/** Returns the resource bundle used by this I18NHelper.
211
* @return the associated resource bundle
213
public ResourceBundle getResourceBundle () {
218
//========= Internal helper methods ==========
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
226
final private static ResourceBundle loadBundle(
227
String original, String bundleName, ClassLoader loader) {
228
ResourceBundle messages = bundles.get(bundleName);
230
if (messages == null) //not found as loaded - add
233
if (loader != null) {
234
messages = ResourceBundle.getBundle(bundleName, locale, loader);
236
// the library was loaded by the boostrap class loader
237
messages = ResourceBundle.getBundle(bundleName, locale,
238
getSystemClassLoaderPrivileged());
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);
248
messages = loadBundle(original, superBundleName, loader);
254
/** Assert resources available
255
* @throws JDOFatalInternalException if the resource bundle could not
256
* be loaded during construction.
258
private void assertBundle () {
260
throw new ClusterJFatalInternalException (
261
"No resources could be found for bundle:\"" +
262
bundleName + "\" ", failure);
265
/** Assert resources available
266
* @param key the message key
267
* @throws JDOFatalInternalException if the resource bundle could not
268
* be loaded during construction.
270
private void assertBundle (String key) {
272
throw new ClusterJFatalInternalException (
273
"No resources could be found for bundle: " + bundleName
274
+ " to annotate error message key:\""
275
+ key + "\"", failure);
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
284
final private static String getMessage(ResourceBundle messages, String messageKey)
286
return messages.getString(messageKey);
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
296
final private static String getMessage(ResourceBundle messages,
297
String messageKey, Object[] msgArgs)
299
for (int i=0; i<msgArgs.length; i++) {
300
if (msgArgs[i] == null) msgArgs[i] = ""; // NOI18N
302
MessageFormat formatter = new MessageFormat(messages.getString(messageKey));
303
return formatter.format(msgArgs);
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
313
final private static String getMessage(ResourceBundle messages,
314
String messageKey, Object arg)
316
Object []args = {arg};
317
return getMessage(messages, messageKey, args);
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
328
final private static String getMessage(ResourceBundle messages,
329
String messageKey, Object arg1, Object arg2)
331
Object []args = {arg1, arg2};
332
return getMessage(messages, messageKey, args);
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
342
final private static String getMessage(ResourceBundle messages,
343
String messageKey, int arg)
345
Object []args = {new Integer(arg)};
346
return getMessage(messages, messageKey, args);
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
356
final private static String getMessage(ResourceBundle messages,
357
String messageKey, boolean arg)
359
Object []args = {String.valueOf(arg)};
360
return getMessage(messages, messageKey, args);
364
* Returns the package portion of the specified class.
365
* @param className the name of the class from which to extract the
367
* @return package portion of the specified class
369
final private static String getPackageName(final String className)
371
final int index = className.lastIndexOf('.');
372
return ((index != -1) ? className.substring(0, index) : ""); // NOI18N
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
381
private static String removeDirectoryName(String bundleName) {
383
int lastDot = bundleName.lastIndexOf(".");
384
String packageName = bundleName.substring(0, lastDot);
385
String suffix = bundleName.substring(lastDot);
386
int index = packageName.lastIndexOf(".");
390
String superPackageName = packageName.substring(0, index);
391
result = superPackageName + suffix;
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);
404
* Get the system class loader. This must be done in a doPrivileged
405
* block because of security.
407
private static ClassLoader getSystemClassLoaderPrivileged() {
408
return AccessController.doPrivileged (
409
new PrivilegedAction<ClassLoader> () {
410
public ClassLoader run () {
411
return ClassLoader.getSystemClassLoader();