001// Copyright 2006, 2007, 2009, 2010, 2012 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.tapestry5.internal.services;
016
017import java.util.Collections;
018import java.util.List;
019import java.util.Locale;
020import java.util.Map;
021import java.util.Set;
022
023import org.apache.tapestry5.OptionModel;
024import org.apache.tapestry5.SelectModel;
025import org.apache.tapestry5.SymbolConstants;
026import org.apache.tapestry5.commons.util.CollectionFactory;
027import org.apache.tapestry5.http.services.Request;
028import org.apache.tapestry5.internal.OptionModelImpl;
029import org.apache.tapestry5.internal.SelectModelImpl;
030import org.apache.tapestry5.internal.TapestryInternalUtils;
031import org.apache.tapestry5.ioc.annotations.Symbol;
032import org.apache.tapestry5.ioc.services.ThreadLocale;
033import org.apache.tapestry5.services.LocalizationSetter;
034import org.apache.tapestry5.services.PersistentLocale;
035
036/**
037 * Given a set of supported locales, for a specified desired locale, sets the current thread's locale to a supported
038 * locale that is closest to the desired.
039 */
040public class LocalizationSetterImpl implements LocalizationSetter
041{
042    private final Request request;
043
044    private final ThreadLocale threadLocale;
045
046    private final Locale defaultLocale;
047
048    private final Set<String> supportedLocaleNames;
049
050    private final List<Locale> supportedLocales;
051
052    private final Map<String, Locale> localeCache = CollectionFactory.newConcurrentMap();
053
054    private final PersistentLocale persistentLocale;
055
056    public LocalizationSetterImpl(Request request,
057
058    PersistentLocale persistentLocale,
059
060    ThreadLocale threadLocale,
061
062    @Symbol(SymbolConstants.SUPPORTED_LOCALES)
063    String localeNames)
064    {
065        this.request = request;
066
067        this.persistentLocale = persistentLocale;
068        this.threadLocale = threadLocale;
069
070        this.supportedLocaleNames = CollectionFactory.newSet();
071        
072        String[] names = TapestryInternalUtils.splitAtCommas(localeNames);
073        
074        for (String name : names)
075        {
076            supportedLocaleNames.add(name.toLowerCase());
077        }
078
079        supportedLocales = parseNames(names);
080
081        defaultLocale = supportedLocales.get(0);
082    }
083
084    private List<Locale> parseNames(String[] localeNames)
085    {
086        List<Locale> list = CollectionFactory.newList();
087
088        for (String name : localeNames)
089        {
090            list.add(toLocale(name));
091        }
092
093        return Collections.unmodifiableList(list);
094    }
095
096    public Locale toLocale(String localeName)
097    {
098        Locale result = localeCache.get(localeName);
099
100        if (result == null)
101        {
102            result = constructLocale(localeName);
103            localeCache.put(localeName, result);
104        }
105
106        return result;
107    }
108
109    private Locale constructLocale(String name)
110    {
111        String[] terms = name.split("_");
112
113        switch (terms.length)
114        {
115            case 1:
116                return new Locale(terms[0], "");
117
118            case 2:
119                return new Locale(terms[0], terms[1]);
120
121            case 3:
122
123                return new Locale(terms[0], terms[1], terms[2]);
124
125            default:
126
127                throw new IllegalArgumentException();
128        }
129    }
130
131    public boolean setLocaleFromLocaleName(String localeName)
132    {
133        boolean supported = isSupportedLocaleName(localeName);
134
135        if (supported)
136        {
137            Locale locale = findClosestSupportedLocale(toLocale(localeName));
138
139            persistentLocale.set(locale);
140
141            threadLocale.setLocale(locale);
142        }
143        else
144        {
145            Locale requestLocale = request.getLocale();
146
147            Locale supportedLocale = findClosestSupportedLocale(requestLocale);
148
149            threadLocale.setLocale(supportedLocale);
150        }
151
152        return supported;
153    }
154
155    public void setNonPersistentLocaleFromLocaleName(String localeName)
156    {
157        Locale requested = toLocale(localeName);
158
159        Locale supported = findClosestSupportedLocale(requested);
160
161        threadLocale.setLocale(supported);
162    }
163    
164    public void setNonPersistentLocaleFromRequest(Request request)
165    {
166        Locale locale = request.getLocale();
167        setNonPersistentLocaleFromLocaleName(locale.toString());
168    }
169
170    private Locale findClosestSupportedLocale(Locale desiredLocale)
171    {
172        String localeName = desiredLocale.toString();
173
174        while (true)
175        {
176            if (isSupportedLocaleName(localeName))
177                return toLocale(localeName);
178
179            localeName = stripTerm(localeName);
180
181            if (localeName.length() == 0)
182                break;
183        }
184
185        return defaultLocale;
186    }
187
188    static String stripTerm(String localeName)
189    {
190        int scorex = localeName.lastIndexOf('_');
191
192        return scorex < 0 ? "" : localeName.substring(0, scorex);
193    }
194
195    public List<Locale> getSupportedLocales()
196    {
197        return supportedLocales;
198    }
199
200    public boolean isSupportedLocaleName(String localeName)
201    {
202        return supportedLocaleNames.contains(localeName.toLowerCase());
203    }
204
205    public SelectModel getSupportedLocalesModel()
206    {
207        List<OptionModel> options = CollectionFactory.newList();
208
209        for (Locale l : supportedLocales)
210        {
211            options.add(new OptionModelImpl(l.getDisplayName(l), l));
212        }
213
214        return new SelectModelImpl(null, options);
215    }
216
217}