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