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 java.util.Arrays;
018    import java.util.HashMap;
019    import java.util.HashSet;
020    import java.util.Locale;
021    import java.util.Map;
022    import java.util.Set;
023    
024    import org.apache.hivemind.service.ThreadLocale;
025    import org.apache.tapestry.TapestryConstants;
026    import org.apache.tapestry.TapestryUtils;
027    import org.apache.tapestry.services.CookieSource;
028    import org.apache.tapestry.services.RequestLocaleManager;
029    import 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     */
038    public 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    }