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