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  */
46 import java.text.ParseException;
47 import java.text.SimpleDateFormat;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.List;
51 import java.util.Locale;
52 import java.util.ResourceBundle;
53 import java.util.Set;
54 import java.util.TimeZone;
55 
56 import sun.util.locale.provider.LocaleProviderAdapter;
57 import sun.util.locale.provider.LocaleProviderAdapter.Type;
58 
59 public 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 
main(String[] args)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      */
testTimeZoneParsing()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      */
testImplicitCldrLocales()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 
checkPresenceCldr(String testName, Locale[] got)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      */
testDateFormatSymbolsCache()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      */
testCandidateLocales()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 
reportDifference(List<Locale> got, List<Locale> expected, String locale)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      */
testImplicitCompatLocales()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 
checkPresenceCompat(String testName, Locale[] got)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