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 }