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