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