View Javadoc

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