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
26package sun.util.locale.provider;
27
28import java.security.AccessController;
29import java.security.PrivilegedActionException;
30import java.security.PrivilegedExceptionAction;
31import java.text.BreakIterator;
32import java.text.Collator;
33import java.text.DateFormat;
34import java.text.DateFormatSymbols;
35import java.text.DecimalFormatSymbols;
36import java.text.NumberFormat;
37import java.text.spi.BreakIteratorProvider;
38import java.text.spi.CollatorProvider;
39import java.text.spi.DateFormatProvider;
40import java.text.spi.DateFormatSymbolsProvider;
41import java.text.spi.DecimalFormatSymbolsProvider;
42import java.text.spi.NumberFormatProvider;
43import java.util.Locale;
44import java.util.Map;
45import java.util.ServiceLoader;
46import java.util.concurrent.ConcurrentHashMap;
47import java.util.concurrent.ConcurrentMap;
48import java.util.spi.CalendarDataProvider;
49import java.util.spi.CalendarNameProvider;
50import java.util.spi.CurrencyNameProvider;
51import java.util.spi.LocaleNameProvider;
52import java.util.spi.LocaleServiceProvider;
53import java.util.spi.TimeZoneNameProvider;
54
55/**
56 * LocaleProviderAdapter implementation for the installed SPI implementations.
57 *
58 * @author Naoto Sato
59 * @author Masayoshi Okutsu
60 */
61public 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}
617