1 /* 2 * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.util.locale.provider; 27 28 import java.lang.ref.SoftReference; 29 import java.util.LinkedList; 30 import java.util.List; 31 import java.util.Locale; 32 import java.util.Map; 33 import java.util.concurrent.ConcurrentHashMap; 34 import java.util.spi.TimeZoneNameProvider; 35 import sun.util.calendar.ZoneInfo; 36 import sun.util.resources.OpenListResourceBundle; 37 import sun.util.resources.TimeZoneNamesBundle; 38 39 /** 40 * Utility class that deals with the localized time zone names 41 * 42 * @author Naoto Sato 43 * @author Masayoshi Okutsu 44 */ 45 public final class TimeZoneNameUtility { 46 47 /** 48 * cache to hold time zone resource bundles. Keyed by Locale 49 */ 50 private static ConcurrentHashMap<Locale, SoftReference<TimeZoneNamesBundle>> cachedBundles = 51 new ConcurrentHashMap<>(); 52 53 /** 54 * cache to hold time zone localized strings. Keyed by Locale 55 */ 56 private static ConcurrentHashMap<Locale, SoftReference<String[][]>> cachedZoneData = 57 new ConcurrentHashMap<>(); 58 59 /** 60 * get time zone localized strings. Enumerate all keys. 61 */ 62 public static String[][] getZoneStrings(Locale locale) { 63 String[][] zones; 64 SoftReference<String[][]> data = cachedZoneData.get(locale); 65 66 if (data == null || ((zones = data.get()) == null)) { 67 zones = loadZoneStrings(locale); 68 data = new SoftReference<>(zones); 69 cachedZoneData.put(locale, data); 70 } 71 72 return zones; 73 } 74 75 private static String[][] loadZoneStrings(Locale locale) { 76 // If the provider is a TimeZoneNameProviderImpl, call its getZoneStrings 77 // in order to avoid per-ID retrieval. 78 LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(TimeZoneNameProvider.class, locale); 79 TimeZoneNameProvider provider = adapter.getTimeZoneNameProvider(); 80 if (provider instanceof TimeZoneNameProviderImpl) { 81 return ((TimeZoneNameProviderImpl)provider).getZoneStrings(locale); 82 } 83 84 // Performs per-ID retrieval. 85 List<String[]> zones = new LinkedList<>(); 86 OpenListResourceBundle rb = getBundle(locale); 87 for (String key : rb.keySet()) { 88 String[] names = retrieveDisplayNamesImpl(key, locale); 89 if (names != null) { 90 zones.add(names); 91 } 92 } 93 94 String[][] zonesArray = new String[zones.size()][]; 95 return zones.toArray(zonesArray); 96 } 97 98 /** 99 * Retrieve display names for a time zone ID. 100 */ 101 public static String[] retrieveDisplayNames(String id, Locale locale) { 102 if (id == null || locale == null) { 103 throw new NullPointerException(); 104 } 105 return retrieveDisplayNamesImpl(id, locale); 106 } 107 108 /** 109 * Retrieves a generic time zone display name for a time zone ID. 110 * 111 * @param id time zone ID 112 * @param style TimeZone.LONG or TimeZone.SHORT 113 * @param locale desired Locale 114 * @return the requested generic time zone display name, or null if not found. 115 */ 116 public static String retrieveGenericDisplayName(String id, int style, Locale locale) { 117 LocaleServiceProviderPool pool = 118 LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class); 119 return pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale, "generic", style, id); 120 } 121 122 /** 123 * Retrieves a standard or daylight-saving time name for the given time zone ID. 124 * 125 * @param id time zone ID 126 * @param daylight true for a daylight saving time name, or false for a standard time name 127 * @param style TimeZone.LONG or TimeZone.SHORT 128 * @param locale desired Locale 129 * @return the requested time zone name, or null if not found. 130 */ 131 public static String retrieveDisplayName(String id, boolean daylight, int style, Locale locale) { 132 LocaleServiceProviderPool pool = 133 LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class); 134 return pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale, daylight ? "dst" : "std", style, id); 135 } 136 137 private static String[] retrieveDisplayNamesImpl(String id, Locale locale) { 138 LocaleServiceProviderPool pool = 139 LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class); 140 return pool.getLocalizedObject(TimeZoneNameArrayGetter.INSTANCE, locale, id); 141 } 142 143 private static TimeZoneNamesBundle getBundle(Locale locale) { 144 TimeZoneNamesBundle rb; 145 SoftReference<TimeZoneNamesBundle> data = cachedBundles.get(locale); 146 147 if (data == null || ((rb = data.get()) == null)) { 148 rb = LocaleProviderAdapter.forJRE().getLocaleData().getTimeZoneNames(locale); 149 data = new SoftReference<>(rb); 150 cachedBundles.put(locale, data); 151 } 152 153 return rb; 154 } 155 156 /** 157 * Obtains a localized time zone strings from a TimeZoneNameProvider 158 * implementation. 159 */ 160 private static class TimeZoneNameArrayGetter 161 implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider, 162 String[]>{ 163 private static final TimeZoneNameArrayGetter INSTANCE = 164 new TimeZoneNameArrayGetter(); 165 166 @Override 167 public String[] getObject(TimeZoneNameProvider timeZoneNameProvider, 168 Locale locale, 169 String requestID, 170 Object... params) { 171 assert params.length == 0; 172 173 // First, try to get names with the request ID 174 String[] names = buildZoneStrings(timeZoneNameProvider, locale, requestID); 175 176 if (names == null) { 177 Map<String, String> aliases = ZoneInfo.getAliasTable(); 178 179 if (aliases != null) { 180 // Check whether this id is an alias, if so, 181 // look for the standard id. 182 String canonicalID = aliases.get(requestID); 183 if (canonicalID != null) { 184 names = buildZoneStrings(timeZoneNameProvider, locale, canonicalID); 185 } 186 if (names == null) { 187 // There may be a case that a standard id has become an 188 // alias. so, check the aliases backward. 189 names = examineAliases(timeZoneNameProvider, locale, 190 canonicalID == null ? requestID : canonicalID, aliases); 191 } 192 } 193 } 194 195 if (names != null) { 196 names[0] = requestID; 197 } 198 199 return names; 200 } 201 202 private static String[] examineAliases(TimeZoneNameProvider tznp, Locale locale, 203 String id, 204 Map<String, String> aliases) { 205 if (aliases.containsValue(id)) { 206 for (Map.Entry<String, String> entry : aliases.entrySet()) { 207 if (entry.getValue().equals(id)) { 208 String alias = entry.getKey(); 209 String[] names = buildZoneStrings(tznp, locale, alias); 210 if (names != null) { 211 return names; 212 } 213 names = examineAliases(tznp, locale, alias, aliases); 214 if (names != null) { 215 return names; 216 } 217 } 218 } 219 } 220 221 return null; 222 } 223 224 private static String[] buildZoneStrings(TimeZoneNameProvider tznp, 225 Locale locale, String id) { 226 String[] names = new String[5]; 227 228 for (int i = 1; i <= 4; i ++) { 229 names[i] = tznp.getDisplayName(id, i>=3, i%2, locale); 230 if (i >= 3 && names[i] == null) { 231 names[i] = names[i-2]; 232 } 233 } 234 235 if (names[1] == null) { 236 // this id seems not localized by this provider 237 names = null; 238 } 239 240 return names; 241 } 242 } 243 244 private static class TimeZoneNameGetter 245 implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider, 246 String> { 247 private static final TimeZoneNameGetter INSTANCE = 248 new TimeZoneNameGetter(); 249 250 @Override 251 public String getObject(TimeZoneNameProvider timeZoneNameProvider, 252 Locale locale, 253 String requestID, 254 Object... params) { 255 assert params.length == 2; 256 int style = (int) params[0]; 257 String tzid = (String) params[1]; 258 String value = getName(timeZoneNameProvider, locale, requestID, style, tzid); 259 if (value == null) { 260 Map<String, String> aliases = ZoneInfo.getAliasTable(); 261 if (aliases != null) { 262 String canonicalID = aliases.get(tzid); 263 if (canonicalID != null) { 264 value = getName(timeZoneNameProvider, locale, requestID, style, canonicalID); 265 } 266 if (value == null) { 267 value = examineAliases(timeZoneNameProvider, locale, requestID, 268 canonicalID != null ? canonicalID : tzid, style, aliases); 269 } 270 } 271 } 272 273 return value; 274 } 275 276 private static String examineAliases(TimeZoneNameProvider tznp, Locale locale, 277 String requestID, String tzid, int style, 278 Map<String, String> aliases) { 279 if (aliases.containsValue(tzid)) { 280 for (Map.Entry<String, String> entry : aliases.entrySet()) { 281 if (entry.getValue().equals(tzid)) { 282 String alias = entry.getKey(); 283 String name = getName(tznp, locale, requestID, style, alias); 284 if (name != null) { 285 return name; 286 } 287 name = examineAliases(tznp, locale, requestID, alias, style, aliases); 288 if (name != null) { 289 return name; 290 } 291 } 292 } 293 } 294 return null; 295 } 296 297 private static String getName(TimeZoneNameProvider timeZoneNameProvider, 298 Locale locale, String requestID, int style, String tzid) { 299 String value = null; 300 switch (requestID) { 301 case "std": 302 value = timeZoneNameProvider.getDisplayName(tzid, false, style, locale); 303 break; 304 case "dst": 305 value = timeZoneNameProvider.getDisplayName(tzid, true, style, locale); 306 break; 307 case "generic": 308 value = timeZoneNameProvider.getGenericDisplayName(tzid, style, locale); 309 break; 310 } 311 return value; 312 } 313 } 314 315 // No instantiation 316 private TimeZoneNameUtility() { 317 } 318 }