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