KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > info > magnolia > cms > i18n > ContextMessages


1 /**
2  * Magnolia and its source-code is licensed under the LGPL.
3  * You may copy, adapt, and redistribute this file for commercial or non-commercial use.
4  * When copying, adapting, or redistributing this document in keeping with the guidelines above,
5  * you are required to provide proper attribution to obinary.
6  * If you reproduce or distribute the document without making any substantive modifications to its content,
7  * please use the following attribution line:
8  *
9  * Copyright 1993-2005 obinary Ltd. (http://www.obinary.com) All rights reserved.
10  *
11  */

12 package info.magnolia.cms.i18n;
13
14 import java.util.Enumeration JavaDoc;
15 import java.util.ListResourceBundle JavaDoc;
16 import java.util.Locale JavaDoc;
17 import java.util.MissingResourceException JavaDoc;
18 import java.util.ResourceBundle JavaDoc;
19
20 import javax.servlet.http.HttpServletRequest JavaDoc;
21 import javax.servlet.http.HttpSession JavaDoc;
22 import javax.servlet.jsp.PageContext JavaDoc;
23 import javax.servlet.jsp.jstl.core.Config;
24 import javax.servlet.jsp.jstl.fmt.LocalizationContext;
25
26 import org.apache.commons.lang.StringUtils;
27 import org.apache.log4j.Logger;
28 import org.apache.taglibs.standard.resources.Resources;
29
30
31 /**
32  * @author philipp bracher This calss is doing the same thing as javax.servlet.jsp.jastl.fmt.LocalSupport. LocalSupport
33  * is using the PageContext which is not avaiable in Servlets, but the most classes do know the request-object and can
34  * deliver it to this class. With this class you get the string stricly under the same rules as JSTL does. The most of
35  * the code is the same as in the jstl classes. If you are able to pass a PageContext you will use
36  * javax.servlet.jsp.jastl.fmt.LocalSupport. Endusers will use the MessageManager to resolve messages.
37  */

