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