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