View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  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,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.trinidad.util;
20  
21  import java.util.Collections;
22  import java.util.HashMap;
23  import java.util.Locale;
24  import java.util.Map;
25  import java.util.MissingResourceException;
26  import java.util.ResourceBundle;
27  
28  import javax.faces.application.Application;
29  import javax.faces.application.FacesMessage;
30  import javax.faces.context.FacesContext;
31  
32  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
33  
34  
35  /**
36   * Used for getting localized strings for Validators and Converters from the
37   * Resource Bundle.  First checks for strings in application specific
38   * bundle.  If not found checks for the resource in faces bundle.
39   * <p>
40   * @version $Name:  $ ($Revision: adfrt/faces/adf-faces-api/src/main/java/oracle/adf/view/faces/util/LocaleUtils.java#0 $) $Date: 10-nov-2005.19:08:38 $
41   */
42  final class LocaleUtils
43  {
44    private LocaleUtils()
45    {
46    }
47  
48    // Looks for the key and key_detail values in the resource bundle and
49    // returns it after formatting it with the parameters in the place holders
50    // of the obtained value for the given key.
51    static ErrorMessages __getErrorMessages(
52      FacesContext context,
53      String resourceId
54      )
55    {
56      BundleSummaryInfo info = _identifyBundleSummaryInfo(context, resourceId);
57      String summary = info.getSummary();
58      if (summary == null)
59      {
60        summary = "???" + resourceId + "???";
61      }
62      ResourceBundle bundle  = info.getBundle();
63      if (null == bundle)
64      {
65        throw new NullPointerException(_LOG.getMessage(
66          "BUNDLE_NOT_FOUND"));
67      }
68  
69      // Look up for key_detail now
70      String detailKey = _getDetailKey(resourceId);
71      String detail = _getBundleString(bundle, detailKey);
72      if (detail == null)
73      {
74        detail = "???" + resourceId + "_detail???";
75      }
76  
77      return new ErrorMessages(summary, detail);
78    }
79  
80    static String __getSummaryString(
81      FacesContext context,
82      String messageId)
83    {
84      BundleSummaryInfo info = _identifyBundleSummaryInfo(context, messageId);
85      return info.getSummary();
86    }
87  
88    static String __getDetailString(
89      FacesContext context,
90      String messageId)
91    {
92      BundleSummaryInfo info = _identifyBundleSummaryInfo(context, messageId);
93      ResourceBundle bundle  = info.getBundle();
94      if (null == bundle)
95      {
96        throw new NullPointerException(_LOG.getMessage(
97          "BUNDLE_NOT_FOUND"));
98      }
99  
100     // Look up for key_detail now
101     String detailKey = _getDetailKey(messageId);
102     return _getBundleString(bundle, detailKey);
103 
104   }
105 
106   private static ClassLoader _getClassLoader()
107   {
108     ClassLoader loader = Thread.currentThread().getContextClassLoader();
109     if (loader == null)
110       loader = ClassLoader.getSystemClassLoader();
111 
112     return loader;
113   }
114 
115   private static String _getDetailKey(
116     String messageId)
117   {
118     return messageId + "_detail";
119   }
120 
121   private static BundleSummaryInfo _identifyBundleSummaryInfo(
122     FacesContext context,
123     String resourceId
124     )
125   {
126     _assertContextNotNull(context);
127 
128     if (resourceId == null)
129     {
130       throw new NullPointerException(_LOG.getMessage(
131         "NULL_RESOURCEID"));
132     }
133 
134     Locale locale = _getLocale(context);
135     ClassLoader loader = _getClassLoader();
136 
137     // First we look in the application specific bundle
138     ResourceBundle bundle = _getApplicationFacesMessageBundle(context, locale, loader);
139     BundleSummaryInfo summary = _getBundleSummaryInfo(bundle, resourceId);
140     if(summary == null)
141     {
142       // The application did not override the resource, let look in the 
143       // private Trinidad bundle
144       bundle = _getTrinidadMessageBundle(locale, loader);
145       summary = _getBundleSummaryInfo(bundle, resourceId);
146       
147       if(summary == null)
148       {
149         // The internal bundle does not know of the requested resource either,
150         // let check the default JSF IMPL message bundle
151         bundle = _getDefaultFacesMessageBundle(locale, loader);
152         if(bundle == null)
153         {
154           // That bundle is from the spec, it should never be null
155           throw new NullPointerException(_LOG.getMessage(
156             "CANNOT_FIND_DEFAULT_FACESMESSAGE"));
157         }
158         
159         summary = _getBundleSummaryInfo(bundle, resourceId);
160         if(summary == null)
161         {
162           summary = new BundleSummaryInfo(null, "???" + resourceId + "???");
163         }
164       }
165     }
166     
167     return summary;
168   }
169 
170   /**
171    * Return the bundle for the appropriate locale.&nbsp;First checks for the
172    * ADF Faces application level Message bundle in the cache, if not found
173    * looks up for faces bundle.&nbsp; Once the bundle is found it is
174    * cached for faster retrieval in subsequent calls.
175    *
176    * @param locale Locale for which the bundle is to be indentifed
177    * @param loader ClassLoader to pickup the bundle
178    * @return Resource bundle for the given locale.
179    */
180   private static ResourceBundle _getTrinidadMessageBundle(
181     Locale locale,
182     ClassLoader loader
183     )
184   {
185     ResourceBundle bundle = _getCachedBundle(locale);
186 
187     // if not available in cache
188     if (null == bundle)
189     {
190       try
191       {
192         bundle = ResourceBundle.getBundle(_APACHE_TRINIDAD_MESSAGE_BUNDLE,
193                                           locale, loader);
194         // let us cache the found bundle
195         _cacheBundle(locale, bundle);
196       }
197       catch (MissingResourceException missingResource)
198       {
199         _LOG.severe("UNABLE_LOAD_MESSAGE_BUNDLE",_APACHE_TRINIDAD_MESSAGE_BUNDLE);
200         _LOG.severe(missingResource);
201       }
202     }
203 
204     return bundle;
205   }
206   
207   private static BundleSummaryInfo _getBundleSummaryInfo(
208     ResourceBundle bundle,
209     String resourceId)
210   {
211     assert resourceId != null;
212     
213     if(bundle != null)
214     {
215       // The bundle exists
216       String summary = _getBundleString(bundle, resourceId);
217       if(summary != null)
218       {
219         // The resource exists
220         return new BundleSummaryInfo(bundle, summary);
221       }
222     }
223     
224     return null;
225   }
226 
227   private static ResourceBundle _getApplicationFacesMessageBundle(
228     FacesContext context,
229     Locale locale,
230     ClassLoader loader
231     )
232   {
233     assert context != null;
234     assert locale  != null;
235     assert loader  != null;
236     
237     Application application = context.getApplication();
238     if(application == null)
239     {
240       // Should not happen, but better check than a NullPointerException
241       return null;
242     }
243     
244     String bundleName = application.getMessageBundle();
245     if(bundleName == null)
246     {
247       // There's no specified message bundle in faces-config.xml
248       return null;
249     }
250     
251     try
252     {
253       return ResourceBundle.getBundle(bundleName, locale, loader);
254     }
255     catch (MissingResourceException missingResource)
256     {
257       _LOG.warning("Unable to load faces-config.xml defined message bundle {0}", bundleName);
258       _LOG.warning(missingResource);
259       return null;
260     }
261   }
262 
263   private static ResourceBundle _getDefaultFacesMessageBundle(
264     Locale locale,
265     ClassLoader loader
266     )
267   {
268     ResourceBundle bundle = null;
269     try
270     {
271       bundle =
272         ResourceBundle.getBundle(FacesMessage.FACES_MESSAGES, locale, loader);
273     }
274     catch (MissingResourceException missingResource)
275     {
276       _LOG.severe("UNABLE_LOAD_FACES_BUNDLE", FacesMessage.FACES_MESSAGES);
277       _LOG.severe(missingResource);
278     }
279     return bundle;
280   }
281 
282   private static Locale _getLocale(FacesContext context)
283   {
284     Locale locale = null;
285     if (context.getViewRoot() != null)
286       locale = context.getViewRoot().getLocale();
287 
288     if (locale == null)
289       locale = Locale.getDefault();
290 
291     return locale;
292   }
293 
294   private static ResourceBundle _getCachedBundle(Locale locale)
295   {
296     return _bundleCache.get(locale);
297   }
298 
299   private static void _cacheBundle(Locale locale, ResourceBundle bundle)
300   {
301     _bundleCache.put(locale, bundle);
302   }
303 
304   private static void _assertContextNotNull(FacesContext context)
305   {
306     if (null == context)
307       throw new NullPointerException(_LOG.getMessage(
308         "NULL_FACESCONTEXT"));
309   }
310 
311   /**
312    * @param bundle Bundle in which translated string is to be found for given key
313    * @param key
314    * @return
315    */
316   private static String _getBundleString(ResourceBundle bundle, String key)
317   {
318     try
319     {
320       Object localeStr = bundle.getObject(key);
321       return localeStr == null ? null : localeStr.toString();
322     }
323     catch (MissingResourceException mre)
324     {
325       _LOG.finer("Key {0} not found in {1}", new Object[]{key, bundle});
326       return null;
327     }
328   }
329 
330 
331   // Encapuslation which stores the identified bundle and the summary message.
332   private static class BundleSummaryInfo
333   {
334     BundleSummaryInfo(ResourceBundle bundle, String summary)
335     {
336       _bundle  = bundle;
337       _summary = summary;
338     }
339 
340     public ResourceBundle getBundle()
341     {
342       return _bundle;
343     }
344 
345     public String getSummary()
346     {
347       return _summary;
348     }
349 
350     private ResourceBundle _bundle;
351     private String  _summary;
352   }
353 
354 
355   private static final String _APACHE_TRINIDAD_MESSAGE_BUNDLE
356     = "org.apache.myfaces.trinidad.resource.MessageBundle";
357 
358   // cache Bundles based on locale
359   private static  Map<Locale, ResourceBundle>  _bundleCache;
360 
361   static
362   {
363     _bundleCache = Collections.synchronizedMap(new HashMap<Locale, ResourceBundle>(13));
364   }
365 
366   static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(LocaleUtils.class);
367 }