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
015package org.apache.tapestry.services.impl;
016
017import java.util.Arrays;
018import java.util.HashMap;
019import java.util.HashSet;
020import java.util.Locale;
021import java.util.Map;
022import java.util.Set;
023
024import org.apache.hivemind.service.ThreadLocale;
025import org.apache.tapestry.TapestryConstants;
026import org.apache.tapestry.TapestryUtils;
027import org.apache.tapestry.services.CookieSource;
028import org.apache.tapestry.services.RequestLocaleManager;
029import org.apache.tapestry.web.WebRequest;
030
031/**
032 * Service tapestry.request.RequestLocaleManager. Identifies the Locale provided by the client
033 * (either in a Tapestry-specific cookie, or interpolated from the HTTP header.
034 * 
035 * @author Howard Lewis Ship
036 * @since 4.0
037 */
038public class RequestLocaleManagerImpl implements RequestLocaleManager
039{
040    private WebRequest _request;
041
042    /**
043     * Extracted at start of request, and used at end of request to see if locale has changed.
044     * Because of this thread-specific state, the service must use the threaded service lifecycle
045     * model.
046     */
047
048    private Locale _requestLocale;
049
050    private CookieSource _cookieSource;
051
052    private ThreadLocale _threadLocale;
053
054    /**
055     * Set from symbol org.apache.tapestry.accepted-locales, a comma-seperated list of locale names.
056     * The first name is the default for requests that can't be matched against the other locale
057     * names. May also be blank, in which case, whatever locale was provided in the request is
058     * accepted (which is Tapestry 3.0 behavior).
059     */
060
061    private String _acceptedLocales;
062
063    private Locale _defaultLocale;
064
065    /**
066     * Set of locale names. Incoming requests will be matched to one of these locales.
067     */
068
069    private Set _acceptedLocaleNamesSet = new HashSet();
070
071    /**
072     * Cache of Locales, keyed on locale name.
073     */
074
075    private Map _localeCache = new HashMap();
076
077    public void initializeService()
078    {
079        String[] names = TapestryUtils.split(_acceptedLocales);
080
081        if (names.length == 0)
082            return;
083
084        _defaultLocale = getLocale(names[0]);
085
086        _acceptedLocaleNamesSet.addAll(Arrays.asList(names));
087
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()
095                .toString();
096
097        _requestLocale = filterRequestedLocale(requestedLocale);
098
099        _threadLocale.setLocale(_requestLocale);
100
101        return _requestLocale;
102    }
103
104    /**
105     * Converts the request locale name into a Locale instance; applies filters (based on
106     * acceptedLocales) if enabled.
107     */
108
109    Locale filterRequestedLocale(String localeName)
110    {
111        if (_acceptedLocaleNamesSet.isEmpty())
112            return getLocale(localeName);
113
114        while (true)
115        {
116            if (_acceptedLocaleNamesSet.contains(localeName))
117                return getLocale(localeName);
118
119            localeName = stripTerm(localeName);
120
121            if (localeName.length() == 0)
122                return _defaultLocale;
123        }
124    }
125
126    private String stripTerm(String localeName)
127    {
128        int scorex = localeName.lastIndexOf('_');
129
130        return scorex < 0 ? "" : localeName.substring(0, scorex);
131    }
132
133    public void persistLocale()
134    {
135        Locale locale = _threadLocale.getLocale();
136
137        if (locale.equals(_requestLocale))
138            return;
139
140        _cookieSource.writeCookieValue(TapestryConstants.LOCALE_COOKIE_NAME, locale.toString());
141    }
142
143    Locale getLocale(String name)
144    {
145        Locale result = (Locale) _localeCache.get(name);
146
147        if (result == null)
148        {
149            result = constructLocale(name);
150            _localeCache.put(name, result);
151        }
152
153        return result;
154    }
155
156    private Locale constructLocale(String name)
157    {
158        String[] terms = TapestryUtils.split(name, '_');
159
160        switch (terms.length)
161        {
162            case 1:
163                return new Locale(terms[0], "");
164
165            case 2:
166                return new Locale(terms[0], terms[1]);
167
168            case 3:
169
170                return new Locale(terms[0], terms[1], terms[2]);
171
172            default:
173
174                throw new IllegalArgumentException();
175        }
176    }
177
178    public void setCookieSource(CookieSource source)
179    {
180        _cookieSource = source;
181    }
182
183    public void setRequest(WebRequest request)
184    {
185        _request = request;
186    }
187
188    public void setThreadLocale(ThreadLocale threadLocale)
189    {
190        _threadLocale = threadLocale;
191    }
192
193    public void setAcceptedLocales(String acceptedLocales)
194    {
195        _acceptedLocales = acceptedLocales;
196    }
197}