001    // Copyright 2006, 2007, 2009, 2010 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.tapestry5.internal.services;
016    
017    import java.util.Collections;
018    import java.util.List;
019    import java.util.Locale;
020    import java.util.Map;
021    import java.util.Set;
022    
023    import org.apache.tapestry5.OptionModel;
024    import org.apache.tapestry5.SelectModel;
025    import org.apache.tapestry5.SymbolConstants;
026    import org.apache.tapestry5.internal.OptionModelImpl;
027    import org.apache.tapestry5.internal.SelectModelImpl;
028    import org.apache.tapestry5.internal.TapestryInternalUtils;
029    import org.apache.tapestry5.ioc.annotations.Symbol;
030    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
031    import org.apache.tapestry5.ioc.services.ThreadLocale;
032    import org.apache.tapestry5.services.LocalizationSetter;
033    import org.apache.tapestry5.services.PersistentLocale;
034    import org.apache.tapestry5.services.Request;
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     */
040    public 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 setNonPeristentLocaleFromLocaleName(String localeName)
156        {
157            Locale requested = toLocale(localeName);
158    
159            Locale supported = findClosestSupportedLocale(requested);
160    
161            threadLocale.setLocale(supported);
162        }
163    
164        private Locale findClosestSupportedLocale(Locale desiredLocale)
165        {
166            String localeName = desiredLocale.toString();
167    
168            while (true)
169            {
170                if (isSupportedLocaleName(localeName))
171                    return toLocale(localeName);
172    
173                localeName = stripTerm(localeName);
174    
175                if (localeName.length() == 0)
176                    break;
177            }
178    
179            return defaultLocale;
180        }
181    
182        static String stripTerm(String localeName)
183        {
184            int scorex = localeName.lastIndexOf('_');
185    
186            return scorex < 0 ? "" : localeName.substring(0, scorex);
187        }
188    
189        public List<Locale> getSupportedLocales()
190        {
191            return supportedLocales;
192        }
193    
194        public boolean isSupportedLocaleName(String localeName)
195        {
196            return supportedLocaleNames.contains(localeName.toLowerCase());
197        }
198    
199        public SelectModel getSupportedLocalesModel()
200        {
201            List<OptionModel> options = CollectionFactory.newList();
202    
203            for (Locale l : supportedLocales)
204            {
205                options.add(new OptionModelImpl(l.getDisplayName(l), l));
206            }
207    
208            return new SelectModelImpl(null, options);
209        }
210    
211    }