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