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