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