1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package javax.jdo.spi;
19
20 import java.util.*;
21 import java.text.MessageFormat;
22 import java.security.AccessController;
23 import java.security.PrivilegedAction;
24
25 import javax.jdo.JDOFatalInternalException;
26
27 /** Helper class for constructing messages from bundles. The intended usage
28 * of this class is to construct a new instance bound to a bundle, as in
29 * <P>
30 * <code>I18NHelper msg = I18NHelper.getInstance("javax.jdo.Bundle");</code>
31 * <P>
32 * This call uses the class loader that loaded the I18NHelper class to find
33 * the specified Bundle. The class provides two overloaded getInstance
34 * methods allowing to specify a different class loader:
35 * {@link #getInstance(Class cls)} looks for a bundle
36 * called "Bundle.properties" located in the package of the specified class
37 * object and {@link #getInstance(String bundleName,ClassLoader loader)}
38 * uses the specified class loader to find the bundle.
39 * <P>
40 * Subsequently, instance methods can be used to format message strings
41 * using the text from the bundle, as in
42 * <P>
43 * <code>throw new JDOFatalInternalException (msg.msg("ERR_NoMetadata",
44 * cls.getName()));</code>
45 * @since 1.0.1
46 * @version 1.1
47 */
48 public class I18NHelper {
49
50 /** Bundles that have already been loaded
51 */
52 private static Hashtable<String,ResourceBundle>
53 bundles = new Hashtable<String,ResourceBundle>();
54
55 /** Helper instances that have already been created
56 */
57 private static Hashtable<String,I18NHelper>
58 helpers = new Hashtable<String,I18NHelper>();
59
60 /** The default locale for this VM.
61 */
62 private static Locale locale = Locale.getDefault();
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";
74
75 /** Constructor */
76 private I18NHelper() {
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 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 = AccessController.doPrivileged (
111 new PrivilegedAction<ClassLoader> () {
112 public ClassLoader 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
138
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
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)
236 {
237 if (loader != null) {
238 messages = ResourceBundle.getBundle(bundleName, locale, loader);
239 } else {
240
241 messages = ResourceBundle.getBundle(bundleName, locale,
242 getSystemClassLoaderPrivileged());
243 }
244 bundles.put(bundleName, messages);
245 }
246 return messages;
247 }
248
249 /** Assert resources available
250 * @since 1.1
251 * @throws JDOFatalInternalException if the resource bundle could not
252 * be loaded during construction.
253 */
254 private void assertBundle () {
255 if (failure != null)
256 throw new JDOFatalInternalException (
257 "No resources could be found for bundle:\"" +
258 bundle + "\" ", failure);
259 }
260
261 /** Assert resources available
262 * @param key the message key
263 * @since 1.0.2
264 * @throws JDOFatalInternalException if the resource bundle could not
265 * be loaded during construction.
266 */
267 private void assertBundle (String key) {
268 if (failure != null)
269 throw new JDOFatalInternalException (
270 "No resources could be found to annotate error message key:\"" +
271 key + "\"", failure);
272 }
273
274 /**
275 * Returns message as <code>String</code>
276 * @param messages the resource bundle
277 * @param messageKey the message key
278 * @return the resolved message text
279 */
280 final private static String getMessage(ResourceBundle messages, String messageKey)
281 {
282 return messages.getString(messageKey);
283 }
284
285 /**
286 * Formats message by adding array of arguments
287 * @param messages the resource bundle
288 * @param messageKey the message key
289 * @param msgArgs an array of arguments to substitute into the message
290 * @return the resolved message text
291 */
292 final private static String getMessage(ResourceBundle messages,
293 String messageKey, Object[] msgArgs)
294 {
295 for (int i=0; i<msgArgs.length; i++) {
296 if (msgArgs[i] == null) msgArgs[i] = "";
297 }
298 MessageFormat formatter = new MessageFormat(messages.getString(messageKey));
299 return formatter.format(msgArgs);
300 }
301
302 /**
303 * Formats message by adding an <code>Object</code> argument.
304 * @param messages the resource bundle
305 * @param messageKey the message key
306 * @param arg the argument
307 * @return the resolved message text
308 */
309 final private static String getMessage(ResourceBundle messages,
310 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,
325 String messageKey, Object arg1, 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,
341 String messageKey, Object arg1, 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,
355 String messageKey, int arg)
356 {
357 Object []args = {new Integer(arg)};
358 return getMessage(messages, messageKey, args);
359 }
360
361 /**
362 * Formats message by adding a <code>boolean</code> as an argument.
363 * @param messages the resource bundle
364 * @param messageKey the message key
365 * @param arg the argument
366 * @return the resolved message text
367 */
368 final private static String getMessage(ResourceBundle messages,
369 String messageKey, boolean arg)
370 {
371 Object []args = {String.valueOf(arg)};
372 return getMessage(messages, messageKey, args);
373 }
374
375 /**
376 * Returns the package portion of the specified class.
377 * @param className the name of the class from which to extract the
378 * package
379 * @return package portion of the specified class
380 */
381 final private static String getPackageName(final String className)
382 {
383 final int index = className.lastIndexOf('.');
384 return ((index != -1) ? className.substring(0, index) : "");
385 }
386
387 /**
388 * Get the system class loader. This must be done in a doPrivileged
389 * block because of security.
390 */
391 private static ClassLoader getSystemClassLoaderPrivileged() {
392 return AccessController.doPrivileged (
393 new PrivilegedAction<ClassLoader> () {
394 public ClassLoader run () {
395 return ClassLoader.getSystemClassLoader();
396 }
397 }
398 );
399 }
400 }