1/*
2 * Copyright (c) 2016, 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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 8167143
27 * @summary Test
28 * Timezone parsing works for all locales for default providers prefernce
29 * as well as when  prefernce list is [COMPAT, CLDR],
30 * CLDR implict locales are correctly reflected,
31 * th_TH bundle is not wrongly cached in DateFormatSymbols,
32 * correct candidate locale list is retrieved for
33 * zh_Hant and zh_Hans and
34 * Implict COMPAT Locales nn-NO, nb-NO are reflected in available locales
35 * for all Providers for COMPAT.
36 * @modules java.base/sun.util.locale.provider
37 *          java.base/sun.util.spi
38 *          jdk.localedata
39 * @run main/othervm -Djava.locale.providers=COMPAT,CLDR Bug8167143 testTimeZone
40 * @run main/othervm  Bug8167143 testTimeZone
41 * @run main/othervm -Djava.locale.providers=CLDR Bug8167143 testCldr
42 * @run main/othervm  Bug8167143 testCache
43 * @run main/othervm  Bug8167143 testCandidateLocales
44 * @run main/othervm  -Djava.locale.providers=COMPAT Bug8167143 testCompat
45 */
46import java.text.ParseException;
47import java.text.SimpleDateFormat;
48import java.util.ArrayList;
49import java.util.Arrays;
50import java.util.List;
51import java.util.Locale;
52import java.util.ResourceBundle;
53import java.util.Set;
54import java.util.TimeZone;
55
56import sun.util.locale.provider.LocaleProviderAdapter;
57import sun.util.locale.provider.LocaleProviderAdapter.Type;
58
59public class Bug8167143 {
60
61    private static final TimeZone REYKJAVIK = TimeZone.getTimeZone("Atlantic/Reykjavik");
62    private static final TimeZone NEW_YORK = TimeZone.getTimeZone("America/New_York");
63    private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
64
65    private static final List<Locale> CLDR_IMPLICIT_LOCS = List.of(Locale.forLanguageTag("zh-Hans-CN"),
66            Locale.forLanguageTag("zh-Hans-SG"),
67            Locale.forLanguageTag("zh-Hant-HK"),
68            Locale.forLanguageTag("zh-Hant-TW"),
69            Locale.forLanguageTag("zh-Hant-MO"));
70
71    private static final List<Locale> COMPAT_IMPLICIT_LOCS = List.of(Locale.forLanguageTag("nn-NO"),
72            Locale.forLanguageTag("nb-NO"));
73    /**
74     * List of candidate locales for zh_Hant
75     */
76    private static final List<Locale> ZH_HANT_CANDLOCS = List.of(
77            Locale.forLanguageTag("zh-Hant"),
78            Locale.forLanguageTag("zh-TW"),
79            Locale.forLanguageTag("zh"),
80            Locale.ROOT);
81    /**
82     * List of candidate locales for zh_Hans
83     */
84    private static final List<Locale> ZH_HANS_CANDLOCS = List.of(
85            Locale.forLanguageTag("zh-Hans"),
86            Locale.forLanguageTag("zh-CN"),
87            Locale.forLanguageTag("zh"),
88            Locale.ROOT);
89
90    public static void main(String[] args) {
91        switch (args[0]) {
92            case "testTimeZone":
93                testTimeZoneParsing();
94                break;
95            case "testCldr":
96                testImplicitCldrLocales();
97                break;
98            case "testCache":
99                testDateFormatSymbolsCache();
100                break;
101            case "testCandidateLocales":
102                testCandidateLocales();
103                break;
104            case "testCompat":
105                testImplicitCompatLocales();
106                break;
107            default:
108                throw new RuntimeException("no test was specified.");
109        }
110    }
111
112    /**
113     * Check that if Locale Provider Preference list is Default, or if Locale
114     * Provider Preference List is COMPAT,CLDR SimplDateFormat parsing works for
115     * all Available Locales.
116     */
117    private static void testTimeZoneParsing() {
118        Set<Locale> locales = Set.of(Locale.forLanguageTag("zh-hant"), new Locale("no", "NO", "NY"));
119        // Set<Locale> locales = Set.of(Locale.getAvailableLocales());
120        locales.forEach((locale) -> {
121            final SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd z", locale);
122            for (final TimeZone tz : new TimeZone[]{REYKJAVIK, GMT, NEW_YORK}) {
123                try {
124                    sdf.parse("2000/02/10 " + tz.getDisplayName(locale));
125                } catch (ParseException e) {
126                    throw new RuntimeException("TimeZone Parsing failed with Locale "
127                            + locale + " for TimeZone  " + tz.getDisplayName(), e);
128                }
129            }
130        });
131    }
132
133    /**
134     * Check that locales implicitly supported from CLDR are reflected in output
135     * from getAvailbleLocales() for each bundle.
136     *
137     */
138    private static void testImplicitCldrLocales() {
139        LocaleProviderAdapter cldr = LocaleProviderAdapter.forType(Type.CLDR);
140        checkPresenceCldr("CurrencyNameProvider",
141                cldr.getCurrencyNameProvider().getAvailableLocales());
142        checkPresenceCldr("LocaleNameProvider",
143                cldr.getLocaleNameProvider().getAvailableLocales());
144        checkPresenceCldr("TimeZoneNameProvider",
145                cldr.getTimeZoneNameProvider().getAvailableLocales());
146        checkPresenceCldr("CalendarDataProvider",
147                cldr.getCalendarDataProvider().getAvailableLocales());
148        checkPresenceCldr("CalendarNameProvider",
149                cldr.getCalendarProvider().getAvailableLocales());
150    }
151
152    private static void checkPresenceCldr(String testName, Locale[] got) {
153        List<Locale> gotLocalesList = Arrays.asList(got);
154        List<Locale> gotList = new ArrayList<>(gotLocalesList);
155        if (!testName.equals("TimeZoneNameProvider")) {
156            if (!gotList.removeAll(CLDR_IMPLICIT_LOCS)) {
157                // check which locale are not present in retrievedLocales List.
158                List<Locale> expectedLocales = new ArrayList<>(CLDR_IMPLICIT_LOCS);
159                expectedLocales.removeAll(gotList);
160                throw new RuntimeException("Locales those not correctly reflected are "
161                        + expectedLocales + " for test " + testName);
162            }
163        } else {
164            // check one extra locale zh_HK for TimeZoneNameProvider
165            Locale zh_HK = Locale.forLanguageTag("zh-HK");
166            if (!gotList.removeAll(CLDR_IMPLICIT_LOCS) && gotList.remove(zh_HK)) {
167                //check which locale are not present in retrievedLocales List
168                List<Locale> expectedLocales = new ArrayList<>(CLDR_IMPLICIT_LOCS);
169                expectedLocales.add(zh_HK);
170                expectedLocales.removeAll(gotList);
171                throw new RuntimeException("Locales those not correctly reflected are "
172                        + expectedLocales + " for test " + testName);
173            }
174        }
175    }
176
177    /**
178     * Check that if Locale Provider Preference list is default and if
179     * SimpleDateFormat instance for th-TH-TH is created first, then JRE bundle
180     * for th-TH should not be cached in cache of DateFormatSymbols class.
181     */
182    private static void testDateFormatSymbolsCache() {
183        Locale th_TH_TH = new Locale("th", "TH", "TH");
184        Locale th_TH = new Locale("th", "TH");
185        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd z", th_TH_TH);
186        String[][] thTHTHZoneStrings = sdf.getDateFormatSymbols().getZoneStrings();
187        String[][] thTHZoneStrings = sdf.getDateFormatSymbols().getZoneStrings();
188        if (Arrays.equals(thTHTHZoneStrings, thTHZoneStrings)) {
189            throw new RuntimeException("th_TH bundle still cached with DateFormatSymbols"
190                    + "cache for locale  " + th_TH
191            );
192        }
193    }
194
195    /**
196     * Check that candidate locales list retrieved for zh__Hant and for zh__Hans
197     * do not have first candidate locale as zh_TW_Hant and zh_CN_Hans
198     * respectively.
199     */
200    private static void testCandidateLocales() {
201        ResourceBundle.Control Control = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT);
202        Locale zh_Hant = Locale.forLanguageTag("zh-Hant");
203        Locale zh_Hans = Locale.forLanguageTag("zh-Hans");
204        List<Locale> zhHantCandidateLocs = Control.getCandidateLocales("", zh_Hant);
205        List<Locale> zhHansCandidateLocs = Control.getCandidateLocales("", zh_Hans);
206        if (!zhHantCandidateLocs.equals(ZH_HANT_CANDLOCS)) {
207            reportDifference(zhHantCandidateLocs, ZH_HANT_CANDLOCS, "zh_Hant");
208
209        }
210        if (!zhHansCandidateLocs.equals(ZH_HANS_CANDLOCS)) {
211            reportDifference(zhHansCandidateLocs, ZH_HANS_CANDLOCS, "zh_Hans");
212
213        }
214    }
215
216    private static void reportDifference(List<Locale> got, List<Locale> expected, String locale) {
217        List<Locale> retrievedList = new ArrayList<>(got);
218        List<Locale> expectedList = new ArrayList<>(expected);
219        retrievedList.removeAll(expectedList);
220        expectedList.removeAll(retrievedList);
221        if ((retrievedList.size() > 0) && (expectedList.size() > 0)) {
222            throw new RuntimeException(" retrievedList contain extra candidate locales " + retrievedList
223                    + " and missing candidate locales " + expectedList
224                    + "for locale " + locale);
225        }
226        if ((retrievedList.size() > 0)) {
227            throw new RuntimeException(" retrievedList contain extra candidate locales " + retrievedList
228                    + "for locale " + locale);
229        }
230        if ((expectedList.size() > 0)) {
231            throw new RuntimeException(" retrievedList contain extra candidate locales " + expectedList
232                    + "for locale " + locale);
233        }
234    }
235
236    /**
237     * checks that locales nn-NO  and nb-NO should be present in list of supported locales for
238     * all Providers for COMPAT.
239     */
240    private static void testImplicitCompatLocales() {
241        LocaleProviderAdapter jre = LocaleProviderAdapter.forJRE();
242        checkPresenceCompat("BreakIteratorProvider",
243            jre.getBreakIteratorProvider().getAvailableLocales());
244        checkPresenceCompat("CollatorProvider",
245            jre.getCollatorProvider().getAvailableLocales());
246        checkPresenceCompat("DateFormatProvider",
247            jre.getDateFormatProvider().getAvailableLocales());
248        checkPresenceCompat("DateFormatSymbolsProvider",
249            jre.getDateFormatSymbolsProvider().getAvailableLocales());
250        checkPresenceCompat("DecimalFormatSymbolsProvider",
251            jre.getDecimalFormatSymbolsProvider().getAvailableLocales());
252        checkPresenceCompat("NumberFormatProvider",
253            jre.getNumberFormatProvider().getAvailableLocales());
254        checkPresenceCompat("CurrencyNameProvider",
255            jre.getCurrencyNameProvider().getAvailableLocales());
256        checkPresenceCompat("LocaleNameProvider",
257            jre.getLocaleNameProvider().getAvailableLocales());
258        checkPresenceCompat("TimeZoneNameProvider",
259            jre.getTimeZoneNameProvider().getAvailableLocales());
260        checkPresenceCompat("CalendarDataProvider",
261            jre.getCalendarDataProvider().getAvailableLocales());
262        checkPresenceCompat("CalendarNameProvider",
263            jre.getCalendarNameProvider().getAvailableLocales());
264        checkPresenceCompat("CalendarProvider",
265            jre.getCalendarProvider().getAvailableLocales());
266    }
267
268    private static void checkPresenceCompat(String testName, Locale[] got) {
269        List<Locale> gotLocalesList = Arrays.asList(got);
270        List<Locale> gotList = new ArrayList<>(gotLocalesList);
271            if (!gotList.removeAll(COMPAT_IMPLICIT_LOCS)) {
272                // check which Implicit locale are not present in retrievedLocales List.
273                List<Locale> implicitLocales = new ArrayList<>(COMPAT_IMPLICIT_LOCS);
274                implicitLocales.removeAll(gotList);
275                throw new RuntimeException("Locales those not correctly reflected are "
276                        + implicitLocales + " for test " + testName);
277            }
278    }
279}
280