View Javadoc

1   /*
2    * Copyright 2002,2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.commons.jelly.tags.fmt;
17  
18  import org.apache.commons.jelly.JellyContext;
19  import org.apache.commons.jelly.JellyTagException;
20  import org.apache.commons.jelly.XMLOutput;
21  import org.apache.commons.jelly.Tag;
22  import org.apache.commons.jelly.TagSupport;
23  import org.apache.commons.jelly.expression.Expression;
24  import java.util.Locale;
25  
26  /***
27   * Support for tag handlers for <setLocale>, the locale setting
28   * tag in JSTL.
29   * @author <a href="mailto:willievu@yahoo.com">Willie Vu</a>
30   * @version 1.2
31   *
32   * @task decide how to implement setResponseLocale
33   */
34  public class SetLocaleTag extends TagSupport {
35  
36      private static final char HYPHEN = '-';
37      private static final char UNDERSCORE = '_';
38  
39      private Expression value;
40  
41      private Expression variant;
42  
43      private String scope;
44  
45      /*** Creates a new instance of SetLocaleTag */
46      public SetLocaleTag() {
47      }
48  
49      /***
50       * Evaluates this tag after all the tags properties have been initialized.
51       *
52       */
53      public void doTag(XMLOutput output) throws JellyTagException {
54          Locale locale = null;
55  
56          Object valueInput = null;
57          if (this.value != null) {
58              valueInput = this.value.evaluate(context);
59          }
60          Object variantInput = null;
61          if (this.variant != null) {
62              variantInput = this.variant.evaluate(context);
63          }
64  
65          if (valueInput == null) {
66              locale = Locale.getDefault();
67          } else if (valueInput instanceof String) {
68              if (((String) valueInput).trim().equals("")) {
69                  locale = Locale.getDefault();
70              } else {
71                  locale = parseLocale((String) valueInput, (String) variantInput);
72              }
73          } else {
74              locale = (Locale) valueInput;
75          }
76  
77          if (scope != null) {
78              context.setVariable(Config.FMT_LOCALE, scope, locale);
79          }
80          else {
81              context.setVariable(Config.FMT_LOCALE, locale);
82          }
83          //setResponseLocale(jc, locale);
84      }
85  
86      public void setValue(Expression value) {
87          this.value = value;
88      }
89  
90      public void setVariant(Expression variant) {
91          this.variant = variant;
92      }
93  
94      public void setScope(String scope) {
95          this.scope = scope;
96      }
97  
98      //**********************************************************************
99      // Public utility methods
100 
101     /**
102      * See parseLocale(String, String) for details.
103      */
104     public static Locale parseLocale(String locale) {
105         return parseLocale(locale, null);
106     }
107 
108     /***
109      * Parses the given locale string into its language and (optionally)
110      * country components, and returns the corresponding
111      * <tt>java.util.Locale</tt> object.
112      *
113      * If the given locale string is null or empty, the runtime's default
114      * locale is returned.
115      *
116      * @param locale the locale string to parse
117      * @param variant the variant
118      *
119      * @return <tt>java.util.Locale</tt> object corresponding to the given
120      * locale string, or the runtime's default locale if the locale string is
121      * null or empty
122      *
123      * @throws IllegalArgumentException if the given locale does not have a
124      * language component or has an empty country component
125      */
126     public static Locale parseLocale(String locale, String variant) {
127 
128         Locale ret = null;
129         String language = locale;
130         String country = null;
131         int index = -1;
132 
133         if (((index = locale.indexOf(HYPHEN)) > -1)
134         || ((index = locale.indexOf(UNDERSCORE)) > -1)) {
135             language = locale.substring(0, index);
136             country = locale.substring(index+1);
137         }
138 
139         if ((language == null) || (language.length() == 0)) {
140             throw new IllegalArgumentException("Missing language");
141         }
142 
143         if (country == null) {
144             if (variant != null)
145                 ret = new Locale(language, "", variant);
146             else
147                 ret = new Locale(language, "");
148         } else if (country.length() > 0) {
149             if (variant != null)
150                 ret = new Locale(language, country, variant);
151             else
152                 ret = new Locale(language, country);
153         } else {
154             throw new IllegalArgumentException("Missing country");
155         }
156 
157         return ret;
158     }
159 
160     /***
161      * Returns the locale specified by the named scoped attribute or context
162      * configuration parameter.
163      *
164      * <p> The named scoped attribute is searched in the page, request,
165      * session (if valid), and application scope(s) (in this order). If no such
166      * attribute exists in any of the scopes, the locale is taken from the
167      * named context configuration parameter.
168      *
169      * @param jc the page in which to search for the named scoped
170      * attribute or context configuration parameter
171      * @param name the name of the scoped attribute or context configuration
172      * parameter
173      *
174      * @return the locale specified by the named scoped attribute or context
175      * configuration parameter, or <tt>null</tt> if no scoped attribute or
176      * configuration parameter with the given name exists
177      */
178     static Locale getLocale(JellyContext jc, String name) {
179         Locale loc = null;
180 
181         Object obj = jc.getVariable(name);
182         if (obj != null) {
183             if (obj instanceof Locale) {
184                 loc = (Locale) obj;
185             } else {
186                 loc = parseLocale((String) obj);
187             }
188         }
189 
190         return loc;
191     }
192 
193     /*
194      * Returns the formatting locale to use with the given formatting action
195      * in the given page.
196      *
197      * @param jc The context containing the formatting action
198      * @param fromTag The formatting action
199      * @param format <tt>true</tt> if the formatting action is of type
200      * <formatXXX> (as opposed to <parseXXX>), and <tt>false</tt> otherwise
201      * (if set to <tt>true</tt>, the formatting locale that is returned by
202      * this method is used to set the response locale).
203      *
204      * @param avail the array of available locales
205      *
206      * @return the formatting locale to use
207      */
208     static Locale getFormattingLocale(JellyContext jc,
209     Tag fromTag,
210     boolean format,
211     Locale[] avail) {
212 
213         LocalizationContext locCtxt = null;
214 
215         // Get formatting locale from enclosing <fmt:bundle>
216         Tag parent = findAncestorWithClass(fromTag, BundleTag.class);
217         if (parent != null) {
218         /*
219          * use locale from localization context established by parent
220          * <fmt:bundle> action, unless that locale is null
221          */
222             locCtxt = ((BundleTag) parent).getLocalizationContext();
223             if (locCtxt.getLocale() != null) {
224                 if (format) {
225                     //setResponseLocale(jc, locCtxt.getLocale());
226                 }
227                 return locCtxt.getLocale();
228             }
229         }
230 
231         // Use locale from default I18N localization context, unless it is null
232         if ((locCtxt = BundleTag.getLocalizationContext(jc)) != null) {
233             if (locCtxt.getLocale() != null) {
234                 if (format) {
235                     //setResponseLocale(jc, locCtxt.getLocale());
236                 }
237                 return locCtxt.getLocale();
238             }
239         }
240 
241     /*
242      * Establish formatting locale by comparing the preferred locales
243      * (in order of preference) against the available formatting
244      * locales, and determining the best matching locale.
245      */
246         Locale match = null;
247         Locale pref = getLocale(jc, Config.FMT_LOCALE);
248         if (pref != null) {
249             // Preferred locale is application-based
250             match = findFormattingMatch(pref, avail);
251         }
252         if (match == null) {
253             //Use fallback locale.
254             pref = getLocale(jc, Config.FMT_FALLBACK_LOCALE);
255             if (pref != null) {
256                 match = findFormattingMatch(pref, avail);
257             }
258         }
259         if (format && (match != null)) {
260             //setResponseLocale(jc, match);
261         }
262 
263         return match;
264     }
265 
266     /*
267      * Returns the best match between the given preferred locale and the
268      * given available locales.
269      *
270      * The best match is given as the first available locale that exactly
271      * matches the given preferred locale ("exact match"). If no exact match
272      * exists, the best match is given as the first available locale that
273      * matches the preferred locale's language component and does not have any
274      * country component ("language match").
275      *
276      * @param pref the preferred locale
277      * @param avail the available formatting locales
278      *
279      * @return Available locale that best matches the given preferred locale,
280      * or <tt>null</tt> if no match exists
281      */
282     private static Locale findFormattingMatch(Locale pref, Locale[] avail) {
283         Locale match = null;
284 
285         for (int i=0; i<avail.length; i++) {
286             if (pref.equals(avail[i])) {
287                 // Exact match
288                 match = avail[i];
289                 break;
290             } else {
291                 if (pref.getLanguage().equals(avail[i].getLanguage())
292                 && ("".equals(avail[i].getCountry()))) {
293                     // Language match
294                     if (match == null) {
295                         match = avail[i];
296                     }
297                 }
298             }
299         }
300 
301         return match;
302     }
303 }