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 }