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.load(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 }