1 /*
2  * Copyright (c) 2010, 2017, 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 import java.io.BufferedReader;
25 import java.io.ByteArrayInputStream;
26 import java.io.ByteArrayOutputStream;
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.InputStreamReader;
30 import java.io.ObjectInputStream;
31 import java.io.ObjectOutputStream;
32 import java.net.URISyntaxException;
33 import java.net.URL;
34 import java.text.DecimalFormatSymbols;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.Calendar;
38 import java.util.IllformedLocaleException;
39 import java.util.List;
40 import java.util.Locale;
41 import java.util.Locale.Builder;
42 import java.util.Set;
43 
44 /**
45  * @test
46  * @bug 6875847 6992272 7002320 7015500 7023613 7032820 7033504 7004603
47  *    7044019 8008577 8176853
48  * @summary test API changes to Locale
49  * @library /java/text/testlib
50  * @modules jdk.localedata
51  * @compile LocaleEnhanceTest.java
52  * @run main/othervm -Djava.locale.providers=JRE,SPI -esa LocaleEnhanceTest
53  */
54 public class LocaleEnhanceTest extends IntlTest {
55 
main(String[] args)56     public static void main(String[] args) throws Exception {
57         List<String> argList = new ArrayList<String>();
58         argList.addAll(Arrays.asList(args));
59         argList.add("-nothrow");
60         new LocaleEnhanceTest().run(argList.toArray(new String[argList.size()]));
61     }
62 
LocaleEnhanceTest()63     public LocaleEnhanceTest() {
64     }
65 
66     ///
67     /// Generic sanity tests
68     ///
69 
70     /** A canonical language code. */
71     private static final String l = "en";
72 
73     /** A canonical script code.. */
74     private static final String s = "Latn";
75 
76     /** A canonical region code. */
77     private static final String c = "US";
78 
79     /** A canonical variant code. */
80     private static final String v = "NewYork";
81 
82     /**
83      * Ensure that Builder builds locales that have the expected
84      * tag and java6 ID.  Note the odd cases for the ID.
85      */
testCreateLocaleCanonicalValid()86     public void testCreateLocaleCanonicalValid() {
87         String[] valids = {
88             "en-Latn-US-NewYork", "en_US_NewYork_#Latn",
89             "en-Latn-US", "en_US_#Latn",
90             "en-Latn-NewYork", "en__NewYork_#Latn", // double underscore
91             "en-Latn", "en__#Latn", // double underscore
92             "en-US-NewYork", "en_US_NewYork",
93             "en-US", "en_US",
94             "en-NewYork", "en__NewYork", // double underscore
95             "en", "en",
96             "und-Latn-US-NewYork", "_US_NewYork_#Latn",
97             "und-Latn-US", "_US_#Latn",
98             "und-Latn-NewYork", "", // variant only not supported
99             "und-Latn", "",
100             "und-US-NewYork", "_US_NewYork",
101             "und-US", "_US",
102             "und-NewYork", "", // variant only not supported
103             "und", ""
104         };
105 
106         Builder builder = new Builder();
107 
108         for (int i = 0; i < valids.length; i += 2) {
109             String tag = valids[i];
110             String id = valids[i+1];
111 
112             String idl = (i & 16) == 0 ? l : "";
113             String ids = (i & 8) == 0 ? s : "";
114             String idc = (i & 4) == 0 ? c : "";
115             String idv = (i & 2) == 0 ? v : "";
116 
117             String msg = String.valueOf(i/2) + ": '" + tag + "' ";
118 
119             try {
120                 Locale l = builder
121                     .setLanguage(idl)
122                     .setScript(ids)
123                     .setRegion(idc)
124                     .setVariant(idv)
125                     .build();
126                 assertEquals(msg + "language", idl, l.getLanguage());
127                 assertEquals(msg + "script", ids, l.getScript());
128                 assertEquals(msg + "country", idc, l.getCountry());
129                 assertEquals(msg + "variant", idv, l.getVariant());
130                 assertEquals(msg + "tag", tag, l.toLanguageTag());
131                 assertEquals(msg + "id", id, l.toString());
132             }
133             catch (IllegalArgumentException e) {
134                 errln(msg + e.getMessage());
135             }
136         }
137     }
138 
139     /**
140      * Test that locale construction works with 'multiple variants'.
141      * <p>
142      * The string "Newer__Yorker" is treated as three subtags,
143      * "Newer", "", and "Yorker", and concatenated into one
144      * subtag by omitting empty subtags and joining the remainer
145      * with underscores.  So the resulting variant tag is "Newer_Yorker".
146      * Note that 'New' and 'York' are invalid BCP47 variant subtags
147      * because they are too short.
148      */
testCreateLocaleMultipleVariants()149     public void testCreateLocaleMultipleVariants() {
150 
151         String[] valids = {
152             "en-Latn-US-Newer-Yorker",  "en_US_Newer_Yorker_#Latn",
153             "en-Latn-Newer-Yorker",     "en__Newer_Yorker_#Latn",
154             "en-US-Newer-Yorker",       "en_US_Newer_Yorker",
155             "en-Newer-Yorker",          "en__Newer_Yorker",
156             "und-Latn-US-Newer-Yorker", "_US_Newer_Yorker_#Latn",
157             "und-Latn-Newer-Yorker",    "",
158             "und-US-Newer-Yorker",      "_US_Newer_Yorker",
159             "und-Newer-Yorker",         "",
160         };
161 
162         Builder builder = new Builder(); // lenient variant
163 
164         final String idv = "Newer_Yorker";
165         for (int i = 0; i < valids.length; i += 2) {
166             String tag = valids[i];
167             String id = valids[i+1];
168 
169             String idl = (i & 8) == 0 ? l : "";
170             String ids = (i & 4) == 0 ? s : "";
171             String idc = (i & 2) == 0 ? c : "";
172 
173             String msg = String.valueOf(i/2) + ": " + tag + " ";
174             try {
175                 Locale l = builder
176                     .setLanguage(idl)
177                     .setScript(ids)
178                     .setRegion(idc)
179                     .setVariant(idv)
180                     .build();
181 
182                 assertEquals(msg + " language", idl, l.getLanguage());
183                 assertEquals(msg + " script", ids, l.getScript());
184                 assertEquals(msg + " country", idc, l.getCountry());
185                 assertEquals(msg + " variant", idv, l.getVariant());
186 
187                 assertEquals(msg + "tag", tag, l.toLanguageTag());
188                 assertEquals(msg + "id", id, l.toString());
189             }
190             catch (IllegalArgumentException e) {
191                 errln(msg + e.getMessage());
192             }
193         }
194     }
195 
196     /**
197      * Ensure that all these invalid formats are not recognized by
198      * forLanguageTag.
199      */
testCreateLocaleCanonicalInvalidSeparator()200     public void testCreateLocaleCanonicalInvalidSeparator() {
201         String[] invalids = {
202             // trailing separator
203             "en_Latn_US_NewYork_",
204             "en_Latn_US_",
205             "en_Latn_",
206             "en_",
207             "_",
208 
209             // double separator
210             "en_Latn_US__NewYork",
211             "_Latn_US__NewYork",
212             "en_US__NewYork",
213             "_US__NewYork",
214 
215             // are these OK?
216             // "en_Latn__US_NewYork", // variant is 'US_NewYork'
217             // "_Latn__US_NewYork", // variant is 'US_NewYork'
218             // "en__Latn_US_NewYork", // variant is 'Latn_US_NewYork'
219             // "en__US_NewYork", // variant is 'US_NewYork'
220 
221             // double separator without language or script
222             "__US",
223             "__NewYork",
224 
225             // triple separator anywhere except within variant
226             "en___NewYork",
227             "en_Latn___NewYork",
228             "_Latn___NewYork",
229             "___NewYork",
230         };
231 
232         for (int i = 0; i < invalids.length; ++i) {
233             String id = invalids[i];
234             Locale l = Locale.forLanguageTag(id);
235             assertEquals(id, "und", l.toLanguageTag());
236         }
237     }
238 
239     /**
240      * Ensure that all current locale ids parse.  Use DateFormat as a proxy
241      * for all current locale ids.
242      */
testCurrentLocales()243     public void testCurrentLocales() {
244         Locale[] locales = java.text.DateFormat.getAvailableLocales();
245         Builder builder = new Builder();
246 
247         for (Locale target : locales) {
248             String tag = target.toLanguageTag();
249 
250             // the tag recreates the original locale,
251             // except no_NO_NY
252             Locale tagResult = Locale.forLanguageTag(tag);
253             if (!target.getVariant().equals("NY")) {
254                 assertEquals("tagResult", target, tagResult);
255             }
256 
257             // the builder also recreates the original locale,
258             // except ja_JP_JP, th_TH_TH and no_NO_NY
259             Locale builderResult = builder.setLocale(target).build();
260             if (target.getVariant().length() != 2) {
261                 assertEquals("builderResult", target, builderResult);
262             }
263         }
264     }
265 
266     /**
267      * Ensure that all icu locale ids parse.
268      */
testIcuLocales()269     public void testIcuLocales() throws Exception {
270         BufferedReader br = new BufferedReader(
271             new InputStreamReader(
272                 LocaleEnhanceTest.class.getResourceAsStream("icuLocales.txt"),
273                 "UTF-8"));
274         String id = null;
275         while (null != (id = br.readLine())) {
276             Locale result = Locale.forLanguageTag(id);
277             assertEquals("ulocale", id, result.toLanguageTag());
278         }
279     }
280 
281     ///
282     /// Compatibility tests
283     ///
284 
testConstructor()285     public void testConstructor() {
286         // all the old weirdness still holds, no new weirdness
287         String[][] tests = {
288             // language to lower case, region to upper, variant unchanged
289             // short
290             { "X", "y", "z", "x", "Y" },
291             // long
292             { "xXxXxXxXxXxX", "yYyYyYyYyYyYyYyY", "zZzZzZzZzZzZzZzZ",
293               "xxxxxxxxxxxx", "YYYYYYYYYYYYYYYY" },
294             // mapped language ids
295             { "he", "IW", "", "iw" },
296             { "iw", "IW", "", "iw" },
297             { "yi", "DE", "", "ji" },
298             { "ji", "DE", "", "ji" },
299             { "id", "ID", "", "in" },
300             { "in", "ID", "", "in" },
301             // special variants
302             { "ja", "JP", "JP" },
303             { "th", "TH", "TH" },
304             { "no", "NO", "NY" },
305             { "no", "NO", "NY" },
306             // no canonicalization of 3-letter language codes
307             { "eng", "US", "" }
308         };
309         for (int i = 0; i < tests.length; ++ i) {
310             String[] test = tests[i];
311             String id = String.valueOf(i);
312             Locale locale = new Locale(test[0], test[1], test[2]);
313             assertEquals(id + " lang", test.length > 3 ? test[3] : test[0], locale.getLanguage());
314             assertEquals(id + " region", test.length > 4 ? test[4] : test[1], locale.getCountry());
315             assertEquals(id + " variant", test.length > 5 ? test[5] : test[2], locale.getVariant());
316         }
317     }
318 
319     ///
320     /// Locale API tests.
321     ///
322 
testGetScript()323     public void testGetScript() {
324         // forLanguageTag normalizes case
325         Locale locale = Locale.forLanguageTag("und-latn");
326         assertEquals("forLanguageTag", "Latn", locale.getScript());
327 
328         // Builder normalizes case
329         locale = new Builder().setScript("LATN").build();
330         assertEquals("builder", "Latn", locale.getScript());
331 
332         // empty string is returned, not null, if there is no script
333         locale = Locale.forLanguageTag("und");
334         assertEquals("script is empty string", "", locale.getScript());
335     }
336 
testGetExtension()337     public void testGetExtension() {
338         // forLanguageTag does NOT normalize to hyphen
339         Locale locale = Locale.forLanguageTag("und-a-some_ex-tension");
340         assertEquals("some_ex-tension", null, locale.getExtension('a'));
341 
342         // regular extension
343         locale = new Builder().setExtension('a', "some-ex-tension").build();
344         assertEquals("builder", "some-ex-tension", locale.getExtension('a'));
345 
346         // returns null if extension is not present
347         assertEquals("empty b", null, locale.getExtension('b'));
348 
349         // throws exception if extension tag is illegal
350         new ExpectIAE() { public void call() { Locale.forLanguageTag("").getExtension('\uD800'); }};
351 
352         // 'x' is not an extension, it's a private use tag, but it's accessed through this API
353         locale = Locale.forLanguageTag("x-y-z-blork");
354         assertEquals("x", "y-z-blork", locale.getExtension('x'));
355     }
356 
testGetExtensionKeys()357     public void testGetExtensionKeys() {
358         Locale locale = Locale.forLanguageTag("und-a-xx-yy-b-zz-ww");
359         Set<Character> result = locale.getExtensionKeys();
360         assertEquals("result size", 2, result.size());
361         assertTrue("'a','b'", result.contains('a') && result.contains('b'));
362 
363         // result is not mutable
364         try {
365             result.add('x');
366             errln("expected exception on add to extension key set");
367         }
368         catch (UnsupportedOperationException e) {
369             // ok
370         }
371 
372         // returns empty set if no extensions
373         locale = Locale.forLanguageTag("und");
374         assertTrue("empty result", locale.getExtensionKeys().isEmpty());
375     }
376 
testGetUnicodeLocaleAttributes()377     public void testGetUnicodeLocaleAttributes() {
378         Locale locale = Locale.forLanguageTag("en-US-u-abc-def");
379         Set<String> attributes = locale.getUnicodeLocaleAttributes();
380         assertEquals("number of attributes", 2, attributes.size());
381         assertTrue("attribute abc", attributes.contains("abc"));
382         assertTrue("attribute def", attributes.contains("def"));
383 
384         locale = Locale.forLanguageTag("en-US-u-ca-gregory");
385         attributes = locale.getUnicodeLocaleAttributes();
386         assertTrue("empty attributes", attributes.isEmpty());
387     }
388 
testGetUnicodeLocaleType()389     public void testGetUnicodeLocaleType() {
390         Locale locale = Locale.forLanguageTag("und-u-co-japanese-nu-thai");
391         assertEquals("collation", "japanese", locale.getUnicodeLocaleType("co"));
392         assertEquals("numbers", "thai", locale.getUnicodeLocaleType("nu"));
393 
394         // Unicode locale extension key is case insensitive
395         assertEquals("key case", "japanese", locale.getUnicodeLocaleType("Co"));
396 
397         // if keyword is not present, returns null
398         assertEquals("locale keyword not present", null, locale.getUnicodeLocaleType("xx"));
399 
400         // if no locale extension is set, returns null
401         locale = Locale.forLanguageTag("und");
402         assertEquals("locale extension not present", null, locale.getUnicodeLocaleType("co"));
403 
404         // typeless keyword
405         locale = Locale.forLanguageTag("und-u-kn");
406         assertEquals("typeless keyword", "", locale.getUnicodeLocaleType("kn"));
407 
408         // invalid keys throw exception
409         new ExpectIAE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType("q"); }};
410         new ExpectIAE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType("abcdefghi"); }};
411 
412         // null argument throws exception
413         new ExpectNPE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType(null); }};
414     }
415 
testGetUnicodeLocaleKeys()416     public void testGetUnicodeLocaleKeys() {
417         Locale locale = Locale.forLanguageTag("und-u-co-japanese-nu-thai");
418         Set<String> result = locale.getUnicodeLocaleKeys();
419         assertEquals("two keys", 2, result.size());
420         assertTrue("co and nu", result.contains("co") && result.contains("nu"));
421 
422         // result is not modifiable
423         try {
424             result.add("frobozz");
425             errln("expected exception when add to locale key set");
426         }
427         catch (UnsupportedOperationException e) {
428             // ok
429         }
430     }
431 
testPrivateUseExtension()432     public void testPrivateUseExtension() {
433         Locale locale = Locale.forLanguageTag("x-y-x-blork-");
434         assertEquals("blork", "y-x-blork", locale.getExtension(Locale.PRIVATE_USE_EXTENSION));
435 
436         locale = Locale.forLanguageTag("und");
437         assertEquals("no privateuse", null, locale.getExtension(Locale.PRIVATE_USE_EXTENSION));
438     }
439 
testToLanguageTag()440     public void testToLanguageTag() {
441         // lots of normalization to test here
442         // test locales created using the constructor
443         String[][] tests = {
444             // empty locale canonicalizes to 'und'
445             { "", "", "", "und" },
446             // variant alone is not a valid Locale, but has a valid language tag
447             { "", "", "NewYork", "und-NewYork" },
448             // standard valid locales
449             { "", "Us", "", "und-US" },
450             { "", "US", "NewYork", "und-US-NewYork" },
451             { "EN", "", "", "en" },
452             { "EN", "", "NewYork", "en-NewYork" },
453             { "EN", "US", "", "en-US" },
454             { "EN", "US", "NewYork", "en-US-NewYork" },
455             // underscore in variant will be emitted as multiple variant subtags
456             { "en", "US", "Newer_Yorker", "en-US-Newer-Yorker" },
457             // invalid variant subtags are appended as private use
458             { "en", "US", "new_yorker", "en-US-x-lvariant-new-yorker" },
459             // the first invalid variant subtags and following variant subtags are appended as private use
460             { "en", "US", "Windows_XP_Home", "en-US-Windows-x-lvariant-XP-Home" },
461             // too long variant and following variant subtags disappear
462             { "en", "US", "WindowsVista_SP2", "en-US" },
463             // invalid region subtag disappears
464             { "en", "USA", "", "en" },
465             // invalid language tag disappears
466             { "e", "US", "", "und-US" },
467             // three-letter language tags are not canonicalized
468             { "Eng", "", "", "eng" },
469             // legacy languages canonicalize to modern equivalents
470             { "he", "IW", "", "he-IW" },
471             { "iw", "IW", "", "he-IW" },
472             { "yi", "DE", "", "yi-DE" },
473             { "ji", "DE", "", "yi-DE" },
474             { "id", "ID", "", "id-ID" },
475             { "in", "ID", "", "id-ID" },
476             // special values are converted on output
477             { "ja", "JP", "JP", "ja-JP-u-ca-japanese-x-lvariant-JP" },
478             { "th", "TH", "TH", "th-TH-u-nu-thai-x-lvariant-TH" },
479             { "no", "NO", "NY", "nn-NO" }
480         };
481         for (int i = 0; i < tests.length; ++i) {
482             String[] test = tests[i];
483             Locale locale = new Locale(test[0], test[1], test[2]);
484             assertEquals("case " + i, test[3], locale.toLanguageTag());
485         }
486 
487         // test locales created from forLanguageTag
488         String[][] tests1 = {
489             // case is normalized during the round trip
490             { "EN-us", "en-US" },
491             { "en-Latn-US", "en-Latn-US" },
492             // reordering Unicode locale extensions
493             { "de-u-co-phonebk-ca-gregory", "de-u-ca-gregory-co-phonebk" },
494             // private use only language tag is preserved (no extra "und")
495             { "x-elmer", "x-elmer" },
496             { "x-lvariant-JP", "x-lvariant-JP" },
497         };
498         for (String[] test : tests1) {
499             Locale locale = Locale.forLanguageTag(test[0]);
500             assertEquals("case " + test[0], test[1], locale.toLanguageTag());
501         }
502 
503     }
504 
testForLanguageTag()505     public void testForLanguageTag() {
506         // forLanguageTag implements the 'Language-Tag' production of
507         // BCP47, so it handles private use and grandfathered tags,
508         // unlike locale builder.  Tags listed below (except for the
509         // sample private use tags) come from 4646bis Feb 29, 2009.
510 
511         String[][] tests = {
512             // private use tags only
513             { "x-abc", "x-abc" },
514             { "x-a-b-c", "x-a-b-c" },
515             { "x-a-12345678", "x-a-12345678" },
516 
517             // grandfathered tags with preferred mappings
518             { "i-ami", "ami" },
519             { "i-bnn", "bnn" },
520             { "i-hak", "hak" },
521             { "i-klingon", "tlh" },
522             { "i-lux", "lb" }, // two-letter tag
523             { "i-navajo", "nv" }, // two-letter tag
524             { "i-pwn", "pwn" },
525             { "i-tao", "tao" },
526             { "i-tay", "tay" },
527             { "i-tsu", "tsu" },
528             { "art-lojban", "jbo" },
529             { "no-bok", "nb" },
530             { "no-nyn", "nn" },
531             { "sgn-BE-FR", "sfb" },
532             { "sgn-BE-NL", "vgt" },
533             { "sgn-CH-DE", "sgg" },
534             { "zh-guoyu", "cmn" },
535             { "zh-hakka", "hak" },
536             { "zh-min-nan", "nan" },
537             { "zh-xiang", "hsn" },
538 
539             // grandfathered irregular tags, no preferred mappings, drop illegal fields
540             // from end.  If no subtag is mappable, fallback to 'und'
541             { "i-default", "en-x-i-default" },
542             { "i-enochian", "x-i-enochian" },
543             { "i-mingo", "see-x-i-mingo" },
544             { "en-GB-oed", "en-GB-x-oed" },
545             { "zh-min", "nan-x-zh-min" },
546             { "cel-gaulish", "xtg-x-cel-gaulish" },
547         };
548         for (int i = 0; i < tests.length; ++i) {
549             String[] test = tests[i];
550             Locale locale = Locale.forLanguageTag(test[0]);
551             assertEquals("grandfathered case " + i, test[1], locale.toLanguageTag());
552         }
553 
554         // forLanguageTag ignores everything past the first place it encounters
555         // a syntax error
556         tests = new String[][] {
557             { "valid",
558               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y-12345678-z",
559               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y-12345678-z" },
560             { "segment of private use tag too long",
561               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y-123456789-z",
562               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y" },
563             { "segment of private use tag is empty",
564               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y--12345678-z",
565               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y" },
566             { "first segment of private use tag is empty",
567               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x--y-12345678-z",
568               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def" },
569             { "illegal extension tag",
570               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-\uD800-y-12345678-z",
571               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def" },
572             { "locale subtag with no value",
573               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-x-y-12345678-z",
574               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-x-y-12345678-z" },
575             { "locale key subtag invalid",
576               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-123456789-def-x-y-12345678-z",
577               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc" },
578             // locale key subtag invalid in earlier position, all following subtags
579             // dropped (and so the locale extension dropped as well)
580             { "locale key subtag invalid in earlier position",
581               "en-US-Newer-Yorker-a-bb-cc-dd-u-123456789-abc-bb-def-x-y-12345678-z",
582               "en-US-Newer-Yorker-a-bb-cc-dd" },
583         };
584         for (int i = 0; i < tests.length; ++i) {
585             String[] test = tests[i];
586             String msg = "syntax error case " + i + " " + test[0];
587             try {
588                 Locale locale = Locale.forLanguageTag(test[1]);
589                 assertEquals(msg, test[2], locale.toLanguageTag());
590             }
591             catch (IllegalArgumentException e) {
592                 errln(msg + " caught exception: " + e);
593             }
594         }
595 
596         // duplicated extension are just ignored
597         Locale locale = Locale.forLanguageTag("und-d-aa-00-bb-01-D-AA-10-cc-11-c-1234");
598         assertEquals("extension", "aa-00-bb-01", locale.getExtension('d'));
599         assertEquals("extension c", "1234", locale.getExtension('c'));
600 
601         locale = Locale.forLanguageTag("und-U-ca-gregory-u-ca-japanese");
602         assertEquals("Unicode extension", "ca-gregory", locale.getExtension(Locale.UNICODE_LOCALE_EXTENSION));
603 
604         // redundant Unicode locale keys in an extension are ignored
605         locale = Locale.forLanguageTag("und-u-aa-000-bb-001-bB-002-cc-003-c-1234");
606         assertEquals("Unicode keywords", "aa-000-bb-001-cc-003", locale.getExtension(Locale.UNICODE_LOCALE_EXTENSION));
607         assertEquals("Duplicated Unicode locake key followed by an extension", "1234", locale.getExtension('c'));
608     }
609 
testGetDisplayScript()610     public void testGetDisplayScript() {
611         Locale latnLocale = Locale.forLanguageTag("und-latn");
612         Locale hansLocale = Locale.forLanguageTag("und-hans");
613 
614         Locale oldLocale = Locale.getDefault();
615 
616         Locale.setDefault(Locale.US);
617         assertEquals("latn US", "Latin", latnLocale.getDisplayScript());
618         assertEquals("hans US", "Simplified Han", hansLocale.getDisplayScript());
619 
620         Locale.setDefault(Locale.GERMANY);
621         assertEquals("latn DE", "Lateinisch", latnLocale.getDisplayScript());
622         assertEquals("hans DE", "Vereinfachte Chinesische Schrift", hansLocale.getDisplayScript());
623 
624         Locale.setDefault(oldLocale);
625     }
626 
testGetDisplayScriptWithLocale()627     public void testGetDisplayScriptWithLocale() {
628         Locale latnLocale = Locale.forLanguageTag("und-latn");
629         Locale hansLocale = Locale.forLanguageTag("und-hans");
630 
631         assertEquals("latn US", "Latin", latnLocale.getDisplayScript(Locale.US));
632         assertEquals("hans US", "Simplified Han", hansLocale.getDisplayScript(Locale.US));
633 
634         assertEquals("latn DE", "Lateinisch", latnLocale.getDisplayScript(Locale.GERMANY));
635         assertEquals("hans DE", "Vereinfachte Chinesische Schrift", hansLocale.getDisplayScript(Locale.GERMANY));
636     }
637 
testGetDisplayName()638     public void testGetDisplayName() {
639         final Locale[] testLocales = {
640                 Locale.ROOT,
641                 new Locale("en"),
642                 new Locale("en", "US"),
643                 new Locale("", "US"),
644                 new Locale("no", "NO", "NY"),
645                 new Locale("", "", "NY"),
646                 Locale.forLanguageTag("zh-Hans"),
647                 Locale.forLanguageTag("zh-Hant"),
648                 Locale.forLanguageTag("zh-Hans-CN"),
649                 Locale.forLanguageTag("und-Hans"),
650         };
651 
652         final String[] displayNameEnglish = {
653                 "",
654                 "English",
655                 "English (United States)",
656                 "United States",
657                 "Norwegian (Norway,Nynorsk)",
658                 "Nynorsk",
659                 "Chinese (Simplified Han)",
660                 "Chinese (Traditional Han)",
661                 "Chinese (Simplified Han,China)",
662                 "Simplified Han",
663         };
664 
665         final String[] displayNameSimplifiedChinese = {
666                 "",
667                 "\u82f1\u6587",
668                 "\u82f1\u6587 (\u7f8e\u56fd)",
669                 "\u7f8e\u56fd",
670                 "\u632a\u5a01\u6587 (\u632a\u5a01,Nynorsk)",
671                 "Nynorsk",
672                 "\u4e2d\u6587 (\u7b80\u4f53\u4e2d\u6587)",
673                 "\u4e2d\u6587 (\u7e41\u4f53\u4e2d\u6587)",
674                 "\u4e2d\u6587 (\u7b80\u4f53\u4e2d\u6587,\u4e2d\u56fd)",
675                 "\u7b80\u4f53\u4e2d\u6587",
676         };
677 
678         for (int i = 0; i < testLocales.length; i++) {
679             Locale loc = testLocales[i];
680             assertEquals("English display name for " + loc.toLanguageTag(),
681                     displayNameEnglish[i], loc.getDisplayName(Locale.ENGLISH));
682             assertEquals("Simplified Chinese display name for " + loc.toLanguageTag(),
683                     displayNameSimplifiedChinese[i], loc.getDisplayName(Locale.CHINA));
684         }
685     }
686 
687     ///
688     /// Builder tests
689     ///
690 
testBuilderSetLocale()691     public void testBuilderSetLocale() {
692         Builder builder = new Builder();
693         Builder lenientBuilder = new Builder();
694 
695         String languageTag = "en-Latn-US-NewYork-a-bb-ccc-u-co-japanese-x-y-z";
696         String target = "en-Latn-US-NewYork-a-bb-ccc-u-co-japanese-x-y-z";
697 
698         Locale locale = Locale.forLanguageTag(languageTag);
699         Locale result = lenientBuilder
700             .setLocale(locale)
701             .build();
702         assertEquals("long tag", target, result.toLanguageTag());
703         assertEquals("long tag", locale, result);
704 
705         // null is illegal
706         new BuilderNPE("locale") {
707             public void call() { b.setLocale(null); }
708         };
709 
710         // builder canonicalizes the three legacy locales:
711         // ja_JP_JP, th_TH_TH, no_NY_NO.
712         locale = builder.setLocale(new Locale("ja", "JP", "JP")).build();
713         assertEquals("ja_JP_JP languagetag", "ja-JP-u-ca-japanese", locale.toLanguageTag());
714         assertEquals("ja_JP_JP variant", "", locale.getVariant());
715 
716         locale = builder.setLocale(new Locale("th", "TH", "TH")).build();
717         assertEquals("th_TH_TH languagetag", "th-TH-u-nu-thai", locale.toLanguageTag());
718         assertEquals("th_TH_TH variant", "", locale.getVariant());
719 
720         locale = builder.setLocale(new Locale("no", "NO", "NY")).build();
721         assertEquals("no_NO_NY languagetag", "nn-NO", locale.toLanguageTag());
722         assertEquals("no_NO_NY language", "nn", locale.getLanguage());
723         assertEquals("no_NO_NY variant", "", locale.getVariant());
724 
725         // non-canonical, non-legacy locales are invalid
726         new BuilderILE("123_4567_89") {
727             public void call() {
728                 b.setLocale(new Locale("123", "4567", "89"));
729             }
730         };
731     }
732 
testBuilderSetLanguageTag()733     public void testBuilderSetLanguageTag() {
734         String source = "eN-LaTn-Us-NewYork-A-Xx-B-Yy-X-1-2-3";
735         String target = "en-Latn-US-NewYork-a-xx-b-yy-x-1-2-3";
736         Builder builder = new Builder();
737         String result = builder
738             .setLanguageTag(source)
739             .build()
740             .toLanguageTag();
741         assertEquals("language", target, result);
742 
743         // redundant extensions cause a failure
744         new BuilderILE() { public void call() { b.setLanguageTag("und-a-xx-yy-b-ww-A-00-11-c-vv"); }};
745 
746         // redundant Unicode locale extension keys within an Unicode locale extension cause a failure
747         new BuilderILE() { public void call() { b.setLanguageTag("und-u-nu-thai-NU-chinese-xx-1234"); }};
748     }
749 
testBuilderSetLanguage()750     public void testBuilderSetLanguage() {
751         // language is normalized to lower case
752         String source = "eN";
753         String target = "en";
754         String defaulted = "";
755         Builder builder = new Builder();
756         String result = builder
757             .setLanguage(source)
758             .build()
759             .getLanguage();
760         assertEquals("en", target, result);
761 
762         // setting with empty resets
763         result = builder
764             .setLanguage(target)
765             .setLanguage("")
766             .build()
767             .getLanguage();
768         assertEquals("empty", defaulted, result);
769 
770         // setting with null resets too
771         result = builder
772                 .setLanguage(target)
773                 .setLanguage(null)
774                 .build()
775                 .getLanguage();
776         assertEquals("null", defaulted, result);
777 
778         // language codes must be 2-8 alpha
779         // for forwards compatibility, 4-alpha and 5-8 alpha (registered)
780         // languages are accepted syntax
781         new BuilderILE("q", "abcdefghi", "13") { public void call() { b.setLanguage(arg); }};
782 
783         // language code validation is NOT performed, any 2-8-alpha passes
784         assertNotNull("2alpha", builder.setLanguage("zz").build());
785         assertNotNull("8alpha", builder.setLanguage("abcdefgh").build());
786 
787         // three-letter language codes are NOT canonicalized to two-letter
788         result = builder
789             .setLanguage("eng")
790             .build()
791             .getLanguage();
792         assertEquals("eng", "eng", result);
793     }
794 
testBuilderSetScript()795     public void testBuilderSetScript() {
796         // script is normalized to title case
797         String source = "lAtN";
798         String target = "Latn";
799         String defaulted = "";
800         Builder builder = new Builder();
801         String result = builder
802             .setScript(source)
803             .build()
804             .getScript();
805         assertEquals("script", target, result);
806 
807         // setting with empty resets
808         result = builder
809             .setScript(target)
810             .setScript("")
811             .build()
812             .getScript();
813         assertEquals("empty", defaulted, result);
814 
815         // settting with null also resets
816         result = builder
817                 .setScript(target)
818                 .setScript(null)
819                 .build()
820                 .getScript();
821         assertEquals("null", defaulted, result);
822 
823         // ill-formed script codes throw IAE
824         // must be 4alpha
825         new BuilderILE("abc", "abcde", "l3tn") { public void call() { b.setScript(arg); }};
826 
827         // script code validation is NOT performed, any 4-alpha passes
828         assertEquals("4alpha", "Wxyz", builder.setScript("wxyz").build().getScript());
829     }
830 
testBuilderSetRegion()831     public void testBuilderSetRegion() {
832         // region is normalized to upper case
833         String source = "uS";
834         String target = "US";
835         String defaulted = "";
836         Builder builder = new Builder();
837         String result = builder
838             .setRegion(source)
839             .build()
840             .getCountry();
841         assertEquals("us", target, result);
842 
843         // setting with empty resets
844         result = builder
845             .setRegion(target)
846             .setRegion("")
847             .build()
848             .getCountry();
849         assertEquals("empty", defaulted, result);
850 
851         // setting with null also resets
852         result = builder
853                 .setRegion(target)
854                 .setRegion(null)
855                 .build()
856                 .getCountry();
857         assertEquals("null", defaulted, result);
858 
859         // ill-formed region codes throw IAE
860         // 2 alpha or 3 numeric
861         new BuilderILE("q", "abc", "12", "1234", "a3", "12a") { public void call() { b.setRegion(arg); }};
862 
863         // region code validation is NOT performed, any 2-alpha or 3-digit passes
864         assertEquals("2alpha", "ZZ", builder.setRegion("ZZ").build().getCountry());
865         assertEquals("3digit", "000", builder.setRegion("000").build().getCountry());
866     }
867 
testBuilderSetVariant()868     public void testBuilderSetVariant() {
869         // Variant case is not normalized in lenient variant mode
870         String source = "NewYork";
871         String target = source;
872         String defaulted = "";
873         Builder builder = new Builder();
874         String result = builder
875             .setVariant(source)
876             .build()
877             .getVariant();
878         assertEquals("NewYork", target, result);
879 
880         result = builder
881             .setVariant("NeWeR_YoRkEr")
882             .build()
883             .toLanguageTag();
884         assertEquals("newer yorker", "und-NeWeR-YoRkEr", result);
885 
886         // subtags of variant are NOT reordered
887         result = builder
888             .setVariant("zzzzz_yyyyy_xxxxx")
889             .build()
890             .getVariant();
891         assertEquals("zyx", "zzzzz_yyyyy_xxxxx", result);
892 
893         // setting to empty resets
894         result = builder
895             .setVariant(target)
896             .setVariant("")
897             .build()
898             .getVariant();
899         assertEquals("empty", defaulted, result);
900 
901         // setting to null also resets
902         result = builder
903                 .setVariant(target)
904                 .setVariant(null)
905                 .build()
906                 .getVariant();
907         assertEquals("null", defaulted, result);
908 
909         // ill-formed variants throw IAE
910         // digit followed by 3-7 characters, or alpha followed by 4-8 characters.
911         new BuilderILE("abcd", "abcdefghi", "1ab", "1abcdefgh") { public void call() { b.setVariant(arg); }};
912 
913         // 4 characters is ok as long as the first is a digit
914         assertEquals("digit+3alpha", "1abc", builder.setVariant("1abc").build().getVariant());
915 
916         // all subfields must conform
917         new BuilderILE("abcde-fg") { public void call() { b.setVariant(arg); }};
918     }
919 
testBuilderSetExtension()920     public void testBuilderSetExtension() {
921         // upper case characters are normalized to lower case
922         final char sourceKey = 'a';
923         final String sourceValue = "aB-aBcdefgh-12-12345678";
924         String target = "ab-abcdefgh-12-12345678";
925         Builder builder = new Builder();
926         String result = builder
927             .setExtension(sourceKey, sourceValue)
928             .build()
929             .getExtension(sourceKey);
930         assertEquals("extension", target, result);
931 
932         // setting with empty resets
933         result = builder
934             .setExtension(sourceKey, sourceValue)
935             .setExtension(sourceKey, "")
936             .build()
937             .getExtension(sourceKey);
938         assertEquals("empty", null, result);
939 
940         // setting with null also resets
941         result = builder
942                 .setExtension(sourceKey, sourceValue)
943                 .setExtension(sourceKey, null)
944                 .build()
945                 .getExtension(sourceKey);
946         assertEquals("null", null, result);
947 
948         // ill-formed extension keys throw IAE
949         // must be in [0-9a-ZA-Z]
950         new BuilderILE("$") { public void call() { b.setExtension('$', sourceValue); }};
951 
952         // each segment of value must be 2-8 alphanum
953         new BuilderILE("ab-cd-123456789") { public void call() { b.setExtension(sourceKey, arg); }};
954 
955         // no multiple hyphens.
956         new BuilderILE("ab--cd") { public void call() { b.setExtension(sourceKey, arg); }};
957 
958         // locale extension key has special handling
959         Locale locale = builder
960             .setExtension('u', "co-japanese")
961             .build();
962         assertEquals("locale extension", "japanese", locale.getUnicodeLocaleType("co"));
963 
964         // locale extension has same behavior with set locale keyword
965         Locale locale2 = builder
966             .setUnicodeLocaleKeyword("co", "japanese")
967             .build();
968         assertEquals("locales with extension", locale, locale2);
969 
970         // setting locale extension overrides all previous calls to setLocaleKeyword
971         Locale locale3 = builder
972             .setExtension('u', "xxx-nu-thai")
973             .build();
974         assertEquals("remove co", null, locale3.getUnicodeLocaleType("co"));
975         assertEquals("override thai", "thai", locale3.getUnicodeLocaleType("nu"));
976         assertEquals("override attribute", 1, locale3.getUnicodeLocaleAttributes().size());
977 
978         // setting locale keyword extends values already set by the locale extension
979         Locale locale4 = builder
980             .setUnicodeLocaleKeyword("co", "japanese")
981             .build();
982         assertEquals("extend", "japanese", locale4.getUnicodeLocaleType("co"));
983         assertEquals("extend", "thai", locale4.getUnicodeLocaleType("nu"));
984 
985         // locale extension subtags are reordered
986         result = builder
987             .clear()
988             .setExtension('u', "456-123-zz-123-yy-456-xx-789")
989             .build()
990             .toLanguageTag();
991         assertEquals("reorder", "und-u-123-456-xx-789-yy-456-zz-123", result);
992 
993         // multiple keyword types
994         result = builder
995             .clear()
996             .setExtension('u', "nu-thai-foobar")
997             .build()
998             .getUnicodeLocaleType("nu");
999         assertEquals("multiple types", "thai-foobar", result);
1000 
1001         // redundant locale extensions are ignored
1002         result = builder
1003             .clear()
1004             .setExtension('u', "nu-thai-NU-chinese-xx-1234")
1005             .build()
1006             .toLanguageTag();
1007         assertEquals("duplicate keys", "und-u-nu-thai-xx-1234", result);
1008     }
1009 
testBuilderAddUnicodeLocaleAttribute()1010     public void testBuilderAddUnicodeLocaleAttribute() {
1011         Builder builder = new Builder();
1012         Locale locale = builder
1013             .addUnicodeLocaleAttribute("def")
1014             .addUnicodeLocaleAttribute("abc")
1015             .build();
1016 
1017         Set<String> uattrs = locale.getUnicodeLocaleAttributes();
1018         assertEquals("number of attributes", 2, uattrs.size());
1019         assertTrue("attribute abc", uattrs.contains("abc"));
1020         assertTrue("attribute def", uattrs.contains("def"));
1021 
1022         // remove attribute
1023         locale = builder.removeUnicodeLocaleAttribute("xxx")
1024             .build();
1025 
1026         assertEquals("remove bogus", 2, uattrs.size());
1027 
1028         // add duplicate
1029         locale = builder.addUnicodeLocaleAttribute("abc")
1030             .build();
1031         assertEquals("add duplicate", 2, uattrs.size());
1032 
1033         // null attribute throws NPE
1034         new BuilderNPE("null attribute") { public void call() { b.addUnicodeLocaleAttribute(null); }};
1035         new BuilderNPE("null attribute removal") { public void call() { b.removeUnicodeLocaleAttribute(null); }};
1036 
1037         // illformed attribute throws IllformedLocaleException
1038         new BuilderILE("invalid attribute") { public void call() { b.addUnicodeLocaleAttribute("ca"); }};
1039     }
1040 
testBuildersetUnicodeLocaleKeyword()1041     public void testBuildersetUnicodeLocaleKeyword() {
1042         // Note: most behavior is tested in testBuilderSetExtension
1043         Builder builder = new Builder();
1044         Locale locale = builder
1045             .setUnicodeLocaleKeyword("co", "japanese")
1046             .setUnicodeLocaleKeyword("nu", "thai")
1047             .build();
1048         assertEquals("co", "japanese", locale.getUnicodeLocaleType("co"));
1049         assertEquals("nu", "thai", locale.getUnicodeLocaleType("nu"));
1050         assertEquals("keys", 2, locale.getUnicodeLocaleKeys().size());
1051 
1052         // can clear a keyword by setting to null, others remain
1053         String result = builder
1054             .setUnicodeLocaleKeyword("co", null)
1055             .build()
1056             .toLanguageTag();
1057         assertEquals("empty co", "und-u-nu-thai", result);
1058 
1059         // locale keyword extension goes when all keywords are gone
1060         result = builder
1061             .setUnicodeLocaleKeyword("nu", null)
1062             .build()
1063             .toLanguageTag();
1064         assertEquals("empty nu", "und", result);
1065 
1066         // locale keywords are ordered independent of order of addition
1067         result = builder
1068             .setUnicodeLocaleKeyword("zz", "012")
1069             .setUnicodeLocaleKeyword("aa", "345")
1070             .build()
1071             .toLanguageTag();
1072         assertEquals("reordered", "und-u-aa-345-zz-012", result);
1073 
1074         // null keyword throws NPE
1075         new BuilderNPE("keyword") { public void call() { b.setUnicodeLocaleKeyword(null, "thai"); }};
1076 
1077         // well-formed keywords are two alphanum
1078         new BuilderILE("a", "abc") { public void call() { b.setUnicodeLocaleKeyword(arg, "value"); }};
1079 
1080         // well-formed values are 3-8 alphanum
1081         new BuilderILE("ab", "abcdefghi") { public void call() { b.setUnicodeLocaleKeyword("ab", arg); }};
1082     }
1083 
testBuilderPrivateUseExtension()1084     public void testBuilderPrivateUseExtension() {
1085         // normalizes hyphens to underscore, case to lower
1086         String source = "c-B-a";
1087         String target = "c-b-a";
1088         Builder builder = new Builder();
1089         String result = builder
1090             .setExtension(Locale.PRIVATE_USE_EXTENSION, source)
1091             .build()
1092             .getExtension(Locale.PRIVATE_USE_EXTENSION);
1093         assertEquals("abc", target, result);
1094 
1095         // multiple hyphens are ill-formed
1096         new BuilderILE("a--b") { public void call() { b.setExtension(Locale.PRIVATE_USE_EXTENSION, arg); }};
1097     }
1098 
testBuilderClear()1099     public void testBuilderClear() {
1100         String monster = "en-latn-US-NewYork-a-bb-cc-u-co-japanese-x-z-y-x-x";
1101         Builder builder = new Builder();
1102         Locale locale = Locale.forLanguageTag(monster);
1103         String result = builder
1104             .setLocale(locale)
1105             .clear()
1106             .build()
1107             .toLanguageTag();
1108         assertEquals("clear", "und", result);
1109     }
1110 
testBuilderRemoveUnicodeAttribute()1111     public void testBuilderRemoveUnicodeAttribute() {
1112         // tested in testBuilderAddUnicodeAttribute
1113     }
1114 
testBuilderBuild()1115     public void testBuilderBuild() {
1116         // tested in other test methods
1117     }
1118 
testSerialize()1119     public void testSerialize() {
1120         final Locale[] testLocales = {
1121             Locale.ROOT,
1122             new Locale("en"),
1123             new Locale("en", "US"),
1124             new Locale("en", "US", "Win"),
1125             new Locale("en", "US", "Win_XP"),
1126             new Locale("ja", "JP"),
1127             new Locale("ja", "JP", "JP"),
1128             new Locale("th", "TH"),
1129             new Locale("th", "TH", "TH"),
1130             new Locale("no", "NO"),
1131             new Locale("nb", "NO"),
1132             new Locale("nn", "NO"),
1133             new Locale("no", "NO", "NY"),
1134             new Locale("nn", "NO", "NY"),
1135             new Locale("he", "IL"),
1136             new Locale("he", "IL", "var"),
1137             new Locale("Language", "Country", "Variant"),
1138             new Locale("", "US"),
1139             new Locale("", "", "Java"),
1140             Locale.forLanguageTag("en-Latn-US"),
1141             Locale.forLanguageTag("zh-Hans"),
1142             Locale.forLanguageTag("zh-Hant-TW"),
1143             Locale.forLanguageTag("ja-JP-u-ca-japanese"),
1144             Locale.forLanguageTag("und-Hant"),
1145             Locale.forLanguageTag("und-a-123-456"),
1146             Locale.forLanguageTag("en-x-java"),
1147             Locale.forLanguageTag("th-TH-u-ca-buddist-nu-thai-x-lvariant-TH"),
1148         };
1149 
1150         for (Locale locale : testLocales) {
1151             try {
1152                 // write
1153                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1154                 ObjectOutputStream oos = new ObjectOutputStream(bos);
1155                 oos.writeObject(locale);
1156 
1157                 // read
1158                 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
1159                 ObjectInputStream ois = new ObjectInputStream(bis);
1160                 Object o = ois.readObject();
1161 
1162                 assertEquals("roundtrip " + locale, locale, o);
1163             } catch (Exception e) {
1164                 errln(locale + " encountered exception:" + e.getLocalizedMessage());
1165             }
1166         }
1167     }
1168 
testDeserialize6()1169     public void testDeserialize6() {
1170         final String TESTFILEPREFIX = "java6locale_";
1171 
1172         File dataDir = null;
1173         String dataDirName = System.getProperty("serialized.data.dir");
1174         if (dataDirName == null) {
1175             URL resdirUrl = getClass().getClassLoader().getResource("serialized");
1176             if (resdirUrl != null) {
1177                 try {
1178                     dataDir = new File(resdirUrl.toURI());
1179                 } catch (URISyntaxException urie) {
1180                 }
1181             }
1182         } else {
1183             dataDir = new File(dataDirName);
1184         }
1185 
1186         if (dataDir == null) {
1187             errln("'dataDir' is null. serialized.data.dir Property value is "+dataDirName);
1188             return;
1189         } else if (!dataDir.isDirectory()) {
1190             errln("'dataDir' is not a directory. dataDir: "+dataDir.toString());
1191             return;
1192         }
1193 
1194         File[] files = dataDir.listFiles();
1195         for (File testfile : files) {
1196             if (testfile.isDirectory()) {
1197                 continue;
1198             }
1199             String name = testfile.getName();
1200             if (!name.startsWith(TESTFILEPREFIX)) {
1201                 continue;
1202             }
1203             Locale locale;
1204             String locStr = name.substring(TESTFILEPREFIX.length());
1205             if (locStr.equals("ROOT")) {
1206                 locale = Locale.ROOT;
1207             } else {
1208                 String[] fields = locStr.split("_", 3);
1209                 String lang = fields[0];
1210                 String country = (fields.length >= 2) ? fields[1] : "";
1211                 String variant = (fields.length == 3) ? fields[2] : "";
1212                 locale = new Locale(lang, country, variant);
1213             }
1214 
1215             // deserialize
1216             try (FileInputStream fis = new FileInputStream(testfile);
1217                  ObjectInputStream ois = new ObjectInputStream(fis))
1218             {
1219                 Object o = ois.readObject();
1220                 assertEquals("Deserialize Java 6 Locale " + locale, o, locale);
1221             } catch (Exception e) {
1222                 errln("Exception while reading " + testfile.getAbsolutePath() + " - " + e.getMessage());
1223             }
1224         }
1225     }
1226 
testBug7002320()1227     public void testBug7002320() {
1228         // forLanguageTag() and Builder.setLanguageTag(String)
1229         // should add a location extension for following two cases.
1230         //
1231         // 1. language/country are "ja"/"JP" and the resolved variant (x-lvariant-*)
1232         //    is exactly "JP" and no BCP 47 extensions are available, then add
1233         //    a Unicode locale extension "ca-japanese".
1234         // 2. language/country are "th"/"TH" and the resolved variant is exactly
1235         //    "TH" and no BCP 47 extensions are available, then add a Unicode locale
1236         //    extension "nu-thai".
1237         //
1238         String[][] testdata = {
1239             {"ja-JP-x-lvariant-JP", "ja-JP-u-ca-japanese-x-lvariant-JP"},   // special case 1
1240             {"ja-JP-x-lvariant-JP-XXX"},
1241             {"ja-JP-u-ca-japanese-x-lvariant-JP"},
1242             {"ja-JP-u-ca-gregory-x-lvariant-JP"},
1243             {"ja-JP-u-cu-jpy-x-lvariant-JP"},
1244             {"ja-x-lvariant-JP"},
1245             {"th-TH-x-lvariant-TH", "th-TH-u-nu-thai-x-lvariant-TH"},   // special case 2
1246             {"th-TH-u-nu-thai-x-lvariant-TH"},
1247             {"en-US-x-lvariant-JP"},
1248         };
1249 
1250         Builder bldr = new Builder();
1251 
1252         for (String[] data : testdata) {
1253             String in = data[0];
1254             String expected = (data.length == 1) ? data[0] : data[1];
1255 
1256             // forLanguageTag
1257             Locale loc = Locale.forLanguageTag(in);
1258             String out = loc.toLanguageTag();
1259             assertEquals("Language tag roundtrip by forLanguageTag with input: " + in, expected, out);
1260 
1261             // setLanguageTag
1262             bldr.clear();
1263             bldr.setLanguageTag(in);
1264             loc = bldr.build();
1265             out = loc.toLanguageTag();
1266             assertEquals("Language tag roundtrip by Builder.setLanguageTag with input: " + in, expected, out);
1267         }
1268     }
1269 
testBug7023613()1270     public void testBug7023613() {
1271         String[][] testdata = {
1272             {"en-Latn", "en__#Latn"},
1273             {"en-u-ca-japanese", "en__#u-ca-japanese"},
1274         };
1275 
1276         for (String[] data : testdata) {
1277             String in = data[0];
1278             String expected = (data.length == 1) ? data[0] : data[1];
1279 
1280             Locale loc = Locale.forLanguageTag(in);
1281             String out = loc.toString();
1282             assertEquals("Empty country field with non-empty script/extension with input: " + in, expected, out);
1283         }
1284     }
1285 
1286     /*
1287      * 7033504: (lc) incompatible behavior change for ja_JP_JP and th_TH_TH locales
1288      */
testBug7033504()1289     public void testBug7033504() {
1290         checkCalendar(new Locale("ja", "JP", "jp"), "java.util.GregorianCalendar");
1291         checkCalendar(new Locale("ja", "jp", "jp"), "java.util.GregorianCalendar");
1292         checkCalendar(new Locale("ja", "JP", "JP"), "java.util.JapaneseImperialCalendar");
1293         checkCalendar(new Locale("ja", "jp", "JP"), "java.util.JapaneseImperialCalendar");
1294         checkCalendar(Locale.forLanguageTag("en-u-ca-japanese"),
1295                       "java.util.JapaneseImperialCalendar");
1296 
1297         checkDigit(new Locale("th", "TH", "th"), '0');
1298         checkDigit(new Locale("th", "th", "th"), '0');
1299         checkDigit(new Locale("th", "TH", "TH"), '\u0e50');
1300         checkDigit(new Locale("th", "TH", "TH"), '\u0e50');
1301         checkDigit(Locale.forLanguageTag("en-u-nu-thai"), '\u0e50');
1302     }
1303 
checkCalendar(Locale loc, String expected)1304     private void checkCalendar(Locale loc, String expected) {
1305         Calendar cal = Calendar.getInstance(loc);
1306         assertEquals("Wrong calendar", expected, cal.getClass().getName());
1307     }
1308 
checkDigit(Locale loc, Character expected)1309     private void checkDigit(Locale loc, Character expected) {
1310         DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(loc);
1311         Character zero = dfs.getZeroDigit();
1312         assertEquals("Wrong digit zero char", expected, zero);
1313     }
1314 
1315     ///
1316     /// utility asserts
1317     ///
1318 
assertTrue(String msg, boolean v)1319     private void assertTrue(String msg, boolean v) {
1320         if (!v) {
1321             errln(msg + ": expected true");
1322         }
1323     }
1324 
assertFalse(String msg, boolean v)1325     private void assertFalse(String msg, boolean v) {
1326         if (v) {
1327             errln(msg + ": expected false");
1328         }
1329     }
1330 
assertEquals(String msg, Object e, Object v)1331     private void assertEquals(String msg, Object e, Object v) {
1332         if (e == null ? v != null : !e.equals(v)) {
1333             if (e != null) {
1334                 e = "'" + e + "'";
1335             }
1336             if (v != null) {
1337                 v = "'" + v + "'";
1338             }
1339             errln(msg + ": expected " + e + " but got " + v);
1340         }
1341     }
1342 
assertNotEquals(String msg, Object e, Object v)1343     private void assertNotEquals(String msg, Object e, Object v) {
1344         if (e == null ? v == null : e.equals(v)) {
1345             if (e != null) {
1346                 e = "'" + e + "'";
1347             }
1348             errln(msg + ": expected not equal " + e);
1349         }
1350     }
1351 
assertNull(String msg, Object o)1352     private void assertNull(String msg, Object o) {
1353         if (o != null) {
1354             errln(msg + ": expected null but got '" + o + "'");
1355         }
1356     }
1357 
assertNotNull(String msg, Object o)1358     private void assertNotNull(String msg, Object o) {
1359         if (o == null) {
1360             errln(msg + ": expected non null");
1361         }
1362     }
1363 
1364     // not currently used, might get rid of exceptions from the API
1365     private abstract class ExceptionTest {
1366         private final Class<? extends Exception> exceptionClass;
1367 
ExceptionTest(Class<? extends Exception> exceptionClass)1368         ExceptionTest(Class<? extends Exception> exceptionClass) {
1369             this.exceptionClass = exceptionClass;
1370         }
1371 
run()1372         public void run() {
1373             String failMsg = null;
1374             try {
1375                 call();
1376                 failMsg = "expected " + exceptionClass.getName() + "  but no exception thrown.";
1377             }
1378             catch (Exception e) {
1379                 if (!exceptionClass.isAssignableFrom(e.getClass())) {
1380                     failMsg = "expected " + exceptionClass.getName() + " but caught " + e;
1381                 }
1382             }
1383             if (failMsg != null) {
1384                 String msg = message();
1385                 msg = msg == null ? "" : msg + " ";
1386                 errln(msg + failMsg);
1387             }
1388         }
1389 
message()1390         public String message() {
1391             return null;
1392         }
1393 
call()1394         public abstract void call();
1395     }
1396 
1397     private abstract class ExpectNPE extends ExceptionTest {
ExpectNPE()1398         ExpectNPE() {
1399             super(NullPointerException.class);
1400             run();
1401         }
1402     }
1403 
1404     private abstract class BuilderNPE extends ExceptionTest {
1405         protected final String msg;
1406         protected final Builder b = new Builder();
1407 
BuilderNPE(String msg)1408         BuilderNPE(String msg) {
1409             super(NullPointerException.class);
1410 
1411             this.msg = msg;
1412 
1413             run();
1414         }
1415 
message()1416         public String message() {
1417             return msg;
1418         }
1419     }
1420 
1421     private abstract class ExpectIAE extends ExceptionTest {
ExpectIAE()1422         ExpectIAE() {
1423             super(IllegalArgumentException.class);
1424             run();
1425         }
1426     }
1427 
1428     private abstract class BuilderILE extends ExceptionTest {
1429         protected final String[] args;
1430         protected final Builder b = new Builder();
1431 
1432         protected String arg; // mutates during call
1433 
BuilderILE(String... args)1434         BuilderILE(String... args) {
1435             super(IllformedLocaleException.class);
1436 
1437             this.args = args;
1438 
1439             run();
1440         }
1441 
run()1442         public void run() {
1443             for (String arg : args) {
1444                 this.arg = arg;
1445                 super.run();
1446             }
1447         }
1448 
message()1449         public String message() {
1450             return "arg: '" + arg + "'";
1451         }
1452     }
1453 }
1454