1 /* 2 * Copyright (c) 2012, 2016, 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.text.spi.BreakIteratorProvider; 29 import java.text.spi.CollatorProvider; 30 import java.text.spi.DateFormatProvider; 31 import java.text.spi.DateFormatSymbolsProvider; 32 import java.text.spi.DecimalFormatSymbolsProvider; 33 import java.text.spi.NumberFormatProvider; 34 import java.util.ArrayList; 35 import java.util.Collections; 36 import java.util.List; 37 import java.util.Locale; 38 import java.util.Map; 39 import java.util.ResourceBundle; 40 import java.util.Set; 41 import java.util.concurrent.ConcurrentHashMap; 42 import java.util.concurrent.ConcurrentMap; 43 import java.util.spi.CalendarDataProvider; 44 import java.util.spi.CalendarNameProvider; 45 import java.util.spi.CurrencyNameProvider; 46 import java.util.spi.LocaleNameProvider; 47 import java.util.spi.LocaleServiceProvider; 48 import java.util.spi.TimeZoneNameProvider; 49 import sun.security.action.GetPropertyAction; 50 import sun.text.spi.JavaTimeDateTimePatternProvider; 51 import sun.util.spi.CalendarProvider; 52 53 /** 54 * The LocaleProviderAdapter abstract class. 55 * 56 * @author Naoto Sato 57 * @author Masayoshi Okutsu 58 */ 59 public abstract class LocaleProviderAdapter { 60 /** 61 * Adapter type. 62 */ 63 public static enum Type { 64 JRE("sun.util.locale.provider.JRELocaleProviderAdapter", "sun.util.resources", "sun.text.resources"), 65 CLDR("sun.util.cldr.CLDRLocaleProviderAdapter", "sun.util.resources.cldr", "sun.text.resources.cldr"), 66 SPI("sun.util.locale.provider.SPILocaleProviderAdapter"), 67 HOST("sun.util.locale.provider.HostLocaleProviderAdapter"), 68 FALLBACK("sun.util.locale.provider.FallbackLocaleProviderAdapter", "sun.util.resources", "sun.text.resources"); 69 70 private final String CLASSNAME; 71 private final String UTIL_RESOURCES_PACKAGE; 72 private final String TEXT_RESOURCES_PACKAGE; 73 74 private Type(String className) { 75 this(className, null, null); 76 } 77 78 private Type(String className, String util, String text) { 79 CLASSNAME = className; 80 UTIL_RESOURCES_PACKAGE = util; 81 TEXT_RESOURCES_PACKAGE = text; 82 } 83 84 public String getAdapterClassName() { 85 return CLASSNAME; 86 } 87 88 public String getUtilResourcesPackage() { 89 return UTIL_RESOURCES_PACKAGE; 90 } 91 92 public String getTextResourcesPackage() { 93 return TEXT_RESOURCES_PACKAGE; 94 } 95 } 96 97 /** 98 * LocaleProviderAdapter preference list. 99 */ 100 private static final List<Type> adapterPreference; 101 102 /** 103 * LocaleProviderAdapter instances 104 */ 105 private static final Map<Type, LocaleProviderAdapter> adapterInstances = new ConcurrentHashMap<>(); 106 107 /** 108 * Default fallback adapter type, which should return something meaningful in any case. 109 * This is either CLDR or FALLBACK. 110 */ 111 static volatile LocaleProviderAdapter.Type defaultLocaleProviderAdapter; 112 113 /** 114 * Adapter lookup cache. 115 */ 116 private static ConcurrentMap<Class<? extends LocaleServiceProvider>, ConcurrentMap<Locale, LocaleProviderAdapter>> 117 adapterCache = new ConcurrentHashMap<>(); 118 119 static { 120 String order = GetPropertyAction.privilegedGetProperty("java.locale.providers"); 121 List<Type> typeList = new ArrayList<>(); 122 123 // Check user specified adapter preference 124 if (order != null && !order.isEmpty()) { 125 String[] types = order.split(","); 126 for (String type : types) { 127 type = type.trim().toUpperCase(Locale.ROOT); 128 if (type.equals("COMPAT")) { 129 type = "JRE"; 130 } 131 try { 132 Type aType = Type.valueOf(type.trim().toUpperCase(Locale.ROOT)); 133 if (!typeList.contains(aType)) { 134 typeList.add(aType); 135 } 136 } catch (IllegalArgumentException | UnsupportedOperationException e) { 137 // could be caused by the user specifying wrong 138 // provider name or format in the system property 139 LocaleServiceProviderPool.config(LocaleProviderAdapter.class, e.toString()); 140 } 141 } 142 } 143 144 defaultLocaleProviderAdapter = Type.CLDR; 145 if (!typeList.isEmpty()) { 146 // bona fide preference exists 147 if (!(typeList.contains(Type.CLDR) || (typeList.contains(Type.JRE)))) { 148 // Append FALLBACK as the last resort when no ResourceBundleBasedAdapter is available. 149 typeList.add(Type.FALLBACK); 150 defaultLocaleProviderAdapter = Type.FALLBACK; 151 } 152 } else { 153 // Default preference list. 154 typeList.add(Type.CLDR); 155 typeList.add(Type.JRE); 156 } 157 adapterPreference = Collections.unmodifiableList(typeList); 158 } 159 160 /** 161 * Returns the singleton instance for each adapter type 162 */ 163 public static LocaleProviderAdapter forType(Type type) { 164 switch (type) { 165 case JRE: 166 case CLDR: 167 case SPI: 168 case HOST: 169 case FALLBACK: 170 LocaleProviderAdapter adapter = null; 171 LocaleProviderAdapter cached = adapterInstances.get(type); 172 if (cached == null) { 173 try { 174 // lazily load adapters here 175 @SuppressWarnings("deprecation") 176 Object tmp = Class.forName(type.getAdapterClassName()).newInstance(); 177 adapter = (LocaleProviderAdapter)tmp; 178 cached = adapterInstances.putIfAbsent(type, adapter); 179 if (cached != null) { 180 adapter = cached; 181 } 182 } catch (ClassNotFoundException | 183 IllegalAccessException | 184 InstantiationException | 185 UnsupportedOperationException e) { 186 LocaleServiceProviderPool.config(LocaleProviderAdapter.class, e.toString()); 187 adapterInstances.putIfAbsent(type, NONEXISTENT_ADAPTER); 188 if (defaultLocaleProviderAdapter == type) { 189 defaultLocaleProviderAdapter = Type.FALLBACK; 190 } 191 } 192 } else if (cached != NONEXISTENT_ADAPTER) { 193 adapter = cached; 194 } 195 return adapter; 196 default: 197 throw new InternalError("unknown locale data adapter type"); 198 } 199 } 200 201 public static LocaleProviderAdapter forJRE() { 202 return forType(Type.JRE); 203 } 204 205 public static LocaleProviderAdapter getResourceBundleBased() { 206 for (Type type : getAdapterPreference()) { 207 if (type == Type.JRE || type == Type.CLDR || type == Type.FALLBACK) { 208 LocaleProviderAdapter adapter = forType(type); 209 if (adapter != null) { 210 return adapter; 211 } 212 } 213 } 214 // Shouldn't happen. 215 throw new InternalError(); 216 } 217 218 /** 219 * Returns the preference order of LocaleProviderAdapter.Type 220 */ 221 public static List<Type> getAdapterPreference() { 222 return adapterPreference; 223 } 224 225 /** 226 * Returns a LocaleProviderAdapter for the given locale service provider that 227 * best matches the given locale. This method returns the LocaleProviderAdapter 228 * for JRE if none is found for the given locale. 229 * 230 * @param providerClass the class for the locale service provider 231 * @param locale the desired locale. 232 * @return a LocaleProviderAdapter 233 */ 234 public static LocaleProviderAdapter getAdapter(Class<? extends LocaleServiceProvider> providerClass, 235 Locale locale) { 236 LocaleProviderAdapter adapter; 237 238 // cache lookup 239 ConcurrentMap<Locale, LocaleProviderAdapter> adapterMap = adapterCache.get(providerClass); 240 if (adapterMap != null) { 241 if ((adapter = adapterMap.get(locale)) != null) { 242 return adapter; 243 } 244 } else { 245 adapterMap = new ConcurrentHashMap<>(); 246 adapterCache.putIfAbsent(providerClass, adapterMap); 247 } 248 249 // Fast look-up for the given locale 250 adapter = findAdapter(providerClass, locale); 251 if (adapter != null) { 252 adapterMap.putIfAbsent(locale, adapter); 253 return adapter; 254 } 255 256 // Try finding an adapter in the normal candidate locales path of the given locale. 257 List<Locale> lookupLocales = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT) 258 .getCandidateLocales("", locale); 259 for (Locale loc : lookupLocales) { 260 if (loc.equals(locale)) { 261 // We've already done with this loc. 262 continue; 263 } 264 adapter = findAdapter(providerClass, loc); 265 if (adapter != null) { 266 adapterMap.putIfAbsent(locale, adapter); 267 return adapter; 268 } 269 } 270 271 // returns the adapter for FALLBACK as the last resort 272 adapterMap.putIfAbsent(locale, forType(Type.FALLBACK)); 273 return forType(Type.FALLBACK); 274 } 275 276 private static LocaleProviderAdapter findAdapter(Class<? extends LocaleServiceProvider> providerClass, 277 Locale locale) { 278 for (Type type : getAdapterPreference()) { 279 LocaleProviderAdapter adapter = forType(type); 280 if (adapter != null) { 281 LocaleServiceProvider provider = adapter.getLocaleServiceProvider(providerClass); 282 if (provider != null) { 283 if (provider.isSupportedLocale(locale)) { 284 return adapter; 285 } 286 } 287 } 288 } 289 return null; 290 } 291 292 /** 293 * A utility method for implementing the default LocaleServiceProvider.isSupportedLocale 294 * for the JRE, CLDR, and FALLBACK adapters. 295 */ 296 public boolean isSupportedProviderLocale(Locale locale, Set<String> langtags) { 297 LocaleProviderAdapter.Type type = getAdapterType(); 298 assert type == Type.JRE || type == Type.CLDR || type == Type.FALLBACK; 299 return false; 300 } 301 302 public static Locale[] toLocaleArray(Set<String> tags) { 303 Locale[] locs = new Locale[tags.size() + 1]; 304 int index = 0; 305 locs[index++] = Locale.ROOT; 306 for (String tag : tags) { 307 switch (tag) { 308 case "ja-JP-JP": 309 locs[index++] = JRELocaleConstants.JA_JP_JP; 310 break; 311 case "th-TH-TH": 312 locs[index++] = JRELocaleConstants.TH_TH_TH; 313 break; 314 default: 315 locs[index++] = Locale.forLanguageTag(tag); 316 break; 317 } 318 } 319 return locs; 320 } 321 322 /** 323 * Returns the type of this LocaleProviderAdapter 324 */ 325 public abstract LocaleProviderAdapter.Type getAdapterType(); 326 327 /** 328 * Getter method for Locale Service Providers. 329 */ 330 public abstract <P extends LocaleServiceProvider> P getLocaleServiceProvider(Class<P> c); 331 332 /** 333 * Returns a BreakIteratorProvider for this LocaleProviderAdapter, or null if no 334 * BreakIteratorProvider is available. 335 * 336 * @return a BreakIteratorProvider 337 */ 338 public abstract BreakIteratorProvider getBreakIteratorProvider(); 339 340 /** 341 * Returns a ollatorProvider for this LocaleProviderAdapter, or null if no 342 * ollatorProvider is available. 343 * 344 * @return a ollatorProvider 345 */ 346 public abstract CollatorProvider getCollatorProvider(); 347 348 /** 349 * Returns a DateFormatProvider for this LocaleProviderAdapter, or null if no 350 * DateFormatProvider is available. 351 * 352 * @return a DateFormatProvider 353 */ 354 public abstract DateFormatProvider getDateFormatProvider(); 355 356 /** 357 * Returns a DateFormatSymbolsProvider for this LocaleProviderAdapter, or null if no 358 * DateFormatSymbolsProvider is available. 359 * 360 * @return a DateFormatSymbolsProvider 361 */ 362 public abstract DateFormatSymbolsProvider getDateFormatSymbolsProvider(); 363 364 /** 365 * Returns a DecimalFormatSymbolsProvider for this LocaleProviderAdapter, or null if no 366 * DecimalFormatSymbolsProvider is available. 367 * 368 * @return a DecimalFormatSymbolsProvider 369 */ 370 public abstract DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider(); 371 372 /** 373 * Returns a NumberFormatProvider for this LocaleProviderAdapter, or null if no 374 * NumberFormatProvider is available. 375 * 376 * @return a NumberFormatProvider 377 */ 378 public abstract NumberFormatProvider getNumberFormatProvider(); 379 380 /* 381 * Getter methods for java.util.spi.* providers 382 */ 383 384 /** 385 * Returns a CurrencyNameProvider for this LocaleProviderAdapter, or null if no 386 * CurrencyNameProvider is available. 387 * 388 * @return a CurrencyNameProvider 389 */ 390 public abstract CurrencyNameProvider getCurrencyNameProvider(); 391 392 /** 393 * Returns a LocaleNameProvider for this LocaleProviderAdapter, or null if no 394 * LocaleNameProvider is available. 395 * 396 * @return a LocaleNameProvider 397 */ 398 public abstract LocaleNameProvider getLocaleNameProvider(); 399 400 /** 401 * Returns a TimeZoneNameProvider for this LocaleProviderAdapter, or null if no 402 * TimeZoneNameProvider is available. 403 * 404 * @return a TimeZoneNameProvider 405 */ 406 public abstract TimeZoneNameProvider getTimeZoneNameProvider(); 407 408 /** 409 * Returns a CalendarDataProvider for this LocaleProviderAdapter, or null if no 410 * CalendarDataProvider is available. 411 * 412 * @return a CalendarDataProvider 413 */ 414 public abstract CalendarDataProvider getCalendarDataProvider(); 415 416 /** 417 * Returns a CalendarNameProvider for this LocaleProviderAdapter, or null if no 418 * CalendarNameProvider is available. 419 * 420 * @return a CalendarNameProvider 421 */ 422 public abstract CalendarNameProvider getCalendarNameProvider(); 423 424 /** 425 * Returns a CalendarProvider for this LocaleProviderAdapter, or null if no 426 * CalendarProvider is available. 427 * 428 * @return a CalendarProvider 429 */ 430 public abstract CalendarProvider getCalendarProvider(); 431 432 /** 433 * Returns a JavaTimeDateTimePatternProvider for this LocaleProviderAdapter, 434 * or null if no JavaTimeDateTimePatternProvider is available. 435 * 436 * @return a JavaTimeDateTimePatternProvider 437 */ 438 public abstract JavaTimeDateTimePatternProvider getJavaTimeDateTimePatternProvider(); 439 440 public abstract LocaleResources getLocaleResources(Locale locale); 441 442 public abstract Locale[] getAvailableLocales(); 443 444 private static final LocaleProviderAdapter NONEXISTENT_ADAPTER = new NonExistentAdapter(); 445 private static final class NonExistentAdapter extends FallbackLocaleProviderAdapter { 446 @Override 447 public LocaleProviderAdapter.Type getAdapterType() { 448 return null; 449 } 450 451 private NonExistentAdapter() {}; 452 } 453 }