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.Enumeration; 30 import java.util.LinkedList; 31 import java.util.List; 32 import java.util.Locale; 33 import java.util.Map; 34 import java.util.Set; 35 import java.util.concurrent.ConcurrentHashMap; 36 import java.util.spi.TimeZoneNameProvider; 37 import sun.util.calendar.ZoneInfo; 38 import sun.util.resources.OpenListResourceBundle; 39 40 /** 41 * Utility class that deals with the localized time zone names 42 * 43 * @author Naoto Sato 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<OpenListResourceBundle>> 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 List<String[]> zones = new LinkedList<>(); 77 OpenListResourceBundle rb = getBundle(locale); 78 Enumeration<String> keys = rb.getKeys(); 79 String[] names; 80 81 while(keys.hasMoreElements()) { 82 String key = keys.nextElement(); 83 84 names = retrieveDisplayNames(rb, key, locale); 85 if (names != null) { 86 zones.add(names); 87 } 88 } 89 90 String[][] zonesArray = new String[zones.size()][]; 91 return zones.toArray(zonesArray); 92 } 93 94 /** 95 * Retrieve display names for a time zone ID. 96 */ 97 public static String[] retrieveDisplayNames(String id, Locale locale) { 98 OpenListResourceBundle rb = getBundle(locale); 99 return retrieveDisplayNames(rb, id, locale); 100 } 101 102 private static String[] retrieveDisplayNames(OpenListResourceBundle rb, 103 String id, Locale locale) { 104 if (id == null || locale == null) { 105 throw new NullPointerException(); 106 } 107 108 LocaleServiceProviderPool pool = 109 LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class); 110 return pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale, id); 111 } 112 113 private static OpenListResourceBundle getBundle(Locale locale) { 114 OpenListResourceBundle rb; 115 SoftReference<OpenListResourceBundle> data = cachedBundles.get(locale); 116 117 if (data == null || ((rb = data.get()) == null)) { 118 rb = LocaleProviderAdapter.forJRE().getLocaleData().getTimeZoneNames(locale); 119 data = new SoftReference<>(rb); 120 cachedBundles.put(locale, data); 121 } 122 123 return rb; 124 } 125 126 /** 127 * Obtains a localized time zone strings from a TimeZoneNameProvider 128 * implementation. 129 */ 130 private static class TimeZoneNameGetter 131 implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider, 132 String[]>{ 133 private static final TimeZoneNameGetter INSTANCE = 134 new TimeZoneNameGetter(); 135 136 @Override 137 public String[] getObject(TimeZoneNameProvider timeZoneNameProvider, 138 Locale locale, 139 String requestID, 140 Object... params) { 141 assert params.length == 0; 142 String queryID = requestID; 143 144 // First, try to get names with the request ID 145 String[] names = buildZoneStrings(timeZoneNameProvider, locale, requestID); 146 147 if (names == null) { 148 Map<String, String> aliases = ZoneInfo.getAliasTable(); 149 150 if (aliases != null) { 151 // Check whether this id is an alias, if so, 152 // look for the standard id. 153 if (aliases.containsKey(queryID)) { 154 String prevID = queryID; 155 while ((queryID = aliases.get(queryID)) != null) { 156 prevID = queryID; 157 } 158 queryID = prevID; 159 } 160 161 names = buildZoneStrings(timeZoneNameProvider, locale, queryID); 162 163 if (names == null) { 164 // There may be a case that a standard id has become an 165 // alias. so, check the aliases backward. 166 names = examineAliases(timeZoneNameProvider, locale, 167 queryID, aliases, aliases.entrySet()); 168 } 169 } 170 } 171 172 if (names != null) { 173 names[0] = requestID; 174 } 175 176 return names; 177 } 178 179 private static String[] examineAliases(TimeZoneNameProvider tznp, Locale locale, 180 String id, 181 Map<String, String> aliases, 182 Set<Map.Entry<String, String>> aliasesSet) { 183 if (aliases.containsValue(id)) { 184 for (Map.Entry<String, String> entry : aliasesSet) { 185 if (entry.getValue().equals(id)) { 186 String alias = entry.getKey(); 187 String[] names = buildZoneStrings(tznp, locale, alias); 188 if (names != null) { 189 return names; 190 } else { 191 names = examineAliases(tznp, locale, alias, aliases, aliasesSet); 192 if (names != null) { 193 return names; 194 } 195 } 196 } 197 } 198 } 199 200 return null; 201 } 202 203 private static String[] buildZoneStrings(TimeZoneNameProvider tznp, 204 Locale locale, String id) { 205 String[] names = new String[5]; 206 207 for (int i = 1; i <= 4; i ++) { 208 names[i] = tznp.getDisplayName(id, i>=3, i%2, locale); 209 if (i >= 3 && names[i] == null) { 210 names[i] = names[i-2]; 211 } 212 } 213 214 if (names[1] == null) { 215 // this id seems not localized by this provider 216 names = null; 217 } 218 219 return names; 220 } 221 } 222 223 // No instantiation 224 private TimeZoneNameUtility() { 225 } 226 }