1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts.util;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22
23 import java.io.Serializable;
24
25 import java.text.MessageFormat;
26
27 import java.util.HashMap;
28 import java.util.Locale;
29
30 /***
31 * General purpose abstract class that describes an API for retrieving
32 * Locale-sensitive messages from underlying resource locations of an
33 * unspecified design, and optionally utilizing the <code>MessageFormat</code>
34 * class to produce internationalized messages with parametric replacement.
35 * <p> Calls to <code>getMessage()</code> variants without a
36 * <code>Locale</code> argument are presumed to be requesting a message string
37 * in the default <code>Locale</code> for this JVM. <p> Calls to
38 * <code>getMessage()</code> with an unknown key, or an unknown
39 * <code>Locale</code> will return <code>null</code> if the
40 * <code>returnNull</code> property is set to <code>true</code>. Otherwise, a
41 * suitable error message will be returned instead. <p> <strong>IMPLEMENTATION
42 * NOTE</strong> - Classes that extend this class must be Serializable so that
43 * instances may be used in distributable application server environments.
44 *
45 * @version $Rev: 421119 $ $Date: 2005-08-29 23:57:50 -0400 (Mon, 29 Aug 2005)
46 * $
47 */
48 public abstract class MessageResources implements Serializable {
49
50
51 /***
52 * Commons Logging instance.
53 */
54 protected static Log log = LogFactory.getLog(MessageResources.class);
55
56
57
58 /***
59 * The default MessageResourcesFactory used to create MessageResources
60 * instances.
61 */
62 protected static MessageResourcesFactory defaultFactory = null;
63
64 /***
65 * The configuration parameter used to initialize this MessageResources.
66 */
67 protected String config = null;
68
69 /***
70 * The default Locale for our environment.
71 */
72 protected Locale defaultLocale = Locale.getDefault();
73
74 /***
75 * The <code>MessageResourcesFactory</code> that created this instance.
76 */
77 protected MessageResourcesFactory factory = null;
78
79 /***
80 * The set of previously created MessageFormat objects, keyed by the key
81 * computed in <code>messageKey()</code>.
82 */
83 protected HashMap formats = new HashMap();
84
85 /***
86 * Indicate is a <code>null</code> is returned instead of an error message
87 * string when an unknown Locale or key is requested.
88 */
89 protected boolean returnNull = false;
90
91 /***
92 * Indicates whether 'escape processing' should be performed on the error
93 * message string.
94 */
95 private boolean escape = true;
96
97
98
99 /***
100 * Construct a new MessageResources according to the specified
101 * parameters.
102 *
103 * @param factory The MessageResourcesFactory that created us
104 * @param config The configuration parameter for this MessageResources
105 */
106 public MessageResources(MessageResourcesFactory factory, String config) {
107 this(factory, config, false);
108 }
109
110 /***
111 * Construct a new MessageResources according to the specified
112 * parameters.
113 *
114 * @param factory The MessageResourcesFactory that created us
115 * @param config The configuration parameter for this
116 * MessageResources
117 * @param returnNull The returnNull property we should initialize with
118 */
119 public MessageResources(MessageResourcesFactory factory, String config,
120 boolean returnNull) {
121 super();
122 this.factory = factory;
123 this.config = config;
124 this.returnNull = returnNull;
125 }
126
127 /***
128 * The configuration parameter used to initialize this MessageResources.
129 *
130 * @return parameter used to initialize this MessageResources
131 */
132 public String getConfig() {
133 return (this.config);
134 }
135
136 /***
137 * The <code>MessageResourcesFactory</code> that created this instance.
138 *
139 * @return <code>MessageResourcesFactory</code> that created instance
140 */
141 public MessageResourcesFactory getFactory() {
142 return (this.factory);
143 }
144
145 /***
146 * Indicates that a <code>null</code> is returned instead of an error
147 * message string if an unknown Locale or key is requested.
148 *
149 * @return true if null is returned if unknown key or locale is requested
150 */
151 public boolean getReturnNull() {
152 return (this.returnNull);
153 }
154
155 /***
156 * Indicates that a <code>null</code> is returned instead of an error
157 * message string if an unknown Locale or key is requested.
158 *
159 * @param returnNull true Indicates that a <code>null</code> is returned
160 * if an unknown Locale or key is requested.
161 */
162 public void setReturnNull(boolean returnNull) {
163 this.returnNull = returnNull;
164 }
165
166 /***
167 * Indicates whether 'escape processing' should be performed on the error
168 * message string.
169 *
170 * @since Struts 1.2.8
171 */
172 public boolean isEscape() {
173 return escape;
174 }
175
176 /***
177 * Set whether 'escape processing' should be performed on the error
178 * message string.
179 *
180 * @since Struts 1.2.8
181 */
182 public void setEscape(boolean escape) {
183 this.escape = escape;
184 }
185
186
187
188 /***
189 * Returns a text message for the specified key, for the default Locale.
190 *
191 * @param key The message key to look up
192 */
193 public String getMessage(String key) {
194 return this.getMessage((Locale) null, key, null);
195 }
196
197 /***
198 * Returns a text message after parametric replacement of the specified
199 * parameter placeholders.
200 *
201 * @param key The message key to look up
202 * @param args An array of replacement parameters for placeholders
203 */
204 public String getMessage(String key, Object[] args) {
205 return this.getMessage((Locale) null, key, args);
206 }
207
208 /***
209 * Returns a text message after parametric replacement of the specified
210 * parameter placeholders.
211 *
212 * @param key The message key to look up
213 * @param arg0 The replacement for placeholder {0} in the message
214 */
215 public String getMessage(String key, Object arg0) {
216 return this.getMessage((Locale) null, key, arg0);
217 }
218
219 /***
220 * Returns a text message after parametric replacement of the specified
221 * parameter placeholders.
222 *
223 * @param key The message key to look up
224 * @param arg0 The replacement for placeholder {0} in the message
225 * @param arg1 The replacement for placeholder {1} in the message
226 */
227 public String getMessage(String key, Object arg0, Object arg1) {
228 return this.getMessage((Locale) null, key, arg0, arg1);
229 }
230
231 /***
232 * Returns a text message after parametric replacement of the specified
233 * parameter placeholders.
234 *
235 * @param key The message key to look up
236 * @param arg0 The replacement for placeholder {0} in the message
237 * @param arg1 The replacement for placeholder {1} in the message
238 * @param arg2 The replacement for placeholder {2} in the message
239 */
240 public String getMessage(String key, Object arg0, Object arg1, Object arg2) {
241 return this.getMessage((Locale) null, key, arg0, arg1, arg2);
242 }
243
244 /***
245 * Returns a text message after parametric replacement of the specified
246 * parameter placeholders.
247 *
248 * @param key The message key to look up
249 * @param arg0 The replacement for placeholder {0} in the message
250 * @param arg1 The replacement for placeholder {1} in the message
251 * @param arg2 The replacement for placeholder {2} in the message
252 * @param arg3 The replacement for placeholder {3} in the message
253 */
254 public String getMessage(String key, Object arg0, Object arg1, Object arg2,
255 Object arg3) {
256 return this.getMessage((Locale) null, key, arg0, arg1, arg2, arg3);
257 }
258
259 /***
260 * Returns a text message for the specified key, for the default Locale. A
261 * null string result will be returned by this method if no relevant
262 * message resource is found for this key or Locale, if the
263 * <code>returnNull</code> property is set. Otherwise, an appropriate
264 * error message will be returned. <p> This method must be implemented by
265 * a concrete subclass.
266 *
267 * @param locale The requested message Locale, or <code>null</code> for
268 * the system default Locale
269 * @param key The message key to look up
270 */
271 public abstract String getMessage(Locale locale, String key);
272
273 /***
274 * Returns a text message after parametric replacement of the specified
275 * parameter placeholders. A null string result will be returned by this
276 * method if no resource bundle has been configured.
277 *
278 * @param locale The requested message Locale, or <code>null</code> for
279 * the system default Locale
280 * @param key The message key to look up
281 * @param args An array of replacement parameters for placeholders
282 */
283 public String getMessage(Locale locale, String key, Object[] args) {
284
285 if (locale == null) {
286 locale = defaultLocale;
287 }
288
289 MessageFormat format = null;
290 String formatKey = messageKey(locale, key);
291
292 synchronized (formats) {
293 format = (MessageFormat) formats.get(formatKey);
294
295 if (format == null) {
296 String formatString = getMessage(locale, key);
297
298 if (formatString == null) {
299 return returnNull ? null : ("???" + formatKey + "???");
300 }
301
302 format = new MessageFormat(escape(formatString));
303 format.setLocale(locale);
304 formats.put(formatKey, format);
305 }
306 }
307
308 return format.format(args);
309 }
310
311 /***
312 * Returns a text message after parametric replacement of the specified
313 * parameter placeholders. A null string result will never be returned by
314 * this method.
315 *
316 * @param locale The requested message Locale, or <code>null</code> for
317 * the system default Locale
318 * @param key The message key to look up
319 * @param arg0 The replacement for placeholder {0} in the message
320 */
321 public String getMessage(Locale locale, String key, Object arg0) {
322 return this.getMessage(locale, key, new Object[] { arg0 });
323 }
324
325 /***
326 * Returns a text message after parametric replacement of the specified
327 * parameter placeholders. A null string result will never be returned by
328 * this method.
329 *
330 * @param locale The requested message Locale, or <code>null</code> for
331 * the system default Locale
332 * @param key The message key to look up
333 * @param arg0 The replacement for placeholder {0} in the message
334 * @param arg1 The replacement for placeholder {1} in the message
335 */
336 public String getMessage(Locale locale, String key, Object arg0, Object arg1) {
337 return this.getMessage(locale, key, new Object[] { arg0, arg1 });
338 }
339
340 /***
341 * Returns a text message after parametric replacement of the specified
342 * parameter placeholders. A null string result will never be returned by
343 * this method.
344 *
345 * @param locale The requested message Locale, or <code>null</code> for
346 * the system default Locale
347 * @param key The message key to look up
348 * @param arg0 The replacement for placeholder {0} in the message
349 * @param arg1 The replacement for placeholder {1} in the message
350 * @param arg2 The replacement for placeholder {2} in the message
351 */
352 public String getMessage(Locale locale, String key, Object arg0,
353 Object arg1, Object arg2) {
354 return this.getMessage(locale, key, new Object[] { arg0, arg1, arg2 });
355 }
356
357 /***
358 * Returns a text message after parametric replacement of the specified
359 * parameter placeholders. A null string result will never be returned by
360 * this method.
361 *
362 * @param locale The requested message Locale, or <code>null</code> for
363 * the system default Locale
364 * @param key The message key to look up
365 * @param arg0 The replacement for placeholder {0} in the message
366 * @param arg1 The replacement for placeholder {1} in the message
367 * @param arg2 The replacement for placeholder {2} in the message
368 * @param arg3 The replacement for placeholder {3} in the message
369 */
370 public String getMessage(Locale locale, String key, Object arg0,
371 Object arg1, Object arg2, Object arg3) {
372 return this.getMessage(locale, key,
373 new Object[] { arg0, arg1, arg2, arg3 });
374 }
375
376 /***
377 * Return <code>true</code> if there is a defined message for the
378 * specified key in the system default locale.
379 *
380 * @param key The message key to look up
381 */
382 public boolean isPresent(String key) {
383 return this.isPresent(null, key);
384 }
385
386 /***
387 * Return <code>true</code> if there is a defined message for the
388 * specified key in the specified Locale.
389 *
390 * @param locale The requested message Locale, or <code>null</code> for
391 * the system default Locale
392 * @param key The message key to look up
393 */
394 public boolean isPresent(Locale locale, String key) {
395 String message = getMessage(locale, key);
396
397 if (message == null) {
398 return false;
399 } else if (message.startsWith("???") && message.endsWith("???")) {
400 return false;
401 } else {
402 return true;
403 }
404 }
405
406
407
408 /***
409 * Escape any single quote characters that are included in the specified
410 * message string.
411 *
412 * @param string The string to be escaped
413 */
414 protected String escape(String string) {
415 if (!isEscape()) {
416 return string;
417 }
418
419 if ((string == null) || (string.indexOf('\'') < 0)) {
420 return string;
421 }
422
423 int n = string.length();
424 StringBuffer sb = new StringBuffer(n);
425
426 for (int i = 0; i < n; i++) {
427 char ch = string.charAt(i);
428
429 if (ch == '\'') {
430 sb.append('\'');
431 }
432
433 sb.append(ch);
434 }
435
436 return sb.toString();
437 }
438
439 /***
440 * Compute and return a key to be used in caching information by a Locale.
441 * <strong>NOTE</strong> - The locale key for the default Locale in our
442 * environment is a zero length String.
443 *
444 * @param locale The locale for which a key is desired
445 */
446 protected String localeKey(Locale locale) {
447 return (locale == null) ? "" : locale.toString();
448 }
449
450 /***
451 * Compute and return a key to be used in caching information by Locale
452 * and message key.
453 *
454 * @param locale The Locale for which this format key is calculated
455 * @param key The message key for which this format key is calculated
456 */
457 protected String messageKey(Locale locale, String key) {
458 return (localeKey(locale) + "." + key);
459 }
460
461 /***
462 * Compute and return a key to be used in caching information by locale
463 * key and message key.
464 *
465 * @param localeKey The locale key for which this cache key is calculated
466 * @param key The message key for which this cache key is
467 * calculated
468 */
469 protected String messageKey(String localeKey, String key) {
470 return (localeKey + "." + key);
471 }
472
473 /***
474 * Create and return an instance of <code>MessageResources</code> for the
475 * created by the default <code>MessageResourcesFactory</code>.
476 *
477 * @param config Configuration parameter for this message bundle.
478 */
479 public synchronized static MessageResources getMessageResources(
480 String config) {
481 if (defaultFactory == null) {
482 defaultFactory = MessageResourcesFactory.createFactory();
483 }
484
485 return defaultFactory.createResources(config);
486 }
487
488 /***
489 * Log a message to the Writer that has been configured for our use.
490 *
491 * @param message The message to be logged
492 */
493 public void log(String message) {
494 log.debug(message);
495 }
496
497 /***
498 * Log a message and exception to the Writer that has been configured for
499 * our use.
500 *
501 * @param message The message to be logged
502 * @param throwable The exception to be logged
503 */
504 public void log(String message, Throwable throwable) {
505 log.debug(message, throwable);
506 }
507 }