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 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}