38
39 public class ContextMessages extends Messages {
40
41     /**
42      * The log4j logger
43      */

44     private static Logger log = Logger.getLogger(ContextMessages.class);
45
46     /**
47      * Copied from the setLocal Tag (jstl)
48      */

49     private static final char HYPHEN = '-';
50
51     /**
52      * Copied from the setLocal Tag (jstl)
53      */

54     private static final char UNDERSCORE = '_';
55
56     /**
57      * Copied from the setLocal Tag (jstl)
58      */

59     private static final Locale JavaDoc EMPTY_LOCALE = new Locale JavaDoc(StringUtils.EMPTY, StringUtils.EMPTY);
60
61     /**
62      * from jstl Config (this suffix are used to inhibit overwriting in other contextes
63      */

64     private static final String JavaDoc REQUEST_SCOPE_SUFFIX = ".request"; //$NON-NLS-1$
65

66     /**
67      * from jstl Config (this suffix are used to inhibit overwriting in other contextes
68      */

69     private static final String JavaDoc SESSION_SCOPE_SUFFIX = ".session"; //$NON-NLS-1$
70

71     /**
72      * from jstl Config (this suffix are used to inhibit overwriting in other contextes
73      */

74     private static final String JavaDoc APPLICATION_SCOPE_SUFFIX = ".application"; //$NON-NLS-1$
75

76     /**
77      * the context found for the current request
78      */

79     private LocalizationContext loc;
80
81     /**
82      * Get the bundle and the local from the context
83      * @param req current request
84      */

85     protected ContextMessages(HttpServletRequest JavaDoc req) {
86         loc = getLocalizationContext(req);
87     }
88
89     /**
90      * Provide a special basename (not info.magnolia.module.admininterface.messages)
91      * @param req the current request
92      * @param basename the name of the bundle
93      */

94     protected ContextMessages(HttpServletRequest JavaDoc req, String JavaDoc basename) {
95         this.setBasename(basename);
96         loc = getLocalizationContext(req, basename);
97     }
98
99     /**
100      * Do not use the current local (do not use the context)
101      * @param req the current request
102      * @param basename the name of the bundle
103      * @param locale use this local to get the strings
104      */

105     protected ContextMessages(HttpServletRequest JavaDoc req, String JavaDoc basename, Locale JavaDoc locale) {
106         this.setBasename(basename);
107         loc = getLocalizationContext(req, basename, locale);
108     }
109
110     /**
111      * @return the current local for this object
112      */

113     public Locale JavaDoc getLocale() {
114         return loc.getLocale();
115     }
116
117     /**
118      * @return the current bundel of the object
119      */

120     public ResourceBundle JavaDoc getBundle() {
121         ResourceBundle JavaDoc bundle = loc.getResourceBundle();
122         if (bundle == null) {
123             log.error("bundle: " + this.getBasename() + " not found"); //$NON-NLS-1$ //$NON-NLS-2$
124
bundle = new ListResourceBundle JavaDoc() {
125
126                 protected Object JavaDoc[][] getContents() {
127                     return new String JavaDoc[][]{};
128                 }
129             };
130         }
131         return bundle;
132     }
133
134     /**
135      * Finds the current locale (request, session, appication)
136      * @param req Request
137      * @return Returns the current locale
138      */

139     public static Locale JavaDoc getCurrentLocale(HttpServletRequest JavaDoc req) {
140         return getLocalizationContext(req).getLocale();
141     }
142
143     /**
144      * Gets the default I18N localization context.
145      * @param req Request in which to look up the default I18N localization context
146      * @return found context
147      */

148     private static LocalizationContext getLocalizationContext(HttpServletRequest JavaDoc req) {
149         LocalizationContext locCtxt = null;
150
151         Object JavaDoc obj = find(req, Config.FMT_LOCALIZATION_CONTEXT);
152         if (obj == null) {
153             return null;
154         }
155
156         if (obj instanceof LocalizationContext) {
157             locCtxt = (LocalizationContext) obj;
158         }
159         else {
160             // localization context is a bundle basename
161
locCtxt = getLocalizationContext(req, (String JavaDoc) obj);
162         }
163
164         return locCtxt;
165     }
166
167     /**
168      * Get the LocalizationContext with a defined basename
169      * @param req the request to start the lookup
170      * @param basename the name of the bundle
171      * @return found context
172      */

173     private static LocalizationContext getLocalizationContext(HttpServletRequest JavaDoc req, String JavaDoc basename) {
174         return getLocalizationContext(req, basename, null);
175     }
176
177     /**
178      * This Code is copied from the JSTL-classed. I addapted it, so that it uses the request instead of the pageContext.
179      * One can provide also a special local.
180      * <p>
181      * Gets the resource bundle with the given base name, whose locale is determined as follows: Check if a match exists
182      * between the ordered set of preferred locales and the available locales, for the given base name. The set of
183      * preferred locales consists of a single locale (if the <tt>javax.servlet.jsp.jstl.fmt.locale</tt> configuration
184      * setting is present) or is equal to the client's preferred locales determined from the client's browser settings.
185      * <p>
186      * If no match was found in the previous step, check if a match exists between the fallback locale (given by the
187      * <tt>javax.servlet.jsp.jstl.fmt.fallbackLocale</tt> configuration setting) and the available locales, for the
188      * given base name.
189      * @param req Request in which the resource bundle with the given base name is requested
190      * @param basename Resource bundle base name
191      * @param locale (added by magnolia team)
192      * @return Localization context containing the resource bundle with the given base name and the locale that led to
193      * the resource bundle match, or the empty localization context if no resource bundle match was found
194      */

195     private static LocalizationContext getLocalizationContext(HttpServletRequest JavaDoc req, String JavaDoc basename, Locale JavaDoc locale) {
196         LocalizationContext locCtxt = null;
197         ResourceBundle JavaDoc bundle = null;
198
199         if (StringUtils.isEmpty(basename)) {
200             return new LocalizationContext();
201         }
202
203         // Try preferred locales
204
Locale JavaDoc pref;
205
206         if (locale != null) {
207             pref = locale;
208         }
209         else {
210             pref = getLocale(req, Config.FMT_LOCALE);
211         }
212
213         if (pref != null) {
214             // Preferred locale is application-based
215
bundle = findMatch(basename, pref);
216             if (bundle != null) {
217                 locCtxt = new LocalizationContext(bundle, pref);
218             }
219         }
220         else {
221             // Preferred locales are browser-based
222
locCtxt = findMatch(req, basename);
223         }
224
225         if (locCtxt == null) {
226             // No match found with preferred locales, try using fallback locale
227
pref = getLocale(req, Config.FMT_FALLBACK_LOCALE);
228             if (pref != null) {
229                 bundle = findMatch(basename, pref);
230                 if (bundle != null) {
231                     locCtxt = new LocalizationContext(bundle, pref);
232                 }
233             }
234         }
235
236         if (locCtxt == null) {
237             // try using the root resource bundle with the given basename
238
try {
239                 bundle = ResourceBundle.getBundle(basename, EMPTY_LOCALE, Thread
240                     .currentThread()
241                     .getContextClassLoader());
242                 if (bundle != null) {
243                     locCtxt = new LocalizationContext(bundle, null);
244                 }
245             }
246             catch (MissingResourceException JavaDoc mre) {
247                 // do nothing
248
}
249         }
250
251         if (locCtxt == null) {
252             locCtxt = new LocalizationContext();
253         }
254
255         return locCtxt;
256     }
257
258     /**
259      * This Code is copied from the JSTL-classes.
260      * <p>
261      * Determines the client's preferred locales from the request, and compares each of the locales (in order of
262      * preference) against the available locales in order to determine the best matching locale.
263      * @param req the current request
264      * @param basename the resource bundle's base name
265      * @return the localization context containing the resource bundle with the given base name and best matching
266      * locale, or <tt> null </tt> if no resource bundle match was found
267      */

268     private static LocalizationContext findMatch(HttpServletRequest JavaDoc req, String JavaDoc basename) {
269         LocalizationContext locCtxt = null;
270
271         // Determine locale from client's browser settings.
272
for (Enumeration JavaDoc en = req.getLocales(); en.hasMoreElements();) {
273             /*
274              * If client request doesn't provide an Accept-Language header, the returned locale Enumeration contains the
275              * runtime's default locale, so it always contains at least one element.
276              */

277             Locale JavaDoc pref = (Locale JavaDoc) en.nextElement();
278             ResourceBundle JavaDoc match = findMatch(basename, pref);
279             if (match != null) {
280                 locCtxt = new LocalizationContext(match, pref);
281                 break;
282             }
283         }
284
285         return locCtxt;
286     }
287
288     /**
289      * This Code is copied from the JSTL-classed.
290      * <p>
291      * Gets the resource bundle with the given base name and preferred locale. This method calls
292      * java.util.ResourceBundle.getBundle(), but ignores its return value unless its locale represents an exact or
293      * language match with the given preferred locale.
294      * @param basename the resource bundle base name
295      * @param pref the preferred locale
296      * @return the requested resource bundle, or <tt> null </tt> if no resource bundle with the given base name exists
297      * or if there is no exact- or language-match between the preferred locale and the locale of the bundle returned by
298      * java.util.ResourceBundle.getBundle().
299      */

300     private static ResourceBundle JavaDoc findMatch(String JavaDoc basename, Locale JavaDoc pref) {
301         ResourceBundle JavaDoc match = null;
302
303         try {
304             ResourceBundle JavaDoc bundle = ResourceBundle.getBundle(basename, pref, Thread
305                 .currentThread()
306                 .getContextClassLoader());
307             Locale JavaDoc avail = bundle.getLocale();
308             if (pref.equals(avail)) {
309                 // Exact match
310
match = bundle;
311             }
312             else {
313                 /*
314                  * We have to make sure that the match we got is for the specified locale. The way
315                  * ResourceBundle.getBundle() works, if a match is not found with (1) the specified locale, it tries to
316                  * match with (2) the current default locale as returned by Locale.getDefault() or (3) the root resource
317                  * bundle (basename). We must ignore any match that could have worked with (2) or (3). So if an exact
318                  * match is not found, we make the following extra tests: - avail locale must be equal to preferred
319                  * locale - avail country must be empty or equal to preferred country (the equality match might have
320                  * failed on the variant)
321                  */

322                 if (pref.getLanguage().equals(avail.getLanguage())
323                     && (StringUtils.isEmpty(avail.getCountry()) || pref.getCountry().equals(avail.getCountry()))) {
324                     /*
325                      * Language match. By making sure the available locale does not have a country and matches the
326                      * preferred locale's language, we rule out "matches" based on the container's default locale. For
327                      * example, if the preferred locale is "en-US", the container's default locale is "en-UK", and there
328                      * is a resource bundle (with the requested base name) available for "en-UK",
329                      * ResourceBundle.getBundle() will return it, but even though its language matches that of the
330                      * preferred locale, we must ignore it, because matches based on the container's default locale are
331                      * not portable across different containers with different default locales.
332                      */

333                     match = bundle;
334                 }
335             }
336         }
337         catch (MissingResourceException JavaDoc mre) {
338             // do nothing
339
}
340
341         return match;
342     }
343
344     /**
345      * This Code is copied from the JSTL-classed. I added the request parameter.
346      * <p>
347      * Returns the locale specified by the named scoped attribute or context configuration parameter.
348      * <p>
349      * The named scoped attribute is searched in the page, request, session (if valid), and application scope(s) (in
350      * this order). If no such attribute exists in any of the scopes, the locale is taken from the named context
351      * configuration parameter.
352      * @param req the request to start the lookup
353      * @param name the name of the scoped attribute or context configuration parameter
354      * @return the locale specified by the named scoped attribute or context configuration parameter, or <tt> null </tt>
355      * if no scoped attribute or configuration parameter with the given name exists
356      */

357     private static Locale JavaDoc getLocale(HttpServletRequest JavaDoc req, String JavaDoc name) {
358         Locale JavaDoc loc = null;
359
360         Object JavaDoc obj = find(req, name);
361         if (obj != null) {
362             if (obj instanceof Locale JavaDoc) {
363                 loc = (Locale JavaDoc) obj;
364             }
365             else {
366                 loc = parseLocale((String JavaDoc) obj);
367             }
368         }
369
370         return loc;
371     }
372
373     /**
374      * See parseLocale(String, String) for details.
375      * @param locale the string to parse the locale from
376      * @return the locale
377      */

378     private static Locale JavaDoc parseLocale(String JavaDoc locale) {
379         return parseLocale(locale, null);
380     }
381
382     /**
383      * Parses the given locale string into its language and (optionally) country components, and returns the
384      * corresponding <tt>java.util.Locale</tt> object. If the given locale string is null or empty, the runtime's
385      * default locale is returned.
386      * @param locale the locale string to parse
387      * @param variant the variant
388      * @return <tt>java.util.Locale</tt> object corresponding to the given locale string, or the runtime's default
389      * locale if the locale string is null or empty
390      * @throws IllegalArgumentException if the given locale does not have a language component or has an empty country
391      * component
392      */

393     private static Locale JavaDoc parseLocale(String JavaDoc locale, String JavaDoc variant) throws IllegalArgumentException JavaDoc {
394
395         Locale JavaDoc ret = null;
396         String JavaDoc language = locale;
397         String JavaDoc country = null;
398         int index;
399
400         if (((index = locale.indexOf(HYPHEN)) > -1) || ((index = locale.indexOf(UNDERSCORE)) > -1)) {
401             language = locale.substring(0, index);
402             country = locale.substring(index + 1);
403         }
404
405         if (StringUtils.isEmpty(language)) {
406             throw new IllegalArgumentException JavaDoc(Resources.getMessage("LOCALE_NO_LANGUAGE")); //$NON-NLS-1$
407
}
408
409         if (country == null) {
410             if (variant != null) {
411                 ret = new Locale JavaDoc(language, StringUtils.EMPTY, variant);
412             }
413             else {
414                 ret = new Locale JavaDoc(language, StringUtils.EMPTY);
415             }
416         }
417         else if (country.length() > 0) {
418             if (variant != null) {
419                 ret = new Locale JavaDoc(language, country, variant);
420             }
421             else {
422                 ret = new Locale JavaDoc(language, country);
423             }
424         }
425         else {
426             throw new IllegalArgumentException JavaDoc(Resources.getMessage("LOCALE_EMPTY_COUNTRY")); //$NON-NLS-1$
427
}
428
429         return ret;
430     }
431
432     /**
433      * Finds the value associated with a specific configuration setting identified by its context initialization
434      * parameter name.
435      * <p>
436      * For each of the JSP scopes (page, request, session, application), get the value of the configuration variable
437      * identified by <tt>name</tt> using method <tt>get()</tt>. Return as soon as a non-null value is found. If no
438      * value is found, get the value of the context initialization parameter identified by <tt>name</tt>.
439      * @param req The request to start from
440      * @param name Context initialization parameter name of the configuration setting
441      * @return The <tt>java.lang.Object</tt> associated with the configuration setting identified by <tt>name</tt>,
442      * or null if it is not defined.
443      */

444     private static Object JavaDoc find(HttpServletRequest JavaDoc req, String JavaDoc name) {
445         Object JavaDoc ret = get(req, name, PageContext.REQUEST_SCOPE);
446         if (ret == null) {
447             if (req.getSession() != null) {
448                 // check session only if a session is present
449
ret = get(req, name, PageContext.SESSION_SCOPE);
450             }
451             if (ret == null) {
452                 ret = get(req, name, PageContext.APPLICATION_SCOPE);
453                 if (ret == null) {
454                     ret = req.getSession().getServletContext().getInitParameter(name);
455                 }
456             }
457         }
458         return ret;
459     }
460
461     /**
462      * Looks up a configuration variable in the given scope.
463      * <p>
464      * The lookup of configuration variables is performed as if each scope had its own name space, that is, the same
465      * configuration variable name in one scope does not replace one stored in a different scope.
466      * @param req Request context in which the configuration variable is to be looked up
467      * @param name Configuration variable name
468      * @param scope Scope in which the configuration variable is to be looked up
469      * @return The <tt>java.lang.Object</tt> associated with the configuration variable, or null if it is not defined.
470      */

471     private static Object JavaDoc get(HttpServletRequest JavaDoc req, String JavaDoc name, int scope) {
472         switch (scope) {
473             case PageContext.REQUEST_SCOPE:
474                 return req.getAttribute(name + REQUEST_SCOPE_SUFFIX);
475             case PageContext.SESSION_SCOPE:
476                 return get(req.getSession(), name);
477             case PageContext.APPLICATION_SCOPE:
478                 return req.getSession().getServletContext().getAttribute(name + APPLICATION_SCOPE_SUFFIX);
479             default:
480                 throw new IllegalArgumentException JavaDoc("unknown scope"); //$NON-NLS-1$
481
}
482     }
483
484     /**
485      * Looks up a configuration variable in the "session" scope.
486      * <p>
487      * The lookup of configuration variables is performed as if each scope had its own name space, that is, the same
488      * configuration variable name in one scope does not replace one stored in a different scope.
489      * @param session Session object in which the configuration variable is to be looked up
490      * @param name Configuration variable name
491      * @return The <tt>java.lang.Object</tt> associated with the configuration variable, or null if it is not defined,
492      * if session is null, or if the session is invalidated.
493      */

494     private static Object JavaDoc get(HttpSession JavaDoc session, String JavaDoc name) {
495         Object JavaDoc ret = null;
496         if (session != null) {
497             try {
498                 ret = session.getAttribute(name + SESSION_SCOPE_SUFFIX);
499             }
500             catch (IllegalStateException JavaDoc ex) {
501                 // when session is invalidated
502
}
503         }
504         return ret;
505     }
506
507     /**
508      * Reload bundles
509      */

510     public void reloadBundles() throws Exception JavaDoc {
511         reloadBundle(loc.getResourceBundle());
512     }
513
514 }
Popular Tags