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