1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.cocoon.acting;
18
19 import org.apache.avalon.framework.configuration.Configurable;
20 import org.apache.avalon.framework.configuration.Configuration;
21 import org.apache.avalon.framework.configuration.ConfigurationException;
22 import org.apache.avalon.framework.parameters.Parameters;
23 import org.apache.avalon.framework.thread.ThreadSafe;
24
25 import org.apache.cocoon.environment.Redirector;
26 import org.apache.cocoon.environment.SourceResolver;
27 import org.apache.cocoon.i18n.I18nUtils;
28
29 import java.util.HashMap;
30 import java.util.Locale;
31 import java.util.Map;
32
33 /**
34 * An action that locates and provides to the pipeline locale information
35 * looked up in a range of ways.
36 *
37 * <h1>Configuration</h1>
38 * <p>A sample configuration (given in the <map:matchers> section of the
39 * sitemap) is given below. This configuration shows default values.
40 * </p>
41 * <pre>
42 * <map:action name="locale" src="org.apache.cocoon.acting.LocaleAction">
43 * <locale-attribute>locale</locale-attribute>
44 * <use-locale>true</use-locale>
45 * <default-locale language="en" country="US"/>
46 * <store-in-request>false<store-in-request>
47 * <create-session>false<create-session>
48 * <store-in-session>false<store-in-session>
49 * <store-in-cookie>false<store-in-cookie>
50 * </map:action>
51 * </pre>
52 *
53 * <p>Above configuration parameters mean:
54 * <ul>
55 * <li><b>locale-attribute</b> specifies the name of the request
56 * parameter / session attribute / cookie that is to be used as a locale
57 * (defaults to <code>locale</code>)</li>
58 * <li><b>use-locale</b> specifies whether the primary locale provided
59 * by the user agent (or server default, is no locale passed by the agent)
60 * is to be used</li>
61 * <li><b>default-locale</b> specifies the default locale to be used when
62 * none found.</li>
63 * <li><b>store-in-request</b> specifies whether found locale should be
64 * stored as request attribute.</li>
65 * <li><b>create-session</b> specifies whether session should be created
66 * when storing found locale as session attribute.</li>
67 * <li><b>store-in-session</b> specifies whether found locale should be
68 * stored as session attribute.</li>
69 * <li><b>store-in-cookie</b> specifies whether found locale should be
70 * stored as cookie.</li>
71 * </ul>
72 * </p>
73 *
74 * <h1>Usage</h1>
75 * <p>This action will be used in a pipeline like so:</p>
76 * <pre>
77 * <map:act type="locale">
78 * <map:generate src="file_{language}_{country}_{variant}.xml"/>
79 * ...
80 * </map:match>
81 * </pre>
82 * <p>or</p>
83 * <pre>
84 * <map:act type="locale">
85 * <map:generate src="file_{locale}.xml"/>
86 * ...
87 * </map:match>
88 * </pre>
89 *
90 * <h1>Locale Identification</h1>
91 * <p>Locales will be tested in following order:</p>
92 * <ul>
93 * <li>Locale provided as a request parameter</li>
94 * <li>Locale provided as a session attribute</li>
95 * <li>Locale provided as a cookie</li>
96 * <li>Locale provided using a sitemap parameter<br>
97 * (<map:parameter name="locale" value="{1}"/> style parameter within
98 * the <map:match> node)</li>
99 * <li>Locale provided by the user agent, or server default,
100 * if <code>use-locale</code> is set to <code>true</code></li>
101 * <li>The default locale, if specified in the matcher's configuration</li>
102 * </ul>
103 * <p>First found locale will be returned.</p>
104 *
105 * <h1>Sitemap Variables</h1>
106 * <p>Once locale has been found, the following sitemap variables
107 * will be available to sitemap elements contained within the action:</p>
108 * <ul>
109 * <li>{locale}: The locale string</li>
110 * <li>{language}: The language of the found locale</li>
111 * <li>{country}: The country of the found locale</li>
112 * <li>{variant}: The variant of the found locale</li>
113 * </ul>
114 *
115 * @author <a href="mailto:Marcus.Crafter@osa.de">Marcus Crafter</a>
116 * @author <a href="mailto:kpiroumian@flagship.ru">Konstantin Piroumian</a>
117 * @author <a href="mailto:lassi.immonen@valkeus.com">Lassi Immonen</a>
118 * @author <a href="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
119 * @version CVS $Id: LocaleAction.java 433543 2006-08-22 06:22:54Z crossley $
120 */
121 public class LocaleAction extends ServiceableAction implements ThreadSafe, Configurable {
122
123 private static final String DEFAULT_DEFAULT_LANG = "en";
124 private static final String DEFAULT_DEFAULT_COUNTRY = "US";
125 private static final String DEFAULT_DEFAULT_VARIANT = "";
126
127 /**
128 * Default locale attribute name.
129 */
130 public static final String LOCALE = "locale";
131
132 /**
133 * Configuration element name for locale attribute name.
134 */
135 public static final String LOCALE_ATTR = "locale-attribute";
136
137
138 /**
139 * Constant representing the request storage configuration attribute
140 */
141 public static final String STORE_REQUEST = "store-in-request";
142
143 /**
144 * Constant representing the session creation configuration attribute
145 */
146 public static final String CREATE_SESSION = "create-session";
147
148 /**
149 * Constant representing the session storage configuration attribute
150 */
151 public static final String STORE_SESSION = "store-in-session";
152
153 /**
154 * Constant representing the cookie storage configuration attribute
155 */
156 public static final String STORE_COOKIE = "store-in-cookie";
157
158
159 /**
160 * Name of the locale request parameter, session attribute, cookie.
161 */
162 private String localeAttribute;
163
164 /**
165 * Whether to query locale provided by the user agent or not.
166 */
167 private boolean useLocale;
168
169 /**
170 * Default locale if no other found and {@link #useLocale} is false.
171 */
172 private Locale defaultLocale;
173
174 /**
175 * Store the locale in request. Default is not to do this.
176 */
177 private boolean storeInRequest;
178
179 /**
180 * Store the locale in session, if available. Default is not to do this.
181 */
182 private boolean storeInSession;
183
184 /**
185 * Should we create a session if needed. Default is not to do this.
186 */
187 private boolean createSession;
188
189 /**
190 * Should we add a cookie with the locale. Default is not to do this.
191 */
192 private boolean storeInCookie;
193
194 /**
195 * Configure this action.
196 *
197 * @param config configuration information (if any)
198 */
199 public void configure(Configuration config)
200 throws ConfigurationException {
201 this.storeInRequest = config.getChild(STORE_REQUEST).getValueAsBoolean(false);
202 this.createSession = config.getChild(CREATE_SESSION).getValueAsBoolean(false);
203 this.storeInSession = config.getChild(STORE_SESSION).getValueAsBoolean(false);
204 this.storeInCookie = config.getChild(STORE_COOKIE).getValueAsBoolean(false);
205 if (getLogger().isDebugEnabled()) {
206 getLogger().debug((this.storeInRequest ? "will" : "won't") + " set values in request");
207 getLogger().debug((this.createSession ? "will" : "won't") + " create session");
208 getLogger().debug((this.storeInSession ? "will" : "won't") + " set values in session");
209 getLogger().debug((this.storeInCookie ? "will" : "won't") + " set values in cookies");
210 }
211
212 this.localeAttribute = config.getChild(LOCALE_ATTR).getValue(LOCALE);
213 this.useLocale = config.getChild("use-locale").getValueAsBoolean(true);
214
215 Configuration child = config.getChild("default-locale", false);
216 if (child != null) {
217 this.defaultLocale = new Locale(child.getAttribute("language", DEFAULT_DEFAULT_LANG),
218 child.getAttribute("country", DEFAULT_DEFAULT_COUNTRY),
219 child.getAttribute("variant", DEFAULT_DEFAULT_VARIANT));
220 }
221
222 if (getLogger().isDebugEnabled()) {
223 getLogger().debug("Locale attribute name is " + this.localeAttribute);
224 getLogger().debug((this.useLocale ? "will" : "won't") + " use request locale");
225 getLogger().debug("default locale " + this.defaultLocale);
226 }
227 }
228
229 /**
230 * Action which obtains the current environments locale information, and
231 * places it in the objectModel (and optionally in a session/cookie).
232 */
233 public Map act(Redirector redirector,
234 SourceResolver resolver,
235 Map objectModel,
236 String source,
237 Parameters params)
238 throws Exception {
239 // Obtain locale information from request, session, cookies, or params
240 Locale locale = I18nUtils.findLocale(objectModel,
241 localeAttribute,
242 params,
243 defaultLocale,
244 useLocale);
245
246 if (locale == null) {
247 if (getLogger().isDebugEnabled()) {
248 getLogger().debug("No locale found.");
249 }
250
251 return null;
252 }
253
254 String localeStr = locale.toString();
255 if (getLogger().isDebugEnabled()) {
256 getLogger().debug("Found locale: " + localeStr);
257 }
258
259 I18nUtils.storeLocale(objectModel,
260 localeAttribute,
261 localeStr,
262 storeInRequest,
263 storeInSession,
264 storeInCookie,
265 createSession);
266
267 // Set up a map for sitemap parameters
268 Map map = new HashMap();
269 map.put("language", locale.getLanguage());
270 map.put("country", locale.getCountry());
271 map.put("variant", locale.getVariant());
272 map.put("locale", localeStr);
273 return map;
274 }
275
276 /**
277 * Helper method to retreive the attribute value containing locale
278 * information. See class documentation for locale determination algorythm.
279 *
280 * @deprecated See I18nUtils.findLocale
281 * @param objectModel requesting object's environment
282 * @return locale value or <code>null</null> if no locale was found
283 */
284 public static String getLocaleAttribute(Map objectModel,
285 String localeAttrName) {
286 Locale locale = I18nUtils.findLocale(objectModel,
287 localeAttrName,
288 null,
289 null,
290 true);
291 return locale.toString();
292 }
293 }