Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/struts/util/PropertyMessageResources.java


1   /*
2    * $Id: PropertyMessageResources.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  
20  package org.apache.struts.util;
21  
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.Locale;
27  import java.util.Properties;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  
32  /**
33   * Concrete subclass of <code>MessageResources</code> that reads message keys
34   * and corresponding strings from named property resources in the same manner
35   * that <code>java.util.PropertyResourceBundle</code> does.  The
36   * <code>base</code> property defines the base property resource name, and
37   * must be specified.
38   * <p>
39   * <strong>IMPLEMENTATION NOTE</strong> - This class trades memory for
40   * speed by caching all messages located via generalizing the Locale under
41   * the original locale as well.
42   * This results in specific messages being stored in the message cache
43   * more than once, but improves response time on subsequent requests for
44   * the same locale + key combination.
45   *
46   * @version $Rev: 54929 $ $Date: 2004-10-16 09:38:42 -0700 (Sat, 16 Oct 2004) $
47   */
48  public class PropertyMessageResources extends MessageResources {
49  
50  
51      // ----------------------------------------------------------- Constructors
52  
53  
54      /**
55       * Construct a new PropertyMessageResources according to the
56       * specified parameters.
57       *
58       * @param factory The MessageResourcesFactory that created us
59       * @param config The configuration parameter for this MessageResources
60       */
61      public PropertyMessageResources(MessageResourcesFactory factory,
62                                      String config) {
63  
64          super(factory, config);
65          log.trace("Initializing, config='" + config + "'");
66  
67      }
68  
69  
70      /**
71       * Construct a new PropertyMessageResources according to the
72       * specified parameters.
73       *
74       * @param factory The MessageResourcesFactory that created us
75       * @param config The configuration parameter for this MessageResources
76       * @param returnNull The returnNull property we should initialize with
77       */
78      public PropertyMessageResources(MessageResourcesFactory factory,
79                                      String config, boolean returnNull) {
80  
81          super(factory, config, returnNull);
82          log.trace("Initializing, config='" + config +
83                   "', returnNull=" + returnNull);
84  
85      }
86  
87  
88      // ------------------------------------------------------------- Properties
89  
90  
91      /**
92       * The set of locale keys for which we have already loaded messages, keyed
93       * by the value calculated in <code>localeKey()</code>.
94       */
95      protected HashMap locales = new HashMap();
96  
97  
98      /**
99       * The <code>Log</code> instance for this class.
100      */
101   protected static final Log log =
102     LogFactory.getLog(PropertyMessageResources.class);
103 
104 
105     /**
106      * The cache of messages we have accumulated over time, keyed by the
107      * value calculated in <code>messageKey()</code>.
108      */
109     protected HashMap messages = new HashMap();
110 
111 
112     // --------------------------------------------------------- Public Methods
113 
114 
115     /**
116      * Returns a text message for the specified key, for the default Locale.
117      * A null string result will be returned by this method if no relevant
118      * message resource is found for this key or Locale, if the
119      * <code>returnNull</code> property is set.  Otherwise, an appropriate
120      * error message will be returned.
121      * <p>
122      * This method must be implemented by a concrete subclass.
123      *
124      * @param locale The requested message Locale, or <code>null</code>
125      *  for the system default Locale
126      * @param key The message key to look up
127      * @return text message for the specified key and locale
128      */
129     public String getMessage(Locale locale, String key) {
130 
131         if (log.isDebugEnabled()) {
132             log.debug("getMessage(" + locale + "," + key + ")");
133         }
134 
135         // Initialize variables we will require
136         String localeKey = localeKey(locale);
137         String originalKey = messageKey(localeKey, key);
138         String messageKey = null;
139         String message = null;
140         int underscore = 0;
141         boolean addIt = false;  // Add if not found under the original key
142 
143         // Loop from specific to general Locales looking for this message
144         while (true) {
145 
146             // Load this Locale's messages if we have not done so yet
147             loadLocale(localeKey);
148 
149             // Check if we have this key for the current locale key
150             messageKey = messageKey(localeKey, key);
151             synchronized (messages) {
152                 message = (String) messages.get(messageKey);
153                 if (message != null) {
154                     if (addIt) {
155                         messages.put(originalKey, message);
156                     }
157                     return (message);
158                 }
159             }
160 
161             // Strip trailing modifiers to try a more general locale key
162             addIt = true;
163             underscore = localeKey.lastIndexOf("_");
164             if (underscore < 0) {
165                 break;
166             }
167             localeKey = localeKey.substring(0, underscore);
168 
169         }
170 
171         // Try the default locale if the current locale is different
172         if (!defaultLocale.equals(locale)) {
173             localeKey = localeKey(defaultLocale);
174             messageKey = messageKey(localeKey, key);
175             loadLocale(localeKey);
176             synchronized (messages) {
177                 message = (String) messages.get(messageKey);
178                 if (message != null) {
179                     messages.put(originalKey, message);
180                     return (message);
181                 }
182             }
183         }
184 
185         // As a last resort, try the default Locale
186         localeKey = "";
187         messageKey = messageKey(localeKey, key);
188         loadLocale(localeKey);
189         synchronized (messages) {
190             message = (String) messages.get(messageKey);
191             if (message != null) {
192                 messages.put(originalKey, message);
193                 return (message);
194             }
195         }
196 
197         // Return an appropriate error indication
198         if (returnNull) {
199             return (null);
200         } else {
201             return ("???" + messageKey(locale, key) + "???");
202         }
203 
204     }
205 
206 
207     // ------------------------------------------------------ Protected Methods
208 
209 
210     /**
211      * Load the messages associated with the specified Locale key.  For this
212      * implementation, the <code>config</code> property should contain a fully
213      * qualified package and resource name, separated by periods, of a series
214      * of property resources to be loaded from the class loader that created
215      * this PropertyMessageResources instance.  This is exactly the same name
216      * format you would use when utilizing the
217      * <code>java.util.PropertyResourceBundle</code> class.
218      *
219      * @param localeKey Locale key for the messages to be retrieved
220      */
221     protected synchronized void loadLocale(String localeKey) {
222 
223         if (log.isTraceEnabled()) {
224             log.trace("loadLocale(" + localeKey + ")");
225         }
226         
227         // Have we already attempted to load messages for this locale?
228         if (locales.get(localeKey) != null) {
229             return;
230         }
231         
232         locales.put(localeKey, localeKey);
233 
234         // Set up to load the property resource for this locale key, if we can
235         String name = config.replace('.', '/');
236         if (localeKey.length() > 0) {
237             name += "_" + localeKey;
238         }
239         
240         name += ".properties";
241         InputStream is = null;
242         Properties props = new Properties();
243 
244         // Load the specified property resource
245         if (log.isTraceEnabled()) {
246             log.trace("  Loading resource '" + name + "'");
247         }
248         
249         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
250         if (classLoader == null) {
251             classLoader = this.getClass().getClassLoader();
252         }
253         
254         is = classLoader.getResourceAsStream(name);
255         if (is != null) {
256             try {
257                 props.load(is);
258                 
259             } catch (IOException e) {
260                 log.error("loadLocale()", e);
261             } finally {
262                 try {
263                     is.close();
264                 } catch (IOException e) {
265                     log.error("loadLocale()", e);
266                 }
267             }
268         }
269         
270         if (log.isTraceEnabled()) {
271             log.trace("  Loading resource completed");
272         }
273 
274         // Copy the corresponding values into our cache
275         if (props.size() < 1) {
276             return;
277         }
278         
279         synchronized (messages) {
280             Iterator names = props.keySet().iterator();
281             while (names.hasNext()) {
282                 String key = (String) names.next();
283                 if (log.isTraceEnabled()) {
284                     log.trace("  Saving message key '" + messageKey(localeKey, key));
285                 }
286                 messages.put(messageKey(localeKey, key), props.getProperty(key));
287             }
288         }
289 
290     }
291 
292 
293 }