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