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        public void initializeService()
073        {
074            String[] names = TapestryUtils.split(_acceptedLocales);
075    
076            if (names.length == 0)
077                return;
078    
079            _defaultLocale = getLocale(names[0]);
080    
081            _acceptedLocaleNamesSet.addAll(Arrays.asList(names));
082    
083        }
084    
085        public Locale extractLocaleForCurrentRequest()
086        {
087            String localeName = _cookieSource.readCookieValue(TapestryConstants.LOCALE_COOKIE_NAME);
088    
089            String requestedLocale = (localeName != null) ? localeName : _request.getLocale().toString();
090            
091            _requestLocale = filterRequestedLocale(requestedLocale);
092    
093            _threadLocale.setLocale(_requestLocale);
094    
095            return _requestLocale;
096        }
097    
098        /**
099         * Converts the request locale name into a Locale instance; applies filters (based on
100         * acceptedLocales) if enabled.
101         */
102    
103        Locale filterRequestedLocale(String localeName)
104        {
105            String requestLocaleName = localeName;
106            if (_acceptedLocaleNamesSet.isEmpty())
107                return getLocale(requestLocaleName);
108    
109            while (requestLocaleName.length() > 0)
110            {
111                if (_acceptedLocaleNamesSet.contains(requestLocaleName))
112                    return getLocale(requestLocaleName);
113    
114                requestLocaleName = stripTerm(requestLocaleName);
115            }
116    
117            // now try "best match"
118    
119            for (Iterator it = _acceptedLocaleNamesSet.iterator(); it.hasNext();) {
120    
121                String locale = (String) it.next();
122                if (locale.startsWith(localeName))
123                    return getLocale(locale);
124            }
125    
126            return _defaultLocale;
127        }
128    
129        private String stripTerm(String localeName)
130        {
131            int scorex = localeName.lastIndexOf('_');
132    
133            return scorex < 0 ? "" : localeName.substring(0, scorex);
134        }
135    
136        public void persistLocale()
137        {
138            Locale locale = _threadLocale.getLocale();
139    
140            if (locale.equals(_requestLocale))
141                return;
142    
143            _cookieSource.writeCookieValue(TapestryConstants.LOCALE_COOKIE_NAME, locale.toString());
144        }
145    
146        Locale getLocale(String name)
147        {
148            Locale result = (Locale) _localeCache.get(name);
149    
150            if (result == null)
151            {
152                result = constructLocale(name);
153                _localeCache.put(name, result);
154            }
155    
156            return result;
157        }
158    
159        private Locale constructLocale(String name)
160        {
161            String[] terms = TapestryUtils.split(name, '_');
162    
163            switch (terms.length)
164            {
165                case 1:
166                    return new Locale(terms[0], "");
167    
168                case 2:
169                    return new Locale(terms[0], terms[1]);
170    
171                case 3:
172    
173                    return new Locale(terms[0], terms[1], terms[2]);
174    
175                default:
176    
177                    throw new IllegalArgumentException();
178            }
179        }
180    
181        public void setCookieSource(CookieSource source)
182        {
183            _cookieSource = source;
184        }
185    
186        public void setRequest(WebRequest request)
187        {
188            _request = request;
189        }
190    
191        public void setThreadLocale(ThreadLocale threadLocale)
192        {
193            _threadLocale = threadLocale;
194        }
195    
196        public void setAcceptedLocales(String acceptedLocales)
197        {
198            _acceptedLocales = acceptedLocales;
199        }
200    }