001 // Copyright 2004, 2005 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry.services.impl;
016
017 import org.apache.hivemind.service.ThreadLocale;
018 import org.apache.tapestry.TapestryConstants;
019 import org.apache.tapestry.TapestryUtils;
020 import org.apache.tapestry.services.CookieSource;
021 import org.apache.tapestry.services.RequestLocaleManager;
022 import org.apache.tapestry.web.WebRequest;
023
024 import java.util.*;
025
026 /**
027 * Service tapestry.request.RequestLocaleManager. Identifies the Locale provided by the client
028 * (either in a Tapestry-specific cookie, or interpolated from the HTTP header.
029 *
030 * @author Howard Lewis Ship
031 * @since 4.0
032 */
033 public class RequestLocaleManagerImpl implements RequestLocaleManager
034 {
035 private WebRequest _request;
036
037 /**
038 * Extracted at start of request, and used at end of request to see if locale has changed.
039 * Because of this thread-specific state, the service must use the threaded service lifecycle
040 * model.
041 */
042
043 private Locale _requestLocale;
044
045 private CookieSource _cookieSource;
046
047 private ThreadLocale _threadLocale;
048
049 /**
050 * Set from symbol org.apache.tapestry.accepted-locales, a comma-seperated list of locale names.
051 * The first name is the default for requests that can't be matched against the other locale
052 * names. May also be blank, in which case, whatever locale was provided in the request is
053 * accepted (which is Tapestry 3.0 behavior).
054 */
055
056 private String _acceptedLocales;
057
058 private Locale _defaultLocale;
059
060 /**
061 * Set of locale names. Incoming requests will be matched to one of these locales.
062 */
063
064 private Set _acceptedLocaleNamesSet = new HashSet();
065
066 /**
067 * Cache of Locales, keyed on locale name.
068 */
069
070 private Map _localeCache = new HashMap();
071
072 /**
073 * Reference to last persisted locale, if any. Used to prevent
074 * duplicate cookie writes of the same locale.
075 */
076 private Locale _lastPersisted;
077
078 public void initializeService()
079 {
080 String[] names = TapestryUtils.split(_acceptedLocales);
081
082 if (names.length == 0)
083 return;
084
085 _defaultLocale = getLocale(names[0]);
086
087 _acceptedLocaleNamesSet.addAll(Arrays.asList(names));
088 }
089
090 public Locale extractLocaleForCurrentRequest()
091 {
092 String localeName = _cookieSource.readCookieValue(TapestryConstants.LOCALE_COOKIE_NAME);
093
094 String requestedLocale = (localeName != null) ? localeName : _request.getLocale().toString();
095
096 _requestLocale = filterRequestedLocale(requestedLocale);
097
098 _threadLocale.setLocale(_requestLocale);
099
100 return _requestLocale;
101 }
102
103 /**
104 * Converts the request locale name into a Locale instance; applies filters (based on
105 * acceptedLocales) if enabled.
106 */
107
108 Locale filterRequestedLocale(String localeName)
109 {
110 String requestLocaleName = localeName;
111 if (_acceptedLocaleNamesSet.isEmpty())
112 return getLocale(requestLocaleName);
113
114 while (requestLocaleName.length() > 0)
115 {
116 if (_acceptedLocaleNamesSet.contains(requestLocaleName))
117 return getLocale(requestLocaleName);
118
119 requestLocaleName = stripTerm(requestLocaleName);
120 }
121
122 // now try "best match"
123
124 for (Iterator it = _acceptedLocaleNamesSet.iterator(); it.hasNext();)
125 {
126 String locale = (String) it.next();
127
128 if (locale.startsWith(localeName))
129 return getLocale(locale);
130 }
131
132 return _defaultLocale;
133 }
134
135 private String stripTerm(String localeName)
136 {
137 int scorex = localeName.lastIndexOf('_');
138
139 return scorex < 0 ? "" : localeName.substring(0, scorex);
140 }
141
142 public void persistLocale()
143 {
144 Locale locale = _threadLocale.getLocale();
145
146 if (locale.equals(_requestLocale)
147 || _lastPersisted != null && locale.equals(_lastPersisted))
148 return;
149
150 _cookieSource.writeCookieValue(TapestryConstants.LOCALE_COOKIE_NAME, locale.toString());
151 _lastPersisted = locale;
152 }
153
154 Locale getLocale(String name)
155 {
156 Locale result = (Locale) _localeCache.get(name);
157
158 if (result == null)
159 {
160 result = constructLocale(name);
161 _localeCache.put(name, result);
162 }
163
164 return result;
165 }
166
167 private Locale constructLocale(String name)
168 {
169 String[] terms = TapestryUtils.split(name, '_');
170
171 switch (terms.length)
172 {
173 case 1:
174 return new Locale(terms[0], "");
175
176 case 2:
177 return new Locale(terms[0], terms[1]);
178
179 case 3:
180
181 return new Locale(terms[0], terms[1], terms[2]);
182
183 default:
184
185 throw new IllegalArgumentException();
186 }
187 }
188
189 public void setCookieSource(CookieSource source)
190 {
191 _cookieSource = source;
192 }
193
194 public void setRequest(WebRequest request)
195 {
196 _request = request;
197 }
198
199 public void setThreadLocale(ThreadLocale threadLocale)
200 {
201 _threadLocale = threadLocale;
202 }
203
204 public void setAcceptedLocales(String acceptedLocales)
205 {
206 _acceptedLocales = acceptedLocales;
207 }
208 }