1 /* 2 * Copyright (c) 2012, 2018, 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.security.AccessController; 29 import java.security.PrivilegedActionException; 30 import java.security.PrivilegedExceptionAction; 31 import java.text.BreakIterator; 32 import java.text.Collator; 33 import java.text.DateFormat; 34 import java.text.DateFormatSymbols; 35 import java.text.DecimalFormatSymbols; 36 import java.text.NumberFormat; 37 import java.text.spi.BreakIteratorProvider; 38 import java.text.spi.CollatorProvider; 39 import java.text.spi.DateFormatProvider; 40 import java.text.spi.DateFormatSymbolsProvider; 41 import java.text.spi.DecimalFormatSymbolsProvider; 42 import java.text.spi.NumberFormatProvider; 43 import java.util.Arrays; 44 import java.util.Locale; 45 import java.util.Map; 46 import java.util.ServiceLoader; 47 import java.util.concurrent.ConcurrentHashMap; 48 import java.util.concurrent.ConcurrentMap; 49 import java.util.spi.CalendarDataProvider; 50 import java.util.spi.CalendarNameProvider; 51 import java.util.spi.CurrencyNameProvider; 52 import java.util.spi.LocaleNameProvider; 53 import java.util.spi.LocaleServiceProvider; 54 import java.util.spi.TimeZoneNameProvider; 55 56 /** 57 * LocaleProviderAdapter implementation for the installed SPI implementations. 58 * 59 * @author Naoto Sato 60 * @author Masayoshi Okutsu 61 */ 62 public class SPILocaleProviderAdapter extends AuxLocaleProviderAdapter { 63 64 /** 65 * Returns the type of this LocaleProviderAdapter 66 */ 67 @Override 68 public LocaleProviderAdapter.Type getAdapterType() { 69 return LocaleProviderAdapter.Type.SPI; 70 } 71 72 @Override 73 protected <P extends LocaleServiceProvider> P findInstalledProvider(final Class<P> c) { 74 try { 75 return AccessController.doPrivileged(new PrivilegedExceptionAction<P>() { 76 @Override 77 @SuppressWarnings(value={"unchecked", "deprecation"}) 78 public P run() { 79 P delegate = null; 80 81 for (LocaleServiceProvider provider : 82 ServiceLoader.load(c, ClassLoader.getSystemClassLoader())) { 83 if (delegate == null) { 84 try { 85 delegate = 86 (P) Class.forName(SPILocaleProviderAdapter.class.getCanonicalName() + 87 "$" + 88 c.getSimpleName() + 89 "Delegate") 90 .newInstance(); 91 } catch (ClassNotFoundException | 92 InstantiationException | 93 IllegalAccessException e) { 94 LocaleServiceProviderPool.config(SPILocaleProviderAdapter.class, e.toString()); 95 return null; 96 } 97 } 98 99 ((Delegate)delegate).addImpl(provider); 100 } 101 return delegate; 102 } 103 }); 104 } catch (PrivilegedActionException e) { 105 LocaleServiceProviderPool.config(SPILocaleProviderAdapter.class, e.toString()); 106 } 107 return null; 108 } 109 110 /* 111 * Delegate interface. All the implementations have to have the class name 112 * following "<provider class name>Delegate" convention. 113 */ 114 private interface Delegate<P extends LocaleServiceProvider> { 115 default public void addImpl(P impl) { 116 for (Locale l : impl.getAvailableLocales()) { 117 getDelegateMap().putIfAbsent(l, impl); 118 } 119 } 120 121 /* 122 * Obtain the real SPI implementation, using locale fallback 123 */ 124 default public P getImpl(Locale locale) { 125 for (Locale l : LocaleServiceProviderPool.getLookupLocales(locale.stripExtensions())) { 126 P ret = getDelegateMap().get(l); 127 if (ret != null) { 128 return ret; 129 } 130 } 131 return null; 132 } 133 134 public Map<Locale, P> getDelegateMap(); 135 136 default public Locale[] getAvailableLocalesDelegate() { 137 return getDelegateMap().keySet().stream().toArray(Locale[]::new); 138 } 139 140 default public boolean isSupportedLocaleDelegate(Locale locale) { 141 Map<Locale, P> map = getDelegateMap(); 142 Locale override = CalendarDataUtility.findRegionOverride(locale); 143 144 // First, call the method with extensions (if any) 145 P impl = map.get(override); 146 if (impl != null) { 147 return impl.isSupportedLocale(override); 148 } else { 149 // The default behavior 150 Locale overrideNoExt = override.stripExtensions(); 151 impl = map.get(overrideNoExt); 152 if (impl != null) { 153 return Arrays.stream(impl.getAvailableLocales()) 154 .anyMatch(overrideNoExt::equals); 155 } 156 } 157 158 return false; 159 } 160 } 161 162 /* 163 * Delegates for the actual SPI implementations. 164 */ 165 static class BreakIteratorProviderDelegate extends BreakIteratorProvider 166 implements Delegate<BreakIteratorProvider> { 167 private final Map<Locale, BreakIteratorProvider> map = new ConcurrentHashMap<>(); 168 169 @Override 170 public Map<Locale, BreakIteratorProvider> getDelegateMap() { 171 return map; 172 } 173 174 @Override 175 public Locale[] getAvailableLocales() { 176 return getAvailableLocalesDelegate(); 177 } 178 179 @Override 180 public boolean isSupportedLocale(Locale locale) { 181 return isSupportedLocaleDelegate(locale); 182 } 183 184 @Override 185 public BreakIterator getWordInstance(Locale locale) { 186 locale = CalendarDataUtility.findRegionOverride(locale); 187 BreakIteratorProvider bip = getImpl(locale); 188 return bip.getWordInstance(locale); 189 } 190 191 @Override 192 public BreakIterator getLineInstance(Locale locale) { 193 locale = CalendarDataUtility.findRegionOverride(locale); 194 BreakIteratorProvider bip = getImpl(locale); 195 return bip.getLineInstance(locale); 196 } 197 198 @Override 199 public BreakIterator getCharacterInstance(Locale locale) { 200 locale = CalendarDataUtility.findRegionOverride(locale); 201 BreakIteratorProvider bip = getImpl(locale); 202 return bip.getCharacterInstance(locale); 203 } 204 205 @Override 206 public BreakIterator getSentenceInstance(Locale locale) { 207 locale = CalendarDataUtility.findRegionOverride(locale); 208 BreakIteratorProvider bip = getImpl(locale); 209 return bip.getSentenceInstance(locale); 210 } 211 212 } 213 214 static class CollatorProviderDelegate extends CollatorProvider implements Delegate<CollatorProvider> { 215 private final Map<Locale, CollatorProvider> map = new ConcurrentHashMap<>(); 216 217 @Override 218 public Map<Locale, CollatorProvider> getDelegateMap() { 219 return map; 220 } 221 222 @Override 223 public Locale[] getAvailableLocales() { 224 return getAvailableLocalesDelegate(); 225 } 226 227 @Override 228 public boolean isSupportedLocale(Locale locale) { 229 return isSupportedLocaleDelegate(locale); 230 } 231 232 @Override 233 public Collator getInstance(Locale locale) { 234 locale = CalendarDataUtility.findRegionOverride(locale); 235 CollatorProvider cp = getImpl(locale); 236 return cp.getInstance(locale); 237 } 238 } 239 240 static class DateFormatProviderDelegate extends DateFormatProvider 241 implements Delegate<DateFormatProvider> { 242 private final Map<Locale, DateFormatProvider> map = new ConcurrentHashMap<>(); 243 244 @Override 245 public Map<Locale, DateFormatProvider> getDelegateMap() { 246 return map; 247 } 248 249 @Override 250 public Locale[] getAvailableLocales() { 251 return getAvailableLocalesDelegate(); 252 } 253 254 @Override 255 public boolean isSupportedLocale(Locale locale) { 256 return isSupportedLocaleDelegate(locale); 257 } 258 259 @Override 260 public DateFormat getTimeInstance(int style, Locale locale) { 261 locale = CalendarDataUtility.findRegionOverride(locale); 262 DateFormatProvider dfp = getImpl(locale); 263 return dfp.getTimeInstance(style, locale); 264 } 265 266 @Override 267 public DateFormat getDateInstance(int style, Locale locale) { 268 locale = CalendarDataUtility.findRegionOverride(locale); 269 DateFormatProvider dfp = getImpl(locale); 270 return dfp.getDateInstance(style, locale); 271 } 272 273 @Override 274 public DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale locale) { 275 locale = CalendarDataUtility.findRegionOverride(locale); 276 DateFormatProvider dfp = getImpl(locale); 277 return dfp.getDateTimeInstance(dateStyle, timeStyle, locale); 278 } 279 } 280 281 static class DateFormatSymbolsProviderDelegate extends DateFormatSymbolsProvider 282 implements Delegate<DateFormatSymbolsProvider> { 283 private final Map<Locale, DateFormatSymbolsProvider> map = new ConcurrentHashMap<>(); 284 285 @Override 286 public Map<Locale, DateFormatSymbolsProvider> getDelegateMap() { 287 return map; 288 } 289 290 @Override 291 public Locale[] getAvailableLocales() { 292 return getAvailableLocalesDelegate(); 293 } 294 295 @Override 296 public boolean isSupportedLocale(Locale locale) { 297 return isSupportedLocaleDelegate(locale); 298 } 299 300 @Override 301 public DateFormatSymbols getInstance(Locale locale) { 302 locale = CalendarDataUtility.findRegionOverride(locale); 303 DateFormatSymbolsProvider dfsp = getImpl(locale); 304 return dfsp.getInstance(locale); 305 } 306 } 307 308 static class DecimalFormatSymbolsProviderDelegate extends DecimalFormatSymbolsProvider 309 implements Delegate<DecimalFormatSymbolsProvider> { 310 private final Map<Locale, DecimalFormatSymbolsProvider> map = new ConcurrentHashMap<>(); 311 312 @Override 313 public Map<Locale, DecimalFormatSymbolsProvider> getDelegateMap() { 314 return map; 315 } 316 317 @Override 318 public Locale[] getAvailableLocales() { 319 return getAvailableLocalesDelegate(); 320 } 321 322 @Override 323 public boolean isSupportedLocale(Locale locale) { 324 return isSupportedLocaleDelegate(locale); 325 } 326 327 @Override 328 public DecimalFormatSymbols getInstance(Locale locale) { 329 locale = CalendarDataUtility.findRegionOverride(locale); 330 DecimalFormatSymbolsProvider dfsp = getImpl(locale); 331 return dfsp.getInstance(locale); 332 } 333 } 334 335 static class NumberFormatProviderDelegate extends NumberFormatProvider 336 implements Delegate<NumberFormatProvider> { 337 private final Map<Locale, NumberFormatProvider> map = new ConcurrentHashMap<>(); 338 339 @Override 340 public Map<Locale, NumberFormatProvider> getDelegateMap() { 341 return map; 342 } 343 344 @Override 345 public Locale[] getAvailableLocales() { 346 return getAvailableLocalesDelegate(); 347 } 348 349 @Override 350 public boolean isSupportedLocale(Locale locale) { 351 return isSupportedLocaleDelegate(locale); 352 } 353 354 @Override 355 public NumberFormat getCurrencyInstance(Locale locale) { 356 locale = CalendarDataUtility.findRegionOverride(locale); 357 NumberFormatProvider nfp = getImpl(locale); 358 return nfp.getCurrencyInstance(locale); 359 } 360 361 @Override 362 public NumberFormat getIntegerInstance(Locale locale) { 363 locale = CalendarDataUtility.findRegionOverride(locale); 364 NumberFormatProvider nfp = getImpl(locale); 365 return nfp.getIntegerInstance(locale); 366 } 367 368 @Override 369 public NumberFormat getNumberInstance(Locale locale) { 370 locale = CalendarDataUtility.findRegionOverride(locale); 371 NumberFormatProvider nfp = getImpl(locale); 372 return nfp.getNumberInstance(locale); 373 } 374 375 @Override 376 public NumberFormat getPercentInstance(Locale locale) { 377 locale = CalendarDataUtility.findRegionOverride(locale); 378 NumberFormatProvider nfp = getImpl(locale); 379 return nfp.getPercentInstance(locale); 380 } 381 382 @Override 383 public NumberFormat getCompactNumberInstance(Locale locale, 384 NumberFormat.Style style) { 385 locale = CalendarDataUtility.findRegionOverride(locale); 386 NumberFormatProvider nfp = getImpl(locale); 387 return nfp.getCompactNumberInstance(locale, style); 388 } 389 } 390 391 static class CalendarDataProviderDelegate extends CalendarDataProvider 392 implements Delegate<CalendarDataProvider> { 393 private final Map<Locale, CalendarDataProvider> map = new ConcurrentHashMap<>(); 394 395 @Override 396 public Map<Locale, CalendarDataProvider> getDelegateMap() { 397 return map; 398 } 399 400 @Override 401 public Locale[] getAvailableLocales() { 402 return getAvailableLocalesDelegate(); 403 } 404 405 @Override 406 public boolean isSupportedLocale(Locale locale) { 407 return isSupportedLocaleDelegate(locale); 408 } 409 410 @Override 411 public int getFirstDayOfWeek(Locale locale) { 412 locale = CalendarDataUtility.findRegionOverride(locale); 413 CalendarDataProvider cdp = getImpl(locale); 414 return cdp.getFirstDayOfWeek(locale); 415 } 416 417 @Override 418 public int getMinimalDaysInFirstWeek(Locale locale) { 419 locale = CalendarDataUtility.findRegionOverride(locale); 420 CalendarDataProvider cdp = getImpl(locale); 421 return cdp.getMinimalDaysInFirstWeek(locale); 422 } 423 } 424 425 static class CalendarNameProviderDelegate extends CalendarNameProvider 426 implements Delegate<CalendarNameProvider> { 427 private final Map<Locale, CalendarNameProvider> map = new ConcurrentHashMap<>(); 428 429 @Override 430 public Map<Locale, CalendarNameProvider> getDelegateMap() { 431 return map; 432 } 433 434 @Override 435 public Locale[] getAvailableLocales() { 436 return getAvailableLocalesDelegate(); 437 } 438 439 @Override 440 public boolean isSupportedLocale(Locale locale) { 441 return isSupportedLocaleDelegate(locale); 442 } 443 444 @Override 445 public String getDisplayName(String calendarType, 446 int field, int value, 447 int style, Locale locale) { 448 locale = CalendarDataUtility.findRegionOverride(locale); 449 CalendarNameProvider cdp = getImpl(locale); 450 return cdp.getDisplayName(calendarType, field, value, style, locale); 451 } 452 453 @Override 454 public Map<String, Integer> getDisplayNames(String calendarType, 455 int field, int style, 456 Locale locale) { 457 locale = CalendarDataUtility.findRegionOverride(locale); 458 CalendarNameProvider cdp = getImpl(locale); 459 return cdp.getDisplayNames(calendarType, field, style, locale); 460 } 461 } 462 463 static class CurrencyNameProviderDelegate extends CurrencyNameProvider 464 implements Delegate<CurrencyNameProvider> { 465 private final Map<Locale, CurrencyNameProvider> map = new ConcurrentHashMap<>(); 466 467 @Override 468 public Map<Locale, CurrencyNameProvider> getDelegateMap() { 469 return map; 470 } 471 472 @Override 473 public Locale[] getAvailableLocales() { 474 return getAvailableLocalesDelegate(); 475 } 476 477 @Override 478 public boolean isSupportedLocale(Locale locale) { 479 return isSupportedLocaleDelegate(locale); 480 } 481 482 @Override 483 public String getSymbol(String currencyCode, Locale locale) { 484 locale = CalendarDataUtility.findRegionOverride(locale); 485 CurrencyNameProvider cnp = getImpl(locale); 486 return cnp.getSymbol(currencyCode, locale); 487 } 488 489 @Override 490 public String getDisplayName(String currencyCode, Locale locale) { 491 locale = CalendarDataUtility.findRegionOverride(locale); 492 CurrencyNameProvider cnp = getImpl(locale); 493 return cnp.getDisplayName(currencyCode, locale); 494 } 495 } 496 497 static class LocaleNameProviderDelegate extends LocaleNameProvider 498 implements Delegate<LocaleNameProvider> { 499 private final Map<Locale, LocaleNameProvider> map = new ConcurrentHashMap<>(); 500 501 @Override 502 public Map<Locale, LocaleNameProvider> getDelegateMap() { 503 return map; 504 } 505 506 @Override 507 public Locale[] getAvailableLocales() { 508 return getAvailableLocalesDelegate(); 509 } 510 511 @Override 512 public boolean isSupportedLocale(Locale locale) { 513 return isSupportedLocaleDelegate(locale); 514 } 515 516 @Override 517 public String getDisplayLanguage(String languageCode, Locale locale) { 518 locale = CalendarDataUtility.findRegionOverride(locale); 519 LocaleNameProvider lnp = getImpl(locale); 520 return lnp.getDisplayLanguage(languageCode, locale); 521 } 522 523 @Override 524 public String getDisplayScript(String scriptCode, Locale locale) { 525 locale = CalendarDataUtility.findRegionOverride(locale); 526 LocaleNameProvider lnp = getImpl(locale); 527 return lnp.getDisplayScript(scriptCode, locale); 528 } 529 530 @Override 531 public String getDisplayCountry(String countryCode, Locale locale) { 532 locale = CalendarDataUtility.findRegionOverride(locale); 533 LocaleNameProvider lnp = getImpl(locale); 534 return lnp.getDisplayCountry(countryCode, locale); 535 } 536 537 @Override 538 public String getDisplayVariant(String variant, Locale locale) { 539 locale = CalendarDataUtility.findRegionOverride(locale); 540 LocaleNameProvider lnp = getImpl(locale); 541 return lnp.getDisplayVariant(variant, locale); 542 } 543 544 @Override 545 public String getDisplayUnicodeExtensionKey(String key, Locale locale) { 546 locale = CalendarDataUtility.findRegionOverride(locale); 547 LocaleNameProvider lnp = getImpl(locale); 548 return lnp.getDisplayUnicodeExtensionKey(key, locale); 549 } 550 551 @Override 552 public String getDisplayUnicodeExtensionType(String extType, String key, Locale locale) { 553 locale = CalendarDataUtility.findRegionOverride(locale); 554 LocaleNameProvider lnp = getImpl(locale); 555 return lnp.getDisplayUnicodeExtensionType(extType, key, locale); 556 } 557 } 558 559 static class TimeZoneNameProviderDelegate extends TimeZoneNameProvider 560 implements Delegate<TimeZoneNameProvider> { 561 private final Map<Locale, TimeZoneNameProvider> map = new ConcurrentHashMap<>(); 562 563 @Override 564 public Map<Locale, TimeZoneNameProvider> getDelegateMap() { 565 return map; 566 } 567 568 @Override 569 public Locale[] getAvailableLocales() { 570 return getAvailableLocalesDelegate(); 571 } 572 573 @Override 574 public boolean isSupportedLocale(Locale locale) { 575 return isSupportedLocaleDelegate(locale); 576 } 577 578 @Override 579 public String getDisplayName(String ID, boolean daylight, int style, Locale locale) { 580 locale = CalendarDataUtility.findRegionOverride(locale); 581 TimeZoneNameProvider tznp = getImpl(locale); 582 return tznp.getDisplayName(ID, daylight, style, locale); 583 } 584 585 @Override 586 public String getGenericDisplayName(String ID, int style, Locale locale) { 587 locale = CalendarDataUtility.findRegionOverride(locale); 588 TimeZoneNameProvider tznp = getImpl(locale); 589 return tznp.getGenericDisplayName(ID, style, locale); 590 } 591 } 592 }