< prev index next >

test/jdk/java/util/Locale/LocaleEnhanceTest.java

Print this page

   1 /*
   2  * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */

  24 import java.io.BufferedReader;
  25 import java.io.ByteArrayInputStream;
  26 import java.io.ByteArrayOutputStream;
  27 import java.io.File;
  28 import java.io.FileInputStream;
  29 import java.io.InputStreamReader;
  30 import java.io.ObjectInputStream;
  31 import java.io.ObjectOutputStream;
  32 import java.net.URISyntaxException;
  33 import java.net.URL;
  34 import java.text.DecimalFormatSymbols;
  35 import java.util.ArrayList;
  36 import java.util.Arrays;
  37 import java.util.Calendar;
  38 import java.util.IllformedLocaleException;
  39 import java.util.List;
  40 import java.util.Locale;
  41 import java.util.Locale.Builder;
  42 import java.util.Set;
  43 




  44 /**
  45  * @test
  46  * @bug 6875847 6992272 7002320 7015500 7023613 7032820 7033504 7004603
  47  *    7044019 8008577 8176853 8255086 8263202 8287868
  48  * @summary test API changes to Locale
  49  * @library /java/text/testlib
  50  * @modules jdk.localedata
  51  * @compile LocaleEnhanceTest.java
  52  * @run main/othervm -Djava.locale.providers=JRE,SPI -esa LocaleEnhanceTest
  53  */
  54 public class LocaleEnhanceTest extends IntlTest {
  55 
  56     public static void main(String[] args) throws Exception {
  57         List<String> argList = new ArrayList<String>();
  58         argList.addAll(Arrays.asList(args));
  59         argList.add("-nothrow");
  60         new LocaleEnhanceTest().run(argList.toArray(new String[argList.size()]));
  61     }
  62 
  63     public LocaleEnhanceTest() {
  64     }
  65 
  66     ///
  67     /// Generic sanity tests
  68     ///
  69 
  70     /** A canonical language code. */
  71     private static final String l = "en";
  72 
  73     /** A canonical script code.. */
  74     private static final String s = "Latn";
  75 
  76     /** A canonical region code. */
  77     private static final String c = "US";
  78 
  79     /** A canonical variant code. */
  80     private static final String v = "NewYork";
  81 
  82     /**
  83      * Ensure that Builder builds locales that have the expected
  84      * tag and java6 ID.  Note the odd cases for the ID.
  85      */

  86     public void testCreateLocaleCanonicalValid() {
  87         String[] valids = {
  88             "en-Latn-US-NewYork", "en_US_NewYork_#Latn",
  89             "en-Latn-US", "en_US_#Latn",
  90             "en-Latn-NewYork", "en__NewYork_#Latn", // double underscore
  91             "en-Latn", "en__#Latn", // double underscore
  92             "en-US-NewYork", "en_US_NewYork",
  93             "en-US", "en_US",
  94             "en-NewYork", "en__NewYork", // double underscore
  95             "en", "en",
  96             "und-Latn-US-NewYork", "_US_NewYork_#Latn",
  97             "und-Latn-US", "_US_#Latn",
  98             "und-Latn-NewYork", "", // variant only not supported
  99             "und-Latn", "",
 100             "und-US-NewYork", "_US_NewYork",
 101             "und-US", "_US",
 102             "und-NewYork", "", // variant only not supported
 103             "und", ""
 104         };
 105 

 114             String idc = (i & 4) == 0 ? c : "";
 115             String idv = (i & 2) == 0 ? v : "";
 116 
 117             String msg = String.valueOf(i/2) + ": '" + tag + "' ";
 118 
 119             try {
 120                 Locale l = builder
 121                     .setLanguage(idl)
 122                     .setScript(ids)
 123                     .setRegion(idc)
 124                     .setVariant(idv)
 125                     .build();
 126                 assertEquals(msg + "language", idl, l.getLanguage());
 127                 assertEquals(msg + "script", ids, l.getScript());
 128                 assertEquals(msg + "country", idc, l.getCountry());
 129                 assertEquals(msg + "variant", idv, l.getVariant());
 130                 assertEquals(msg + "tag", tag, l.toLanguageTag());
 131                 assertEquals(msg + "id", id, l.toString());
 132             }
 133             catch (IllegalArgumentException e) {
 134                 errln(msg + e.getMessage());
 135             }
 136         }
 137     }
 138 
 139     /**
 140      * Test that locale construction works with 'multiple variants'.
 141      * <p>
 142      * The string "Newer__Yorker" is treated as three subtags,
 143      * "Newer", "", and "Yorker", and concatenated into one
 144      * subtag by omitting empty subtags and joining the remainer
 145      * with underscores.  So the resulting variant tag is "Newer_Yorker".
 146      * Note that 'New' and 'York' are invalid BCP47 variant subtags
 147      * because they are too short.
 148      */

 149     public void testCreateLocaleMultipleVariants() {
 150 
 151         String[] valids = {
 152             "en-Latn-US-Newer-Yorker",  "en_US_Newer_Yorker_#Latn",
 153             "en-Latn-Newer-Yorker",     "en__Newer_Yorker_#Latn",
 154             "en-US-Newer-Yorker",       "en_US_Newer_Yorker",
 155             "en-Newer-Yorker",          "en__Newer_Yorker",
 156             "und-Latn-US-Newer-Yorker", "_US_Newer_Yorker_#Latn",
 157             "und-Latn-Newer-Yorker",    "",
 158             "und-US-Newer-Yorker",      "_US_Newer_Yorker",
 159             "und-Newer-Yorker",         "",
 160         };
 161 
 162         Builder builder = new Builder(); // lenient variant
 163 
 164         final String idv = "Newer_Yorker";
 165         for (int i = 0; i < valids.length; i += 2) {
 166             String tag = valids[i];
 167             String id = valids[i+1];
 168 

 171             String idc = (i & 2) == 0 ? c : "";
 172 
 173             String msg = String.valueOf(i/2) + ": " + tag + " ";
 174             try {
 175                 Locale l = builder
 176                     .setLanguage(idl)
 177                     .setScript(ids)
 178                     .setRegion(idc)
 179                     .setVariant(idv)
 180                     .build();
 181 
 182                 assertEquals(msg + " language", idl, l.getLanguage());
 183                 assertEquals(msg + " script", ids, l.getScript());
 184                 assertEquals(msg + " country", idc, l.getCountry());
 185                 assertEquals(msg + " variant", idv, l.getVariant());
 186 
 187                 assertEquals(msg + "tag", tag, l.toLanguageTag());
 188                 assertEquals(msg + "id", id, l.toString());
 189             }
 190             catch (IllegalArgumentException e) {
 191                 errln(msg + e.getMessage());
 192             }
 193         }
 194     }
 195 
 196     /**
 197      * Ensure that all these invalid formats are not recognized by
 198      * forLanguageTag.
 199      */

 200     public void testCreateLocaleCanonicalInvalidSeparator() {
 201         String[] invalids = {
 202             // trailing separator
 203             "en_Latn_US_NewYork_",
 204             "en_Latn_US_",
 205             "en_Latn_",
 206             "en_",
 207             "_",
 208 
 209             // double separator
 210             "en_Latn_US__NewYork",
 211             "_Latn_US__NewYork",
 212             "en_US__NewYork",
 213             "_US__NewYork",
 214 
 215             // are these OK?
 216             // "en_Latn__US_NewYork", // variant is 'US_NewYork'
 217             // "_Latn__US_NewYork", // variant is 'US_NewYork'
 218             // "en__Latn_US_NewYork", // variant is 'Latn_US_NewYork'
 219             // "en__US_NewYork", // variant is 'US_NewYork'

 223             "__NewYork",
 224 
 225             // triple separator anywhere except within variant
 226             "en___NewYork",
 227             "en_Latn___NewYork",
 228             "_Latn___NewYork",
 229             "___NewYork",
 230         };
 231 
 232         for (int i = 0; i < invalids.length; ++i) {
 233             String id = invalids[i];
 234             Locale l = Locale.forLanguageTag(id);
 235             assertEquals(id, "und", l.toLanguageTag());
 236         }
 237     }
 238 
 239     /**
 240      * Ensure that all current locale ids parse.  Use DateFormat as a proxy
 241      * for all current locale ids.
 242      */

 243     public void testCurrentLocales() {
 244         Locale[] locales = java.text.DateFormat.getAvailableLocales();
 245         Builder builder = new Builder();
 246 
 247         for (Locale target : locales) {
 248             String tag = target.toLanguageTag();
 249 
 250             // the tag recreates the original locale,
 251             // except no_NO_NY
 252             Locale tagResult = Locale.forLanguageTag(tag);
 253             if (!target.getVariant().equals("NY")) {
 254                 assertEquals("tagResult", target, tagResult);
 255             }
 256 
 257             // the builder also recreates the original locale,
 258             // except ja_JP_JP, th_TH_TH and no_NO_NY
 259             Locale builderResult = builder.setLocale(target).build();
 260             if (target.getVariant().length() != 2) {
 261                 assertEquals("builderResult", target, builderResult);
 262             }
 263         }
 264     }
 265 
 266     /**
 267      * Ensure that all icu locale ids parse.
 268      */

 269     public void testIcuLocales() throws Exception {
 270         BufferedReader br = new BufferedReader(
 271             new InputStreamReader(
 272                 LocaleEnhanceTest.class.getResourceAsStream("icuLocales.txt"),
 273                 "UTF-8"));
 274         String id = null;
 275         while (null != (id = br.readLine())) {
 276             Locale result = Locale.forLanguageTag(id);
 277             assertEquals("ulocale", id, result.toLanguageTag());
 278         }
 279     }
 280 
 281     ///
 282     /// Compatibility tests
 283     ///
 284 

 285     public void testConstructor() {
 286         // all the old weirdness still holds, no new weirdness
 287         String[][] tests = {
 288             // language to lower case, region to upper, variant unchanged
 289             // short
 290             { "X", "y", "z", "x", "Y" },
 291             // long
 292             { "xXxXxXxXxXxX", "yYyYyYyYyYyYyYyY", "zZzZzZzZzZzZzZzZ",
 293               "xxxxxxxxxxxx", "YYYYYYYYYYYYYYYY" },
 294             // mapped language ids
 295             { "he", "IL", "", "he" },
 296             { "iw", "IL", "", "he" },
 297             { "yi", "DE", "", "yi" },
 298             { "ji", "DE", "", "yi" },
 299             { "id", "ID", "", "id" },
 300             { "in", "ID", "", "id" },
 301             // special variants
 302             { "ja", "JP", "JP" },
 303             { "th", "TH", "TH" },
 304             { "no", "NO", "NY" },
 305             { "no", "NO", "NY" },
 306             // no canonicalization of 3-letter language codes
 307             { "eng", "US", "" }
 308         };
 309         for (int i = 0; i < tests.length; ++ i) {
 310             String[] test = tests[i];
 311             String id = String.valueOf(i);
 312             Locale locale = Locale.of(test[0], test[1], test[2]);
 313             assertEquals(id + " lang", test.length > 3 ? test[3] : test[0], locale.getLanguage());
 314             assertEquals(id + " region", test.length > 4 ? test[4] : test[1], locale.getCountry());
 315             assertEquals(id + " variant", test.length > 5 ? test[5] : test[2], locale.getVariant());
 316         }
 317     }
 318 
 319     ///
 320     /// Locale API tests.
 321     ///
 322 

 323     public void testGetScript() {
 324         // forLanguageTag normalizes case
 325         Locale locale = Locale.forLanguageTag("und-latn");
 326         assertEquals("forLanguageTag", "Latn", locale.getScript());
 327 
 328         // Builder normalizes case
 329         locale = new Builder().setScript("LATN").build();
 330         assertEquals("builder", "Latn", locale.getScript());
 331 
 332         // empty string is returned, not null, if there is no script
 333         locale = Locale.forLanguageTag("und");
 334         assertEquals("script is empty string", "", locale.getScript());
 335     }
 336 

 337     public void testGetExtension() {
 338         // forLanguageTag does NOT normalize to hyphen
 339         Locale locale = Locale.forLanguageTag("und-a-some_ex-tension");
 340         assertEquals("some_ex-tension", null, locale.getExtension('a'));
 341 
 342         // regular extension
 343         locale = new Builder().setExtension('a', "some-ex-tension").build();
 344         assertEquals("builder", "some-ex-tension", locale.getExtension('a'));
 345 
 346         // returns null if extension is not present
 347         assertEquals("empty b", null, locale.getExtension('b'));
 348 
 349         // throws exception if extension tag is illegal
 350         new ExpectIAE() { public void call() { Locale.forLanguageTag("").getExtension('\uD800'); }};
 351 
 352         // 'x' is not an extension, it's a private use tag, but it's accessed through this API
 353         locale = Locale.forLanguageTag("x-y-z-blork");
 354         assertEquals("x", "y-z-blork", locale.getExtension('x'));
 355     }
 356 

 357     public void testGetExtensionKeys() {
 358         Locale locale = Locale.forLanguageTag("und-a-xx-yy-b-zz-ww");
 359         Set<Character> result = locale.getExtensionKeys();
 360         assertEquals("result size", 2, result.size());
 361         assertTrue("'a','b'", result.contains('a') && result.contains('b'));
 362 
 363         // result is not mutable
 364         try {
 365             result.add('x');
 366             errln("expected exception on add to extension key set");
 367         }
 368         catch (UnsupportedOperationException e) {
 369             // ok
 370         }
 371 
 372         // returns empty set if no extensions
 373         locale = Locale.forLanguageTag("und");
 374         assertTrue("empty result", locale.getExtensionKeys().isEmpty());
 375     }
 376 

 377     public void testGetUnicodeLocaleAttributes() {
 378         Locale locale = Locale.forLanguageTag("en-US-u-abc-def");
 379         Set<String> attributes = locale.getUnicodeLocaleAttributes();
 380         assertEquals("number of attributes", 2, attributes.size());
 381         assertTrue("attribute abc", attributes.contains("abc"));
 382         assertTrue("attribute def", attributes.contains("def"));
 383 
 384         locale = Locale.forLanguageTag("en-US-u-ca-gregory");
 385         attributes = locale.getUnicodeLocaleAttributes();
 386         assertTrue("empty attributes", attributes.isEmpty());
 387     }
 388 

 389     public void testGetUnicodeLocaleType() {
 390         Locale locale = Locale.forLanguageTag("und-u-co-japanese-nu-thai");
 391         assertEquals("collation", "japanese", locale.getUnicodeLocaleType("co"));
 392         assertEquals("numbers", "thai", locale.getUnicodeLocaleType("nu"));
 393 
 394         // Unicode locale extension key is case insensitive
 395         assertEquals("key case", "japanese", locale.getUnicodeLocaleType("Co"));
 396 
 397         // if keyword is not present, returns null
 398         assertEquals("locale keyword not present", null, locale.getUnicodeLocaleType("xx"));
 399 
 400         // if no locale extension is set, returns null
 401         locale = Locale.forLanguageTag("und");
 402         assertEquals("locale extension not present", null, locale.getUnicodeLocaleType("co"));
 403 
 404         // typeless keyword
 405         locale = Locale.forLanguageTag("und-u-kn");
 406         assertEquals("typeless keyword", "", locale.getUnicodeLocaleType("kn"));
 407 
 408         // invalid keys throw exception
 409         new ExpectIAE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType("q"); }};
 410         new ExpectIAE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType("abcdefghi"); }};
 411 
 412         // null argument throws exception
 413         new ExpectNPE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType(null); }};
 414     }
 415 

 416     public void testGetUnicodeLocaleKeys() {
 417         Locale locale = Locale.forLanguageTag("und-u-co-japanese-nu-thai");
 418         Set<String> result = locale.getUnicodeLocaleKeys();
 419         assertEquals("two keys", 2, result.size());
 420         assertTrue("co and nu", result.contains("co") && result.contains("nu"));
 421 
 422         // result is not modifiable
 423         try {
 424             result.add("frobozz");
 425             errln("expected exception when add to locale key set");
 426         }
 427         catch (UnsupportedOperationException e) {
 428             // ok
 429         }
 430     }
 431 

 432     public void testPrivateUseExtension() {
 433         Locale locale = Locale.forLanguageTag("x-y-x-blork-");
 434         assertEquals("blork", "y-x-blork", locale.getExtension(Locale.PRIVATE_USE_EXTENSION));
 435 
 436         locale = Locale.forLanguageTag("und");
 437         assertEquals("no privateuse", null, locale.getExtension(Locale.PRIVATE_USE_EXTENSION));
 438     }
 439 

 440     public void testToLanguageTag() {
 441         // lots of normalization to test here
 442         // test locales created using the constructor
 443         String[][] tests = {
 444             // empty locale canonicalizes to 'und'
 445             { "", "", "", "und" },
 446             // variant alone is not a valid Locale, but has a valid language tag
 447             { "", "", "NewYork", "und-NewYork" },
 448             // standard valid locales
 449             { "", "Us", "", "und-US" },
 450             { "", "US", "NewYork", "und-US-NewYork" },
 451             { "EN", "", "", "en" },
 452             { "EN", "", "NewYork", "en-NewYork" },
 453             { "EN", "US", "", "en-US" },
 454             { "EN", "US", "NewYork", "en-US-NewYork" },
 455             // underscore in variant will be emitted as multiple variant subtags
 456             { "en", "US", "Newer_Yorker", "en-US-Newer-Yorker" },
 457             // invalid variant subtags are appended as private use
 458             { "en", "US", "new_yorker", "en-US-x-lvariant-new-yorker" },
 459             // the first invalid variant subtags and following variant subtags are appended as private use

 485         }
 486 
 487         // test locales created from forLanguageTag
 488         String[][] tests1 = {
 489             // case is normalized during the round trip
 490             { "EN-us", "en-US" },
 491             { "en-Latn-US", "en-Latn-US" },
 492             // reordering Unicode locale extensions
 493             { "de-u-co-phonebk-ca-gregory", "de-u-ca-gregory-co-phonebk" },
 494             // private use only language tag is preserved (no extra "und")
 495             { "x-elmer", "x-elmer" },
 496             { "x-lvariant-JP", "x-lvariant-JP" },
 497         };
 498         for (String[] test : tests1) {
 499             Locale locale = Locale.forLanguageTag(test[0]);
 500             assertEquals("case " + test[0], test[1], locale.toLanguageTag());
 501         }
 502 
 503     }
 504 

 505     public void testForLanguageTag() {
 506         // forLanguageTag implements the 'Language-Tag' production of
 507         // BCP47, so it handles private use and legacy language tags,
 508         // unlike locale builder.  Tags listed below (except for the
 509         // sample private use tags) come from 4646bis Feb 29, 2009.
 510 
 511         String[][] tests = {
 512             // private use tags only
 513             { "x-abc", "x-abc" },
 514             { "x-a-b-c", "x-a-b-c" },
 515             { "x-a-12345678", "x-a-12345678" },
 516 
 517             // legacy language tags with preferred mappings
 518             { "i-ami", "ami" },
 519             { "i-bnn", "bnn" },
 520             { "i-hak", "hak" },
 521             { "i-klingon", "tlh" },
 522             { "i-lux", "lb" }, // two-letter tag
 523             { "i-navajo", "nv" }, // two-letter tag
 524             { "i-pwn", "pwn" },

 572             { "locale subtag with no value",
 573               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-x-y-12345678-z",
 574               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-x-y-12345678-z" },
 575             { "locale key subtag invalid",
 576               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-123456789-def-x-y-12345678-z",
 577               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc" },
 578             // locale key subtag invalid in earlier position, all following subtags
 579             // dropped (and so the locale extension dropped as well)
 580             { "locale key subtag invalid in earlier position",
 581               "en-US-Newer-Yorker-a-bb-cc-dd-u-123456789-abc-bb-def-x-y-12345678-z",
 582               "en-US-Newer-Yorker-a-bb-cc-dd" },
 583         };
 584         for (int i = 0; i < tests.length; ++i) {
 585             String[] test = tests[i];
 586             String msg = "syntax error case " + i + " " + test[0];
 587             try {
 588                 Locale locale = Locale.forLanguageTag(test[1]);
 589                 assertEquals(msg, test[2], locale.toLanguageTag());
 590             }
 591             catch (IllegalArgumentException e) {
 592                 errln(msg + " caught exception: " + e);
 593             }
 594         }
 595 
 596         // duplicated extension are just ignored
 597         Locale locale = Locale.forLanguageTag("und-d-aa-00-bb-01-D-AA-10-cc-11-c-1234");
 598         assertEquals("extension", "aa-00-bb-01", locale.getExtension('d'));
 599         assertEquals("extension c", "1234", locale.getExtension('c'));
 600 
 601         locale = Locale.forLanguageTag("und-U-ca-gregory-u-ca-japanese");
 602         assertEquals("Unicode extension", "ca-gregory", locale.getExtension(Locale.UNICODE_LOCALE_EXTENSION));
 603 
 604         // redundant Unicode locale keys in an extension are ignored
 605         locale = Locale.forLanguageTag("und-u-aa-000-bb-001-bB-002-cc-003-c-1234");
 606         assertEquals("Unicode keywords", "aa-000-bb-001-cc-003", locale.getExtension(Locale.UNICODE_LOCALE_EXTENSION));
 607         assertEquals("Duplicated Unicode locake key followed by an extension", "1234", locale.getExtension('c'));
 608     }
 609 

 610     public void testGetDisplayScript() {
 611         Locale latnLocale = Locale.forLanguageTag("und-latn");
 612         Locale hansLocale = Locale.forLanguageTag("und-hans");
 613 
 614         Locale oldLocale = Locale.getDefault();
 615 
 616         Locale.setDefault(Locale.US);
 617         assertEquals("latn US", "Latin", latnLocale.getDisplayScript());
 618         assertEquals("hans US", "Simplified", hansLocale.getDisplayScript());
 619 
 620         Locale.setDefault(Locale.GERMANY);
 621         assertEquals("latn DE", "Lateinisch", latnLocale.getDisplayScript());
 622         assertEquals("hans DE", "Vereinfacht", hansLocale.getDisplayScript());
 623 
 624         Locale.setDefault(oldLocale);
 625     }
 626 

 627     public void testGetDisplayScriptWithLocale() {
 628         Locale latnLocale = Locale.forLanguageTag("und-latn");
 629         Locale hansLocale = Locale.forLanguageTag("und-hans");
 630 
 631         assertEquals("latn US", "Latin", latnLocale.getDisplayScript(Locale.US));
 632         assertEquals("hans US", "Simplified", hansLocale.getDisplayScript(Locale.US));
 633 
 634         assertEquals("latn DE", "Lateinisch", latnLocale.getDisplayScript(Locale.GERMANY));
 635         assertEquals("hans DE", "Vereinfacht", hansLocale.getDisplayScript(Locale.GERMANY));
 636     }
 637 

 638     public void testGetDisplayName() {
 639         final Locale[] testLocales = {
 640                 Locale.ROOT,
 641                 Locale.ENGLISH,
 642                 Locale.US,
 643                 Locale.of("", "US"),
 644                 Locale.of("no", "NO", "NY"),
 645                 Locale.of("", "", "NY"),
 646                 Locale.forLanguageTag("zh-Hans"),
 647                 Locale.forLanguageTag("zh-Hant"),
 648                 Locale.forLanguageTag("zh-Hans-CN"),
 649                 Locale.forLanguageTag("und-Hans"),
 650         };
 651 
 652         final String[] displayNameEnglish = {
 653                 "",
 654                 "English",
 655                 "English (United States)",
 656                 "United States",
 657                 "Norwegian (Norway,Nynorsk)",

 671                 "Nynorsk",
 672                 "\u4e2d\u6587 (\u7b80\u4f53)",
 673                 "\u4e2d\u6587 (\u7e41\u4f53)",
 674                 "\u4e2d\u6587 (\u7b80\u4f53,\u4e2d\u56fd)",
 675                 "\u7b80\u4f53",
 676         };
 677 
 678         for (int i = 0; i < testLocales.length; i++) {
 679             Locale loc = testLocales[i];
 680             assertEquals("English display name for " + loc.toLanguageTag(),
 681                     displayNameEnglish[i], loc.getDisplayName(Locale.ENGLISH));
 682             assertEquals("Simplified Chinese display name for " + loc.toLanguageTag(),
 683                     displayNameSimplifiedChinese[i], loc.getDisplayName(Locale.CHINA));
 684         }
 685     }
 686 
 687     ///
 688     /// Builder tests
 689     ///
 690 

 691     public void testBuilderSetLocale() {
 692         Builder builder = new Builder();
 693         Builder lenientBuilder = new Builder();
 694 
 695         String languageTag = "en-Latn-US-NewYork-a-bb-ccc-u-co-japanese-x-y-z";
 696         String target = "en-Latn-US-NewYork-a-bb-ccc-u-co-japanese-x-y-z";
 697 
 698         Locale locale = Locale.forLanguageTag(languageTag);
 699         Locale result = lenientBuilder
 700             .setLocale(locale)
 701             .build();
 702         assertEquals("long tag", target, result.toLanguageTag());
 703         assertEquals("long tag", locale, result);
 704 
 705         // null is illegal
 706         new BuilderNPE("locale") {
 707             public void call() { b.setLocale(null); }
 708         };
 709 
 710         // builder canonicalizes the three legacy locales:

 713         assertEquals("ja_JP_JP languagetag", "ja-JP-u-ca-japanese", locale.toLanguageTag());
 714         assertEquals("ja_JP_JP variant", "", locale.getVariant());
 715 
 716         locale = builder.setLocale(Locale.of("th", "TH", "TH")).build();
 717         assertEquals("th_TH_TH languagetag", "th-TH-u-nu-thai", locale.toLanguageTag());
 718         assertEquals("th_TH_TH variant", "", locale.getVariant());
 719 
 720         locale = builder.setLocale(Locale.of("no", "NO", "NY")).build();
 721         assertEquals("no_NO_NY languagetag", "nn-NO", locale.toLanguageTag());
 722         assertEquals("no_NO_NY language", "nn", locale.getLanguage());
 723         assertEquals("no_NO_NY variant", "", locale.getVariant());
 724 
 725         // non-canonical, non-legacy locales are invalid
 726         new BuilderILE("123_4567_89") {
 727             public void call() {
 728                 b.setLocale(Locale.of("123", "4567", "89"));
 729             }
 730         };
 731     }
 732 

 733     public void testBuilderSetLanguageTag() {
 734         String source = "eN-LaTn-Us-NewYork-A-Xx-B-Yy-X-1-2-3";
 735         String target = "en-Latn-US-NewYork-a-xx-b-yy-x-1-2-3";
 736         Builder builder = new Builder();
 737         String result = builder
 738             .setLanguageTag(source)
 739             .build()
 740             .toLanguageTag();
 741         assertEquals("language", target, result);
 742 
 743         // redundant extensions cause a failure
 744         new BuilderILE() { public void call() { b.setLanguageTag("und-a-xx-yy-b-ww-A-00-11-c-vv"); }};
 745 
 746         // redundant Unicode locale extension keys within an Unicode locale extension cause a failure
 747         new BuilderILE() { public void call() { b.setLanguageTag("und-u-nu-thai-NU-chinese-xx-1234"); }};
 748     }
 749 

 750     public void testBuilderSetLanguage() {
 751         // language is normalized to lower case
 752         String source = "eN";
 753         String target = "en";
 754         String defaulted = "";
 755         Builder builder = new Builder();
 756         String result = builder
 757             .setLanguage(source)
 758             .build()
 759             .getLanguage();
 760         assertEquals("en", target, result);
 761 
 762         // setting with empty resets
 763         result = builder
 764             .setLanguage(target)
 765             .setLanguage("")
 766             .build()
 767             .getLanguage();
 768         assertEquals("empty", defaulted, result);
 769 

 775                 .getLanguage();
 776         assertEquals("null", defaulted, result);
 777 
 778         // language codes must be 2-8 alpha
 779         // for forwards compatibility, 4-alpha and 5-8 alpha (registered)
 780         // languages are accepted syntax
 781         new BuilderILE("q", "abcdefghi", "13") { public void call() { b.setLanguage(arg); }};
 782 
 783         // language code validation is NOT performed, any 2-8-alpha passes
 784         assertNotNull("2alpha", builder.setLanguage("zz").build());
 785         assertNotNull("8alpha", builder.setLanguage("abcdefgh").build());
 786 
 787         // three-letter language codes are NOT canonicalized to two-letter
 788         result = builder
 789             .setLanguage("eng")
 790             .build()
 791             .getLanguage();
 792         assertEquals("eng", "eng", result);
 793     }
 794 

 795     public void testBuilderSetScript() {
 796         // script is normalized to title case
 797         String source = "lAtN";
 798         String target = "Latn";
 799         String defaulted = "";
 800         Builder builder = new Builder();
 801         String result = builder
 802             .setScript(source)
 803             .build()
 804             .getScript();
 805         assertEquals("script", target, result);
 806 
 807         // setting with empty resets
 808         result = builder
 809             .setScript(target)
 810             .setScript("")
 811             .build()
 812             .getScript();
 813         assertEquals("empty", defaulted, result);
 814 
 815         // settting with null also resets
 816         result = builder
 817                 .setScript(target)
 818                 .setScript(null)
 819                 .build()
 820                 .getScript();
 821         assertEquals("null", defaulted, result);
 822 
 823         // ill-formed script codes throw IAE
 824         // must be 4alpha
 825         new BuilderILE("abc", "abcde", "l3tn") { public void call() { b.setScript(arg); }};
 826 
 827         // script code validation is NOT performed, any 4-alpha passes
 828         assertEquals("4alpha", "Wxyz", builder.setScript("wxyz").build().getScript());
 829     }
 830 

 831     public void testBuilderSetRegion() {
 832         // region is normalized to upper case
 833         String source = "uS";
 834         String target = "US";
 835         String defaulted = "";
 836         Builder builder = new Builder();
 837         String result = builder
 838             .setRegion(source)
 839             .build()
 840             .getCountry();
 841         assertEquals("us", target, result);
 842 
 843         // setting with empty resets
 844         result = builder
 845             .setRegion(target)
 846             .setRegion("")
 847             .build()
 848             .getCountry();
 849         assertEquals("empty", defaulted, result);
 850 
 851         // setting with null also resets
 852         result = builder
 853                 .setRegion(target)
 854                 .setRegion(null)
 855                 .build()
 856                 .getCountry();
 857         assertEquals("null", defaulted, result);
 858 
 859         // ill-formed region codes throw IAE
 860         // 2 alpha or 3 numeric
 861         new BuilderILE("q", "abc", "12", "1234", "a3", "12a") { public void call() { b.setRegion(arg); }};
 862 
 863         // region code validation is NOT performed, any 2-alpha or 3-digit passes
 864         assertEquals("2alpha", "ZZ", builder.setRegion("ZZ").build().getCountry());
 865         assertEquals("3digit", "000", builder.setRegion("000").build().getCountry());
 866     }
 867 

 868     public void testBuilderSetVariant() {
 869         // Variant case is not normalized in lenient variant mode
 870         String source = "NewYork";
 871         String target = source;
 872         String defaulted = "";
 873         Builder builder = new Builder();
 874         String result = builder
 875             .setVariant(source)
 876             .build()
 877             .getVariant();
 878         assertEquals("NewYork", target, result);
 879 
 880         result = builder
 881             .setVariant("NeWeR_YoRkEr")
 882             .build()
 883             .toLanguageTag();
 884         assertEquals("newer yorker", "und-NeWeR-YoRkEr", result);
 885 
 886         // subtags of variant are NOT reordered
 887         result = builder

 900 
 901         // setting to null also resets
 902         result = builder
 903                 .setVariant(target)
 904                 .setVariant(null)
 905                 .build()
 906                 .getVariant();
 907         assertEquals("null", defaulted, result);
 908 
 909         // ill-formed variants throw IAE
 910         // digit followed by 3-7 characters, or alpha followed by 4-8 characters.
 911         new BuilderILE("abcd", "abcdefghi", "1ab", "1abcdefgh") { public void call() { b.setVariant(arg); }};
 912 
 913         // 4 characters is ok as long as the first is a digit
 914         assertEquals("digit+3alpha", "1abc", builder.setVariant("1abc").build().getVariant());
 915 
 916         // all subfields must conform
 917         new BuilderILE("abcde-fg") { public void call() { b.setVariant(arg); }};
 918     }
 919 

 920     public void testBuilderSetExtension() {
 921         // upper case characters are normalized to lower case
 922         final char sourceKey = 'a';
 923         final String sourceValue = "aB-aBcdefgh-12-12345678";
 924         String target = "ab-abcdefgh-12-12345678";
 925         Builder builder = new Builder();
 926         String result = builder
 927             .setExtension(sourceKey, sourceValue)
 928             .build()
 929             .getExtension(sourceKey);
 930         assertEquals("extension", target, result);
 931 
 932         // setting with empty resets
 933         result = builder
 934             .setExtension(sourceKey, sourceValue)
 935             .setExtension(sourceKey, "")
 936             .build()
 937             .getExtension(sourceKey);
 938         assertEquals("empty", null, result);
 939 

 990             .toLanguageTag();
 991         assertEquals("reorder", "und-u-123-456-xx-789-yy-456-zz-123", result);
 992 
 993         // multiple keyword types
 994         result = builder
 995             .clear()
 996             .setExtension('u', "nu-thai-foobar")
 997             .build()
 998             .getUnicodeLocaleType("nu");
 999         assertEquals("multiple types", "thai-foobar", result);
1000 
1001         // redundant locale extensions are ignored
1002         result = builder
1003             .clear()
1004             .setExtension('u', "nu-thai-NU-chinese-xx-1234")
1005             .build()
1006             .toLanguageTag();
1007         assertEquals("duplicate keys", "und-u-nu-thai-xx-1234", result);
1008     }
1009 

1010     public void testBuilderAddUnicodeLocaleAttribute() {
1011         Builder builder = new Builder();
1012         Locale locale = builder
1013             .addUnicodeLocaleAttribute("def")
1014             .addUnicodeLocaleAttribute("abc")
1015             .build();
1016 
1017         Set<String> uattrs = locale.getUnicodeLocaleAttributes();
1018         assertEquals("number of attributes", 2, uattrs.size());
1019         assertTrue("attribute abc", uattrs.contains("abc"));
1020         assertTrue("attribute def", uattrs.contains("def"));
1021 
1022         // remove attribute
1023         locale = builder.removeUnicodeLocaleAttribute("xxx")
1024             .build();
1025 
1026         assertEquals("remove bogus", 2, uattrs.size());
1027 
1028         // add duplicate
1029         locale = builder.addUnicodeLocaleAttribute("abc")
1030             .build();
1031         assertEquals("add duplicate", 2, uattrs.size());
1032 
1033         // null attribute throws NPE
1034         new BuilderNPE("null attribute") { public void call() { b.addUnicodeLocaleAttribute(null); }};
1035         new BuilderNPE("null attribute removal") { public void call() { b.removeUnicodeLocaleAttribute(null); }};
1036 
1037         // illformed attribute throws IllformedLocaleException
1038         new BuilderILE("invalid attribute") { public void call() { b.addUnicodeLocaleAttribute("ca"); }};
1039     }
1040 

1041     public void testBuildersetUnicodeLocaleKeyword() {
1042         // Note: most behavior is tested in testBuilderSetExtension
1043         Builder builder = new Builder();
1044         Locale locale = builder
1045             .setUnicodeLocaleKeyword("co", "japanese")
1046             .setUnicodeLocaleKeyword("nu", "thai")
1047             .build();
1048         assertEquals("co", "japanese", locale.getUnicodeLocaleType("co"));
1049         assertEquals("nu", "thai", locale.getUnicodeLocaleType("nu"));
1050         assertEquals("keys", 2, locale.getUnicodeLocaleKeys().size());
1051 
1052         // can clear a keyword by setting to null, others remain
1053         String result = builder
1054             .setUnicodeLocaleKeyword("co", null)
1055             .build()
1056             .toLanguageTag();
1057         assertEquals("empty co", "und-u-nu-thai", result);
1058 
1059         // locale keyword extension goes when all keywords are gone
1060         result = builder

1064         assertEquals("empty nu", "und", result);
1065 
1066         // locale keywords are ordered independent of order of addition
1067         result = builder
1068             .setUnicodeLocaleKeyword("zz", "012")
1069             .setUnicodeLocaleKeyword("aa", "345")
1070             .build()
1071             .toLanguageTag();
1072         assertEquals("reordered", "und-u-aa-345-zz-012", result);
1073 
1074         // null keyword throws NPE
1075         new BuilderNPE("keyword") { public void call() { b.setUnicodeLocaleKeyword(null, "thai"); }};
1076 
1077         // well-formed keywords are two alphanum
1078         new BuilderILE("a", "abc") { public void call() { b.setUnicodeLocaleKeyword(arg, "value"); }};
1079 
1080         // well-formed values are 3-8 alphanum
1081         new BuilderILE("ab", "abcdefghi") { public void call() { b.setUnicodeLocaleKeyword("ab", arg); }};
1082     }
1083 

1084     public void testBuilderPrivateUseExtension() {
1085         // normalizes hyphens to underscore, case to lower
1086         String source = "c-B-a";
1087         String target = "c-b-a";
1088         Builder builder = new Builder();
1089         String result = builder
1090             .setExtension(Locale.PRIVATE_USE_EXTENSION, source)
1091             .build()
1092             .getExtension(Locale.PRIVATE_USE_EXTENSION);
1093         assertEquals("abc", target, result);
1094 
1095         // multiple hyphens are ill-formed
1096         new BuilderILE("a--b") { public void call() { b.setExtension(Locale.PRIVATE_USE_EXTENSION, arg); }};
1097     }
1098 

1099     public void testBuilderClear() {
1100         String monster = "en-latn-US-NewYork-a-bb-cc-u-co-japanese-x-z-y-x-x";
1101         Builder builder = new Builder();
1102         Locale locale = Locale.forLanguageTag(monster);
1103         String result = builder
1104             .setLocale(locale)
1105             .clear()
1106             .build()
1107             .toLanguageTag();
1108         assertEquals("clear", "und", result);
1109     }
1110 

1111     public void testBuilderRemoveUnicodeAttribute() {
1112         // tested in testBuilderAddUnicodeAttribute
1113     }
1114 

1115     public void testBuilderBuild() {
1116         // tested in other test methods
1117     }
1118 

1119     public void testSerialize() {
1120         final Locale[] testLocales = {
1121             Locale.ROOT,
1122             Locale.ENGLISH,
1123             Locale.US,
1124             Locale.of("en", "US", "Win"),
1125             Locale.of("en", "US", "Win_XP"),
1126             Locale.JAPAN,
1127             Locale.of("ja", "JP", "JP"),
1128             Locale.of("th", "TH"),
1129             Locale.of("th", "TH", "TH"),
1130             Locale.of("no", "NO"),
1131             Locale.of("nb", "NO"),
1132             Locale.of("nn", "NO"),
1133             Locale.of("no", "NO", "NY"),
1134             Locale.of("nn", "NO", "NY"),
1135             Locale.of("he", "IL"),
1136             Locale.of("he", "IL", "var"),
1137             Locale.of("Language", "Country", "Variant"),
1138             Locale.of("", "US"),

1144             Locale.forLanguageTag("und-Hant"),
1145             Locale.forLanguageTag("und-a-123-456"),
1146             Locale.forLanguageTag("en-x-java"),
1147             Locale.forLanguageTag("th-TH-u-ca-buddist-nu-thai-x-lvariant-TH"),
1148         };
1149 
1150         for (Locale locale : testLocales) {
1151             try {
1152                 // write
1153                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1154                 ObjectOutputStream oos = new ObjectOutputStream(bos);
1155                 oos.writeObject(locale);
1156 
1157                 // read
1158                 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
1159                 ObjectInputStream ois = new ObjectInputStream(bis);
1160                 Object o = ois.readObject();
1161 
1162                 assertEquals("roundtrip " + locale, locale, o);
1163             } catch (Exception e) {
1164                 errln(locale + " encountered exception:" + e.getLocalizedMessage());
1165             }
1166         }
1167     }
1168 

1169     public void testDeserialize6() {
1170         final String TESTFILEPREFIX = "java6locale_";
1171 
1172         File dataDir = null;
1173         String dataDirName = System.getProperty("serialized.data.dir");
1174         if (dataDirName == null) {
1175             URL resdirUrl = getClass().getClassLoader().getResource("serialized");
1176             if (resdirUrl != null) {
1177                 try {
1178                     dataDir = new File(resdirUrl.toURI());
1179                 } catch (URISyntaxException urie) {
1180                 }
1181             }
1182         } else {
1183             dataDir = new File(dataDirName);
1184         }
1185 
1186         if (dataDir == null) {
1187             errln("'dataDir' is null. serialized.data.dir Property value is "+dataDirName);
1188             return;
1189         } else if (!dataDir.isDirectory()) {
1190             errln("'dataDir' is not a directory. dataDir: "+dataDir.toString());
1191             return;
1192         }
1193 
1194         File[] files = dataDir.listFiles();
1195         for (File testfile : files) {
1196             if (testfile.isDirectory()) {
1197                 continue;
1198             }
1199             String name = testfile.getName();
1200             if (!name.startsWith(TESTFILEPREFIX)) {
1201                 continue;
1202             }
1203             Locale locale;
1204             String locStr = name.substring(TESTFILEPREFIX.length());
1205             if (locStr.equals("ROOT")) {
1206                 locale = Locale.ROOT;
1207             } else {
1208                 String[] fields = locStr.split("_", 3);
1209                 String lang = fields[0];
1210                 String country = (fields.length >= 2) ? fields[1] : "";
1211                 String variant = (fields.length == 3) ? fields[2] : "";
1212                 locale = Locale.of(lang, country, variant);
1213             }
1214 
1215             // deserialize
1216             try (FileInputStream fis = new FileInputStream(testfile);
1217                  ObjectInputStream ois = new ObjectInputStream(fis))
1218             {
1219                 Object o = ois.readObject();
1220                 assertEquals("Deserialize Java 6 Locale " + locale, o, locale);
1221             } catch (Exception e) {
1222                 errln("Exception while reading " + testfile.getAbsolutePath() + " - " + e.getMessage());
1223             }
1224         }
1225     }
1226 

1227     public void testBug7002320() {
1228         // forLanguageTag() and Builder.setLanguageTag(String)
1229         // should add a location extension for following two cases.
1230         //
1231         // 1. language/country are "ja"/"JP" and the resolved variant (x-lvariant-*)
1232         //    is exactly "JP" and no BCP 47 extensions are available, then add
1233         //    a Unicode locale extension "ca-japanese".
1234         // 2. language/country are "th"/"TH" and the resolved variant is exactly
1235         //    "TH" and no BCP 47 extensions are available, then add a Unicode locale
1236         //    extension "nu-thai".
1237         //
1238         String[][] testdata = {
1239             {"ja-JP-x-lvariant-JP", "ja-JP-u-ca-japanese-x-lvariant-JP"},   // special case 1
1240             {"ja-JP-x-lvariant-JP-XXX"},
1241             {"ja-JP-u-ca-japanese-x-lvariant-JP"},
1242             {"ja-JP-u-ca-gregory-x-lvariant-JP"},
1243             {"ja-JP-u-cu-jpy-x-lvariant-JP"},
1244             {"ja-x-lvariant-JP"},
1245             {"th-TH-x-lvariant-TH", "th-TH-u-nu-thai-x-lvariant-TH"},   // special case 2
1246             {"th-TH-u-nu-thai-x-lvariant-TH"},

1250         Builder bldr = new Builder();
1251 
1252         for (String[] data : testdata) {
1253             String in = data[0];
1254             String expected = (data.length == 1) ? data[0] : data[1];
1255 
1256             // forLanguageTag
1257             Locale loc = Locale.forLanguageTag(in);
1258             String out = loc.toLanguageTag();
1259             assertEquals("Language tag roundtrip by forLanguageTag with input: " + in, expected, out);
1260 
1261             // setLanguageTag
1262             bldr.clear();
1263             bldr.setLanguageTag(in);
1264             loc = bldr.build();
1265             out = loc.toLanguageTag();
1266             assertEquals("Language tag roundtrip by Builder.setLanguageTag with input: " + in, expected, out);
1267         }
1268     }
1269 

1270     public void testBug7023613() {
1271         String[][] testdata = {
1272             {"en-Latn", "en__#Latn"},
1273             {"en-u-ca-japanese", "en__#u-ca-japanese"},
1274         };
1275 
1276         for (String[] data : testdata) {
1277             String in = data[0];
1278             String expected = (data.length == 1) ? data[0] : data[1];
1279 
1280             Locale loc = Locale.forLanguageTag(in);
1281             String out = loc.toString();
1282             assertEquals("Empty country field with non-empty script/extension with input: " + in, expected, out);
1283         }
1284     }
1285 
1286     /*
1287      * 7033504: (lc) incompatible behavior change for ja_JP_JP and th_TH_TH locales
1288      */

1289     public void testBug7033504() {
1290         checkCalendar(Locale.of("ja", "JP", "jp"), "java.util.GregorianCalendar");
1291         checkCalendar(Locale.of("ja", "jp", "jp"), "java.util.GregorianCalendar");
1292         checkCalendar(Locale.of("ja", "JP", "JP"), "java.util.JapaneseImperialCalendar");
1293         checkCalendar(Locale.of("ja", "jp", "JP"), "java.util.JapaneseImperialCalendar");
1294         checkCalendar(Locale.forLanguageTag("en-u-ca-japanese"),
1295                       "java.util.JapaneseImperialCalendar");
1296 
1297         checkDigit(Locale.of("th", "TH", "th"), '0');
1298         checkDigit(Locale.of("th", "th", "th"), '0');
1299         checkDigit(Locale.of("th", "TH", "TH"), '\u0e50');
1300         checkDigit(Locale.of("th", "TH", "TH"), '\u0e50');
1301         checkDigit(Locale.forLanguageTag("en-u-nu-thai"), '\u0e50');
1302     }
1303 
1304     private void checkCalendar(Locale loc, String expected) {
1305         Calendar cal = Calendar.getInstance(loc);
1306         assertEquals("Wrong calendar", expected, cal.getClass().getName());
1307     }
1308 
1309     private void checkDigit(Locale loc, Character expected) {
1310         DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(loc);
1311         Character zero = dfs.getZeroDigit();
1312         assertEquals("Wrong digit zero char", expected, zero);
1313     }
1314 
1315     ///
1316     /// utility asserts
1317     ///
1318 
1319     private void assertTrue(String msg, boolean v) {
1320         if (!v) {
1321             errln(msg + ": expected true");
1322         }
1323     }
1324 
1325     private void assertFalse(String msg, boolean v) {
1326         if (v) {
1327             errln(msg + ": expected false");
1328         }
1329     }
1330 
1331     private void assertEquals(String msg, Object e, Object v) {
1332         if (e == null ? v != null : !e.equals(v)) {
1333             if (e != null) {
1334                 e = "'" + e + "'";
1335             }
1336             if (v != null) {
1337                 v = "'" + v + "'";
1338             }
1339             errln(msg + ": expected " + e + " but got " + v);
1340         }
1341     }
1342 
1343     private void assertNotEquals(String msg, Object e, Object v) {
1344         if (e == null ? v == null : e.equals(v)) {
1345             if (e != null) {
1346                 e = "'" + e + "'";
1347             }
1348             errln(msg + ": expected not equal " + e);
1349         }
1350     }
1351 
1352     private void assertNull(String msg, Object o) {
1353         if (o != null) {
1354             errln(msg + ": expected null but got '" + o + "'");
1355         }
1356     }
1357 
1358     private void assertNotNull(String msg, Object o) {
1359         if (o == null) {
1360             errln(msg + ": expected non null");
1361         }
1362     }
1363 
1364     // not currently used, might get rid of exceptions from the API
1365     private abstract class ExceptionTest {
1366         private final Class<? extends Exception> exceptionClass;
1367 
1368         ExceptionTest(Class<? extends Exception> exceptionClass) {
1369             this.exceptionClass = exceptionClass;
1370         }
1371 
1372         public void run() {
1373             String failMsg = null;
1374             try {
1375                 call();
1376                 failMsg = "expected " + exceptionClass.getName() + "  but no exception thrown.";
1377             }
1378             catch (Exception e) {
1379                 if (!exceptionClass.isAssignableFrom(e.getClass())) {
1380                     failMsg = "expected " + exceptionClass.getName() + " but caught " + e;
1381                 }
1382             }
1383             if (failMsg != null) {
1384                 String msg = message();
1385                 msg = msg == null ? "" : msg + " ";
1386                 errln(msg + failMsg);
1387             }
1388         }
1389 
1390         public String message() {
1391             return null;
1392         }
1393 
1394         public abstract void call();
1395     }
1396 
1397     private abstract class ExpectNPE extends ExceptionTest {
1398         ExpectNPE() {
1399             super(NullPointerException.class);
1400             run();
1401         }
1402     }
1403 
1404     private abstract class BuilderNPE extends ExceptionTest {
1405         protected final String msg;
1406         protected final Builder b = new Builder();

   1 /*
   2  * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */

  24 import java.io.BufferedReader;
  25 import java.io.ByteArrayInputStream;
  26 import java.io.ByteArrayOutputStream;
  27 import java.io.File;
  28 import java.io.FileInputStream;
  29 import java.io.InputStreamReader;
  30 import java.io.ObjectInputStream;
  31 import java.io.ObjectOutputStream;
  32 import java.net.URISyntaxException;
  33 import java.net.URL;
  34 import java.text.DecimalFormatSymbols;
  35 import java.util.ArrayList;
  36 import java.util.Arrays;
  37 import java.util.Calendar;
  38 import java.util.IllformedLocaleException;
  39 import java.util.List;
  40 import java.util.Locale;
  41 import java.util.Locale.Builder;
  42 import java.util.Set;
  43 
  44 import org.junit.jupiter.api.Test;
  45 
  46 import static org.junit.jupiter.api.Assertions.fail;
  47 
  48 /**
  49  * @test
  50  * @bug 6875847 6992272 7002320 7015500 7023613 7032820 7033504 7004603
  51  *    7044019 8008577 8176853 8255086 8263202 8287868
  52  * @summary test API changes to Locale

  53  * @modules jdk.localedata
  54  * @compile LocaleEnhanceTest.java
  55  * @run junit/othervm -Djava.locale.providers=JRE,SPI -esa LocaleEnhanceTest
  56  */
  57 public class LocaleEnhanceTest {







  58 
  59     public LocaleEnhanceTest() {
  60     }
  61 
  62     ///
  63     /// Generic sanity tests
  64     ///
  65 
  66     /** A canonical language code. */
  67     private static final String l = "en";
  68 
  69     /** A canonical script code.. */
  70     private static final String s = "Latn";
  71 
  72     /** A canonical region code. */
  73     private static final String c = "US";
  74 
  75     /** A canonical variant code. */
  76     private static final String v = "NewYork";
  77 
  78     /**
  79      * Ensure that Builder builds locales that have the expected
  80      * tag and java6 ID.  Note the odd cases for the ID.
  81      */
  82     @Test
  83     public void testCreateLocaleCanonicalValid() {
  84         String[] valids = {
  85             "en-Latn-US-NewYork", "en_US_NewYork_#Latn",
  86             "en-Latn-US", "en_US_#Latn",
  87             "en-Latn-NewYork", "en__NewYork_#Latn", // double underscore
  88             "en-Latn", "en__#Latn", // double underscore
  89             "en-US-NewYork", "en_US_NewYork",
  90             "en-US", "en_US",
  91             "en-NewYork", "en__NewYork", // double underscore
  92             "en", "en",
  93             "und-Latn-US-NewYork", "_US_NewYork_#Latn",
  94             "und-Latn-US", "_US_#Latn",
  95             "und-Latn-NewYork", "", // variant only not supported
  96             "und-Latn", "",
  97             "und-US-NewYork", "_US_NewYork",
  98             "und-US", "_US",
  99             "und-NewYork", "", // variant only not supported
 100             "und", ""
 101         };
 102 

 111             String idc = (i & 4) == 0 ? c : "";
 112             String idv = (i & 2) == 0 ? v : "";
 113 
 114             String msg = String.valueOf(i/2) + ": '" + tag + "' ";
 115 
 116             try {
 117                 Locale l = builder
 118                     .setLanguage(idl)
 119                     .setScript(ids)
 120                     .setRegion(idc)
 121                     .setVariant(idv)
 122                     .build();
 123                 assertEquals(msg + "language", idl, l.getLanguage());
 124                 assertEquals(msg + "script", ids, l.getScript());
 125                 assertEquals(msg + "country", idc, l.getCountry());
 126                 assertEquals(msg + "variant", idv, l.getVariant());
 127                 assertEquals(msg + "tag", tag, l.toLanguageTag());
 128                 assertEquals(msg + "id", id, l.toString());
 129             }
 130             catch (IllegalArgumentException e) {
 131                 fail(msg + e.getMessage());
 132             }
 133         }
 134     }
 135 
 136     /**
 137      * Test that locale construction works with 'multiple variants'.
 138      * <p>
 139      * The string "Newer__Yorker" is treated as three subtags,
 140      * "Newer", "", and "Yorker", and concatenated into one
 141      * subtag by omitting empty subtags and joining the remainer
 142      * with underscores.  So the resulting variant tag is "Newer_Yorker".
 143      * Note that 'New' and 'York' are invalid BCP47 variant subtags
 144      * because they are too short.
 145      */
 146     @Test
 147     public void testCreateLocaleMultipleVariants() {
 148 
 149         String[] valids = {
 150             "en-Latn-US-Newer-Yorker",  "en_US_Newer_Yorker_#Latn",
 151             "en-Latn-Newer-Yorker",     "en__Newer_Yorker_#Latn",
 152             "en-US-Newer-Yorker",       "en_US_Newer_Yorker",
 153             "en-Newer-Yorker",          "en__Newer_Yorker",
 154             "und-Latn-US-Newer-Yorker", "_US_Newer_Yorker_#Latn",
 155             "und-Latn-Newer-Yorker",    "",
 156             "und-US-Newer-Yorker",      "_US_Newer_Yorker",
 157             "und-Newer-Yorker",         "",
 158         };
 159 
 160         Builder builder = new Builder(); // lenient variant
 161 
 162         final String idv = "Newer_Yorker";
 163         for (int i = 0; i < valids.length; i += 2) {
 164             String tag = valids[i];
 165             String id = valids[i+1];
 166 

 169             String idc = (i & 2) == 0 ? c : "";
 170 
 171             String msg = String.valueOf(i/2) + ": " + tag + " ";
 172             try {
 173                 Locale l = builder
 174                     .setLanguage(idl)
 175                     .setScript(ids)
 176                     .setRegion(idc)
 177                     .setVariant(idv)
 178                     .build();
 179 
 180                 assertEquals(msg + " language", idl, l.getLanguage());
 181                 assertEquals(msg + " script", ids, l.getScript());
 182                 assertEquals(msg + " country", idc, l.getCountry());
 183                 assertEquals(msg + " variant", idv, l.getVariant());
 184 
 185                 assertEquals(msg + "tag", tag, l.toLanguageTag());
 186                 assertEquals(msg + "id", id, l.toString());
 187             }
 188             catch (IllegalArgumentException e) {
 189                 fail(msg + e.getMessage());
 190             }
 191         }
 192     }
 193 
 194     /**
 195      * Ensure that all these invalid formats are not recognized by
 196      * forLanguageTag.
 197      */
 198     @Test
 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'

 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     @Test
 243     public void testCurrentLocales() {
 244         Locale[] locales = java.text.DateFormat.getAvailableLocales();
 245         Builder builder = new Builder();
 246 
 247         for (Locale target : locales) {
 248             String tag = target.toLanguageTag();
 249 
 250             // the tag recreates the original locale,
 251             // except no_NO_NY
 252             Locale tagResult = Locale.forLanguageTag(tag);
 253             if (!target.getVariant().equals("NY")) {
 254                 assertEquals("tagResult", target, tagResult);
 255             }
 256 
 257             // the builder also recreates the original locale,
 258             // except ja_JP_JP, th_TH_TH and no_NO_NY
 259             Locale builderResult = builder.setLocale(target).build();
 260             if (target.getVariant().length() != 2) {
 261                 assertEquals("builderResult", target, builderResult);
 262             }
 263         }
 264     }
 265 
 266     /**
 267      * Ensure that all icu locale ids parse.
 268      */
 269     @Test
 270     public void testIcuLocales() throws Exception {
 271         BufferedReader br = new BufferedReader(
 272             new InputStreamReader(
 273                 LocaleEnhanceTest.class.getResourceAsStream("icuLocales.txt"),
 274                 "UTF-8"));
 275         String id = null;
 276         while (null != (id = br.readLine())) {
 277             Locale result = Locale.forLanguageTag(id);
 278             assertEquals("ulocale", id, result.toLanguageTag());
 279         }
 280     }
 281 
 282     ///
 283     /// Compatibility tests
 284     ///
 285 
 286     @Test
 287     public void testConstructor() {
 288         // all the old weirdness still holds, no new weirdness
 289         String[][] tests = {
 290             // language to lower case, region to upper, variant unchanged
 291             // short
 292             { "X", "y", "z", "x", "Y" },
 293             // long
 294             { "xXxXxXxXxXxX", "yYyYyYyYyYyYyYyY", "zZzZzZzZzZzZzZzZ",
 295               "xxxxxxxxxxxx", "YYYYYYYYYYYYYYYY" },
 296             // mapped language ids
 297             { "he", "IL", "", "he" },
 298             { "iw", "IL", "", "he" },
 299             { "yi", "DE", "", "yi" },
 300             { "ji", "DE", "", "yi" },
 301             { "id", "ID", "", "id" },
 302             { "in", "ID", "", "id" },
 303             // special variants
 304             { "ja", "JP", "JP" },
 305             { "th", "TH", "TH" },
 306             { "no", "NO", "NY" },
 307             { "no", "NO", "NY" },
 308             // no canonicalization of 3-letter language codes
 309             { "eng", "US", "" }
 310         };
 311         for (int i = 0; i < tests.length; ++ i) {
 312             String[] test = tests[i];
 313             String id = String.valueOf(i);
 314             Locale locale = Locale.of(test[0], test[1], test[2]);
 315             assertEquals(id + " lang", test.length > 3 ? test[3] : test[0], locale.getLanguage());
 316             assertEquals(id + " region", test.length > 4 ? test[4] : test[1], locale.getCountry());
 317             assertEquals(id + " variant", test.length > 5 ? test[5] : test[2], locale.getVariant());
 318         }
 319     }
 320 
 321     ///
 322     /// Locale API tests.
 323     ///
 324 
 325     @Test
 326     public void testGetScript() {
 327         // forLanguageTag normalizes case
 328         Locale locale = Locale.forLanguageTag("und-latn");
 329         assertEquals("forLanguageTag", "Latn", locale.getScript());
 330 
 331         // Builder normalizes case
 332         locale = new Builder().setScript("LATN").build();
 333         assertEquals("builder", "Latn", locale.getScript());
 334 
 335         // empty string is returned, not null, if there is no script
 336         locale = Locale.forLanguageTag("und");
 337         assertEquals("script is empty string", "", locale.getScript());
 338     }
 339 
 340     @Test
 341     public void testGetExtension() {
 342         // forLanguageTag does NOT normalize to hyphen
 343         Locale locale = Locale.forLanguageTag("und-a-some_ex-tension");
 344         assertEquals("some_ex-tension", null, locale.getExtension('a'));
 345 
 346         // regular extension
 347         locale = new Builder().setExtension('a', "some-ex-tension").build();
 348         assertEquals("builder", "some-ex-tension", locale.getExtension('a'));
 349 
 350         // returns null if extension is not present
 351         assertEquals("empty b", null, locale.getExtension('b'));
 352 
 353         // throws exception if extension tag is illegal
 354         new ExpectIAE() { public void call() { Locale.forLanguageTag("").getExtension('\uD800'); }};
 355 
 356         // 'x' is not an extension, it's a private use tag, but it's accessed through this API
 357         locale = Locale.forLanguageTag("x-y-z-blork");
 358         assertEquals("x", "y-z-blork", locale.getExtension('x'));
 359     }
 360 
 361     @Test
 362     public void testGetExtensionKeys() {
 363         Locale locale = Locale.forLanguageTag("und-a-xx-yy-b-zz-ww");
 364         Set<Character> result = locale.getExtensionKeys();
 365         assertEquals("result size", 2, result.size());
 366         assertTrue("'a','b'", result.contains('a') && result.contains('b'));
 367 
 368         // result is not mutable
 369         try {
 370             result.add('x');
 371             fail("expected exception on add to extension key set");
 372         }
 373         catch (UnsupportedOperationException e) {
 374             // ok
 375         }
 376 
 377         // returns empty set if no extensions
 378         locale = Locale.forLanguageTag("und");
 379         assertTrue("empty result", locale.getExtensionKeys().isEmpty());
 380     }
 381 
 382     @Test
 383     public void testGetUnicodeLocaleAttributes() {
 384         Locale locale = Locale.forLanguageTag("en-US-u-abc-def");
 385         Set<String> attributes = locale.getUnicodeLocaleAttributes();
 386         assertEquals("number of attributes", 2, attributes.size());
 387         assertTrue("attribute abc", attributes.contains("abc"));
 388         assertTrue("attribute def", attributes.contains("def"));
 389 
 390         locale = Locale.forLanguageTag("en-US-u-ca-gregory");
 391         attributes = locale.getUnicodeLocaleAttributes();
 392         assertTrue("empty attributes", attributes.isEmpty());
 393     }
 394 
 395     @Test
 396     public void testGetUnicodeLocaleType() {
 397         Locale locale = Locale.forLanguageTag("und-u-co-japanese-nu-thai");
 398         assertEquals("collation", "japanese", locale.getUnicodeLocaleType("co"));
 399         assertEquals("numbers", "thai", locale.getUnicodeLocaleType("nu"));
 400 
 401         // Unicode locale extension key is case insensitive
 402         assertEquals("key case", "japanese", locale.getUnicodeLocaleType("Co"));
 403 
 404         // if keyword is not present, returns null
 405         assertEquals("locale keyword not present", null, locale.getUnicodeLocaleType("xx"));
 406 
 407         // if no locale extension is set, returns null
 408         locale = Locale.forLanguageTag("und");
 409         assertEquals("locale extension not present", null, locale.getUnicodeLocaleType("co"));
 410 
 411         // typeless keyword
 412         locale = Locale.forLanguageTag("und-u-kn");
 413         assertEquals("typeless keyword", "", locale.getUnicodeLocaleType("kn"));
 414 
 415         // invalid keys throw exception
 416         new ExpectIAE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType("q"); }};
 417         new ExpectIAE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType("abcdefghi"); }};
 418 
 419         // null argument throws exception
 420         new ExpectNPE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType(null); }};
 421     }
 422 
 423     @Test
 424     public void testGetUnicodeLocaleKeys() {
 425         Locale locale = Locale.forLanguageTag("und-u-co-japanese-nu-thai");
 426         Set<String> result = locale.getUnicodeLocaleKeys();
 427         assertEquals("two keys", 2, result.size());
 428         assertTrue("co and nu", result.contains("co") && result.contains("nu"));
 429 
 430         // result is not modifiable
 431         try {
 432             result.add("frobozz");
 433             fail("expected exception when add to locale key set");
 434         }
 435         catch (UnsupportedOperationException e) {
 436             // ok
 437         }
 438     }
 439 
 440     @Test
 441     public void testPrivateUseExtension() {
 442         Locale locale = Locale.forLanguageTag("x-y-x-blork-");
 443         assertEquals("blork", "y-x-blork", locale.getExtension(Locale.PRIVATE_USE_EXTENSION));
 444 
 445         locale = Locale.forLanguageTag("und");
 446         assertEquals("no privateuse", null, locale.getExtension(Locale.PRIVATE_USE_EXTENSION));
 447     }
 448 
 449     @Test
 450     public void testToLanguageTag() {
 451         // lots of normalization to test here
 452         // test locales created using the constructor
 453         String[][] tests = {
 454             // empty locale canonicalizes to 'und'
 455             { "", "", "", "und" },
 456             // variant alone is not a valid Locale, but has a valid language tag
 457             { "", "", "NewYork", "und-NewYork" },
 458             // standard valid locales
 459             { "", "Us", "", "und-US" },
 460             { "", "US", "NewYork", "und-US-NewYork" },
 461             { "EN", "", "", "en" },
 462             { "EN", "", "NewYork", "en-NewYork" },
 463             { "EN", "US", "", "en-US" },
 464             { "EN", "US", "NewYork", "en-US-NewYork" },
 465             // underscore in variant will be emitted as multiple variant subtags
 466             { "en", "US", "Newer_Yorker", "en-US-Newer-Yorker" },
 467             // invalid variant subtags are appended as private use
 468             { "en", "US", "new_yorker", "en-US-x-lvariant-new-yorker" },
 469             // the first invalid variant subtags and following variant subtags are appended as private use

 495         }
 496 
 497         // test locales created from forLanguageTag
 498         String[][] tests1 = {
 499             // case is normalized during the round trip
 500             { "EN-us", "en-US" },
 501             { "en-Latn-US", "en-Latn-US" },
 502             // reordering Unicode locale extensions
 503             { "de-u-co-phonebk-ca-gregory", "de-u-ca-gregory-co-phonebk" },
 504             // private use only language tag is preserved (no extra "und")
 505             { "x-elmer", "x-elmer" },
 506             { "x-lvariant-JP", "x-lvariant-JP" },
 507         };
 508         for (String[] test : tests1) {
 509             Locale locale = Locale.forLanguageTag(test[0]);
 510             assertEquals("case " + test[0], test[1], locale.toLanguageTag());
 511         }
 512 
 513     }
 514 
 515     @Test
 516     public void testForLanguageTag() {
 517         // forLanguageTag implements the 'Language-Tag' production of
 518         // BCP47, so it handles private use and legacy language tags,
 519         // unlike locale builder.  Tags listed below (except for the
 520         // sample private use tags) come from 4646bis Feb 29, 2009.
 521 
 522         String[][] tests = {
 523             // private use tags only
 524             { "x-abc", "x-abc" },
 525             { "x-a-b-c", "x-a-b-c" },
 526             { "x-a-12345678", "x-a-12345678" },
 527 
 528             // legacy language tags with preferred mappings
 529             { "i-ami", "ami" },
 530             { "i-bnn", "bnn" },
 531             { "i-hak", "hak" },
 532             { "i-klingon", "tlh" },
 533             { "i-lux", "lb" }, // two-letter tag
 534             { "i-navajo", "nv" }, // two-letter tag
 535             { "i-pwn", "pwn" },

 583             { "locale subtag with no value",
 584               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-x-y-12345678-z",
 585               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-x-y-12345678-z" },
 586             { "locale key subtag invalid",
 587               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-123456789-def-x-y-12345678-z",
 588               "en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc" },
 589             // locale key subtag invalid in earlier position, all following subtags
 590             // dropped (and so the locale extension dropped as well)
 591             { "locale key subtag invalid in earlier position",
 592               "en-US-Newer-Yorker-a-bb-cc-dd-u-123456789-abc-bb-def-x-y-12345678-z",
 593               "en-US-Newer-Yorker-a-bb-cc-dd" },
 594         };
 595         for (int i = 0; i < tests.length; ++i) {
 596             String[] test = tests[i];
 597             String msg = "syntax error case " + i + " " + test[0];
 598             try {
 599                 Locale locale = Locale.forLanguageTag(test[1]);
 600                 assertEquals(msg, test[2], locale.toLanguageTag());
 601             }
 602             catch (IllegalArgumentException e) {
 603                 fail(msg + " caught exception: " + e);
 604             }
 605         }
 606 
 607         // duplicated extension are just ignored
 608         Locale locale = Locale.forLanguageTag("und-d-aa-00-bb-01-D-AA-10-cc-11-c-1234");
 609         assertEquals("extension", "aa-00-bb-01", locale.getExtension('d'));
 610         assertEquals("extension c", "1234", locale.getExtension('c'));
 611 
 612         locale = Locale.forLanguageTag("und-U-ca-gregory-u-ca-japanese");
 613         assertEquals("Unicode extension", "ca-gregory", locale.getExtension(Locale.UNICODE_LOCALE_EXTENSION));
 614 
 615         // redundant Unicode locale keys in an extension are ignored
 616         locale = Locale.forLanguageTag("und-u-aa-000-bb-001-bB-002-cc-003-c-1234");
 617         assertEquals("Unicode keywords", "aa-000-bb-001-cc-003", locale.getExtension(Locale.UNICODE_LOCALE_EXTENSION));
 618         assertEquals("Duplicated Unicode locake key followed by an extension", "1234", locale.getExtension('c'));
 619     }
 620 
 621     @Test
 622     public void testGetDisplayScript() {
 623         Locale latnLocale = Locale.forLanguageTag("und-latn");
 624         Locale hansLocale = Locale.forLanguageTag("und-hans");
 625 
 626         Locale oldLocale = Locale.getDefault();
 627 
 628         Locale.setDefault(Locale.US);
 629         assertEquals("latn US", "Latin", latnLocale.getDisplayScript());
 630         assertEquals("hans US", "Simplified", hansLocale.getDisplayScript());
 631 
 632         Locale.setDefault(Locale.GERMANY);
 633         assertEquals("latn DE", "Lateinisch", latnLocale.getDisplayScript());
 634         assertEquals("hans DE", "Vereinfacht", hansLocale.getDisplayScript());
 635 
 636         Locale.setDefault(oldLocale);
 637     }
 638 
 639     @Test
 640     public void testGetDisplayScriptWithLocale() {
 641         Locale latnLocale = Locale.forLanguageTag("und-latn");
 642         Locale hansLocale = Locale.forLanguageTag("und-hans");
 643 
 644         assertEquals("latn US", "Latin", latnLocale.getDisplayScript(Locale.US));
 645         assertEquals("hans US", "Simplified", hansLocale.getDisplayScript(Locale.US));
 646 
 647         assertEquals("latn DE", "Lateinisch", latnLocale.getDisplayScript(Locale.GERMANY));
 648         assertEquals("hans DE", "Vereinfacht", hansLocale.getDisplayScript(Locale.GERMANY));
 649     }
 650 
 651     @Test
 652     public void testGetDisplayName() {
 653         final Locale[] testLocales = {
 654                 Locale.ROOT,
 655                 Locale.ENGLISH,
 656                 Locale.US,
 657                 Locale.of("", "US"),
 658                 Locale.of("no", "NO", "NY"),
 659                 Locale.of("", "", "NY"),
 660                 Locale.forLanguageTag("zh-Hans"),
 661                 Locale.forLanguageTag("zh-Hant"),
 662                 Locale.forLanguageTag("zh-Hans-CN"),
 663                 Locale.forLanguageTag("und-Hans"),
 664         };
 665 
 666         final String[] displayNameEnglish = {
 667                 "",
 668                 "English",
 669                 "English (United States)",
 670                 "United States",
 671                 "Norwegian (Norway,Nynorsk)",

 685                 "Nynorsk",
 686                 "\u4e2d\u6587 (\u7b80\u4f53)",
 687                 "\u4e2d\u6587 (\u7e41\u4f53)",
 688                 "\u4e2d\u6587 (\u7b80\u4f53,\u4e2d\u56fd)",
 689                 "\u7b80\u4f53",
 690         };
 691 
 692         for (int i = 0; i < testLocales.length; i++) {
 693             Locale loc = testLocales[i];
 694             assertEquals("English display name for " + loc.toLanguageTag(),
 695                     displayNameEnglish[i], loc.getDisplayName(Locale.ENGLISH));
 696             assertEquals("Simplified Chinese display name for " + loc.toLanguageTag(),
 697                     displayNameSimplifiedChinese[i], loc.getDisplayName(Locale.CHINA));
 698         }
 699     }
 700 
 701     ///
 702     /// Builder tests
 703     ///
 704 
 705     @Test
 706     public void testBuilderSetLocale() {
 707         Builder builder = new Builder();
 708         Builder lenientBuilder = new Builder();
 709 
 710         String languageTag = "en-Latn-US-NewYork-a-bb-ccc-u-co-japanese-x-y-z";
 711         String target = "en-Latn-US-NewYork-a-bb-ccc-u-co-japanese-x-y-z";
 712 
 713         Locale locale = Locale.forLanguageTag(languageTag);
 714         Locale result = lenientBuilder
 715             .setLocale(locale)
 716             .build();
 717         assertEquals("long tag", target, result.toLanguageTag());
 718         assertEquals("long tag", locale, result);
 719 
 720         // null is illegal
 721         new BuilderNPE("locale") {
 722             public void call() { b.setLocale(null); }
 723         };
 724 
 725         // builder canonicalizes the three legacy locales:

 728         assertEquals("ja_JP_JP languagetag", "ja-JP-u-ca-japanese", locale.toLanguageTag());
 729         assertEquals("ja_JP_JP variant", "", locale.getVariant());
 730 
 731         locale = builder.setLocale(Locale.of("th", "TH", "TH")).build();
 732         assertEquals("th_TH_TH languagetag", "th-TH-u-nu-thai", locale.toLanguageTag());
 733         assertEquals("th_TH_TH variant", "", locale.getVariant());
 734 
 735         locale = builder.setLocale(Locale.of("no", "NO", "NY")).build();
 736         assertEquals("no_NO_NY languagetag", "nn-NO", locale.toLanguageTag());
 737         assertEquals("no_NO_NY language", "nn", locale.getLanguage());
 738         assertEquals("no_NO_NY variant", "", locale.getVariant());
 739 
 740         // non-canonical, non-legacy locales are invalid
 741         new BuilderILE("123_4567_89") {
 742             public void call() {
 743                 b.setLocale(Locale.of("123", "4567", "89"));
 744             }
 745         };
 746     }
 747 
 748     @Test
 749     public void testBuilderSetLanguageTag() {
 750         String source = "eN-LaTn-Us-NewYork-A-Xx-B-Yy-X-1-2-3";
 751         String target = "en-Latn-US-NewYork-a-xx-b-yy-x-1-2-3";
 752         Builder builder = new Builder();
 753         String result = builder
 754             .setLanguageTag(source)
 755             .build()
 756             .toLanguageTag();
 757         assertEquals("language", target, result);
 758 
 759         // redundant extensions cause a failure
 760         new BuilderILE() { public void call() { b.setLanguageTag("und-a-xx-yy-b-ww-A-00-11-c-vv"); }};
 761 
 762         // redundant Unicode locale extension keys within an Unicode locale extension cause a failure
 763         new BuilderILE() { public void call() { b.setLanguageTag("und-u-nu-thai-NU-chinese-xx-1234"); }};
 764     }
 765 
 766     @Test
 767     public void testBuilderSetLanguage() {
 768         // language is normalized to lower case
 769         String source = "eN";
 770         String target = "en";
 771         String defaulted = "";
 772         Builder builder = new Builder();
 773         String result = builder
 774             .setLanguage(source)
 775             .build()
 776             .getLanguage();
 777         assertEquals("en", target, result);
 778 
 779         // setting with empty resets
 780         result = builder
 781             .setLanguage(target)
 782             .setLanguage("")
 783             .build()
 784             .getLanguage();
 785         assertEquals("empty", defaulted, result);
 786 

 792                 .getLanguage();
 793         assertEquals("null", defaulted, result);
 794 
 795         // language codes must be 2-8 alpha
 796         // for forwards compatibility, 4-alpha and 5-8 alpha (registered)
 797         // languages are accepted syntax
 798         new BuilderILE("q", "abcdefghi", "13") { public void call() { b.setLanguage(arg); }};
 799 
 800         // language code validation is NOT performed, any 2-8-alpha passes
 801         assertNotNull("2alpha", builder.setLanguage("zz").build());
 802         assertNotNull("8alpha", builder.setLanguage("abcdefgh").build());
 803 
 804         // three-letter language codes are NOT canonicalized to two-letter
 805         result = builder
 806             .setLanguage("eng")
 807             .build()
 808             .getLanguage();
 809         assertEquals("eng", "eng", result);
 810     }
 811 
 812     @Test
 813     public void testBuilderSetScript() {
 814         // script is normalized to title case
 815         String source = "lAtN";
 816         String target = "Latn";
 817         String defaulted = "";
 818         Builder builder = new Builder();
 819         String result = builder
 820             .setScript(source)
 821             .build()
 822             .getScript();
 823         assertEquals("script", target, result);
 824 
 825         // setting with empty resets
 826         result = builder
 827             .setScript(target)
 828             .setScript("")
 829             .build()
 830             .getScript();
 831         assertEquals("empty", defaulted, result);
 832 
 833         // settting with null also resets
 834         result = builder
 835                 .setScript(target)
 836                 .setScript(null)
 837                 .build()
 838                 .getScript();
 839         assertEquals("null", defaulted, result);
 840 
 841         // ill-formed script codes throw IAE
 842         // must be 4alpha
 843         new BuilderILE("abc", "abcde", "l3tn") { public void call() { b.setScript(arg); }};
 844 
 845         // script code validation is NOT performed, any 4-alpha passes
 846         assertEquals("4alpha", "Wxyz", builder.setScript("wxyz").build().getScript());
 847     }
 848 
 849     @Test
 850     public void testBuilderSetRegion() {
 851         // region is normalized to upper case
 852         String source = "uS";
 853         String target = "US";
 854         String defaulted = "";
 855         Builder builder = new Builder();
 856         String result = builder
 857             .setRegion(source)
 858             .build()
 859             .getCountry();
 860         assertEquals("us", target, result);
 861 
 862         // setting with empty resets
 863         result = builder
 864             .setRegion(target)
 865             .setRegion("")
 866             .build()
 867             .getCountry();
 868         assertEquals("empty", defaulted, result);
 869 
 870         // setting with null also resets
 871         result = builder
 872                 .setRegion(target)
 873                 .setRegion(null)
 874                 .build()
 875                 .getCountry();
 876         assertEquals("null", defaulted, result);
 877 
 878         // ill-formed region codes throw IAE
 879         // 2 alpha or 3 numeric
 880         new BuilderILE("q", "abc", "12", "1234", "a3", "12a") { public void call() { b.setRegion(arg); }};
 881 
 882         // region code validation is NOT performed, any 2-alpha or 3-digit passes
 883         assertEquals("2alpha", "ZZ", builder.setRegion("ZZ").build().getCountry());
 884         assertEquals("3digit", "000", builder.setRegion("000").build().getCountry());
 885     }
 886 
 887     @Test
 888     public void testBuilderSetVariant() {
 889         // Variant case is not normalized in lenient variant mode
 890         String source = "NewYork";
 891         String target = source;
 892         String defaulted = "";
 893         Builder builder = new Builder();
 894         String result = builder
 895             .setVariant(source)
 896             .build()
 897             .getVariant();
 898         assertEquals("NewYork", target, result);
 899 
 900         result = builder
 901             .setVariant("NeWeR_YoRkEr")
 902             .build()
 903             .toLanguageTag();
 904         assertEquals("newer yorker", "und-NeWeR-YoRkEr", result);
 905 
 906         // subtags of variant are NOT reordered
 907         result = builder

 920 
 921         // setting to null also resets
 922         result = builder
 923                 .setVariant(target)
 924                 .setVariant(null)
 925                 .build()
 926                 .getVariant();
 927         assertEquals("null", defaulted, result);
 928 
 929         // ill-formed variants throw IAE
 930         // digit followed by 3-7 characters, or alpha followed by 4-8 characters.
 931         new BuilderILE("abcd", "abcdefghi", "1ab", "1abcdefgh") { public void call() { b.setVariant(arg); }};
 932 
 933         // 4 characters is ok as long as the first is a digit
 934         assertEquals("digit+3alpha", "1abc", builder.setVariant("1abc").build().getVariant());
 935 
 936         // all subfields must conform
 937         new BuilderILE("abcde-fg") { public void call() { b.setVariant(arg); }};
 938     }
 939 
 940     @Test
 941     public void testBuilderSetExtension() {
 942         // upper case characters are normalized to lower case
 943         final char sourceKey = 'a';
 944         final String sourceValue = "aB-aBcdefgh-12-12345678";
 945         String target = "ab-abcdefgh-12-12345678";
 946         Builder builder = new Builder();
 947         String result = builder
 948             .setExtension(sourceKey, sourceValue)
 949             .build()
 950             .getExtension(sourceKey);
 951         assertEquals("extension", target, result);
 952 
 953         // setting with empty resets
 954         result = builder
 955             .setExtension(sourceKey, sourceValue)
 956             .setExtension(sourceKey, "")
 957             .build()
 958             .getExtension(sourceKey);
 959         assertEquals("empty", null, result);
 960 

1011             .toLanguageTag();
1012         assertEquals("reorder", "und-u-123-456-xx-789-yy-456-zz-123", result);
1013 
1014         // multiple keyword types
1015         result = builder
1016             .clear()
1017             .setExtension('u', "nu-thai-foobar")
1018             .build()
1019             .getUnicodeLocaleType("nu");
1020         assertEquals("multiple types", "thai-foobar", result);
1021 
1022         // redundant locale extensions are ignored
1023         result = builder
1024             .clear()
1025             .setExtension('u', "nu-thai-NU-chinese-xx-1234")
1026             .build()
1027             .toLanguageTag();
1028         assertEquals("duplicate keys", "und-u-nu-thai-xx-1234", result);
1029     }
1030 
1031     @Test
1032     public void testBuilderAddUnicodeLocaleAttribute() {
1033         Builder builder = new Builder();
1034         Locale locale = builder
1035             .addUnicodeLocaleAttribute("def")
1036             .addUnicodeLocaleAttribute("abc")
1037             .build();
1038 
1039         Set<String> uattrs = locale.getUnicodeLocaleAttributes();
1040         assertEquals("number of attributes", 2, uattrs.size());
1041         assertTrue("attribute abc", uattrs.contains("abc"));
1042         assertTrue("attribute def", uattrs.contains("def"));
1043 
1044         // remove attribute
1045         locale = builder.removeUnicodeLocaleAttribute("xxx")
1046             .build();
1047 
1048         assertEquals("remove bogus", 2, uattrs.size());
1049 
1050         // add duplicate
1051         locale = builder.addUnicodeLocaleAttribute("abc")
1052             .build();
1053         assertEquals("add duplicate", 2, uattrs.size());
1054 
1055         // null attribute throws NPE
1056         new BuilderNPE("null attribute") { public void call() { b.addUnicodeLocaleAttribute(null); }};
1057         new BuilderNPE("null attribute removal") { public void call() { b.removeUnicodeLocaleAttribute(null); }};
1058 
1059         // illformed attribute throws IllformedLocaleException
1060         new BuilderILE("invalid attribute") { public void call() { b.addUnicodeLocaleAttribute("ca"); }};
1061     }
1062 
1063     @Test
1064     public void testBuildersetUnicodeLocaleKeyword() {
1065         // Note: most behavior is tested in testBuilderSetExtension
1066         Builder builder = new Builder();
1067         Locale locale = builder
1068             .setUnicodeLocaleKeyword("co", "japanese")
1069             .setUnicodeLocaleKeyword("nu", "thai")
1070             .build();
1071         assertEquals("co", "japanese", locale.getUnicodeLocaleType("co"));
1072         assertEquals("nu", "thai", locale.getUnicodeLocaleType("nu"));
1073         assertEquals("keys", 2, locale.getUnicodeLocaleKeys().size());
1074 
1075         // can clear a keyword by setting to null, others remain
1076         String result = builder
1077             .setUnicodeLocaleKeyword("co", null)
1078             .build()
1079             .toLanguageTag();
1080         assertEquals("empty co", "und-u-nu-thai", result);
1081 
1082         // locale keyword extension goes when all keywords are gone
1083         result = builder

1087         assertEquals("empty nu", "und", result);
1088 
1089         // locale keywords are ordered independent of order of addition
1090         result = builder
1091             .setUnicodeLocaleKeyword("zz", "012")
1092             .setUnicodeLocaleKeyword("aa", "345")
1093             .build()
1094             .toLanguageTag();
1095         assertEquals("reordered", "und-u-aa-345-zz-012", result);
1096 
1097         // null keyword throws NPE
1098         new BuilderNPE("keyword") { public void call() { b.setUnicodeLocaleKeyword(null, "thai"); }};
1099 
1100         // well-formed keywords are two alphanum
1101         new BuilderILE("a", "abc") { public void call() { b.setUnicodeLocaleKeyword(arg, "value"); }};
1102 
1103         // well-formed values are 3-8 alphanum
1104         new BuilderILE("ab", "abcdefghi") { public void call() { b.setUnicodeLocaleKeyword("ab", arg); }};
1105     }
1106 
1107     @Test
1108     public void testBuilderPrivateUseExtension() {
1109         // normalizes hyphens to underscore, case to lower
1110         String source = "c-B-a";
1111         String target = "c-b-a";
1112         Builder builder = new Builder();
1113         String result = builder
1114             .setExtension(Locale.PRIVATE_USE_EXTENSION, source)
1115             .build()
1116             .getExtension(Locale.PRIVATE_USE_EXTENSION);
1117         assertEquals("abc", target, result);
1118 
1119         // multiple hyphens are ill-formed
1120         new BuilderILE("a--b") { public void call() { b.setExtension(Locale.PRIVATE_USE_EXTENSION, arg); }};
1121     }
1122 
1123     @Test
1124     public void testBuilderClear() {
1125         String monster = "en-latn-US-NewYork-a-bb-cc-u-co-japanese-x-z-y-x-x";
1126         Builder builder = new Builder();
1127         Locale locale = Locale.forLanguageTag(monster);
1128         String result = builder
1129             .setLocale(locale)
1130             .clear()
1131             .build()
1132             .toLanguageTag();
1133         assertEquals("clear", "und", result);
1134     }
1135 
1136     @Test
1137     public void testBuilderRemoveUnicodeAttribute() {
1138         // tested in testBuilderAddUnicodeAttribute
1139     }
1140 
1141     @Test
1142     public void testBuilderBuild() {
1143         // tested in other test methods
1144     }
1145 
1146     @Test
1147     public void testSerialize() {
1148         final Locale[] testLocales = {
1149             Locale.ROOT,
1150             Locale.ENGLISH,
1151             Locale.US,
1152             Locale.of("en", "US", "Win"),
1153             Locale.of("en", "US", "Win_XP"),
1154             Locale.JAPAN,
1155             Locale.of("ja", "JP", "JP"),
1156             Locale.of("th", "TH"),
1157             Locale.of("th", "TH", "TH"),
1158             Locale.of("no", "NO"),
1159             Locale.of("nb", "NO"),
1160             Locale.of("nn", "NO"),
1161             Locale.of("no", "NO", "NY"),
1162             Locale.of("nn", "NO", "NY"),
1163             Locale.of("he", "IL"),
1164             Locale.of("he", "IL", "var"),
1165             Locale.of("Language", "Country", "Variant"),
1166             Locale.of("", "US"),

1172             Locale.forLanguageTag("und-Hant"),
1173             Locale.forLanguageTag("und-a-123-456"),
1174             Locale.forLanguageTag("en-x-java"),
1175             Locale.forLanguageTag("th-TH-u-ca-buddist-nu-thai-x-lvariant-TH"),
1176         };
1177 
1178         for (Locale locale : testLocales) {
1179             try {
1180                 // write
1181                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1182                 ObjectOutputStream oos = new ObjectOutputStream(bos);
1183                 oos.writeObject(locale);
1184 
1185                 // read
1186                 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
1187                 ObjectInputStream ois = new ObjectInputStream(bis);
1188                 Object o = ois.readObject();
1189 
1190                 assertEquals("roundtrip " + locale, locale, o);
1191             } catch (Exception e) {
1192                 fail(locale + " encountered exception:" + e.getLocalizedMessage());
1193             }
1194         }
1195     }
1196 
1197     @Test
1198     public void testDeserialize6() {
1199         final String TESTFILEPREFIX = "java6locale_";
1200 
1201         File dataDir = null;
1202         String dataDirName = System.getProperty("serialized.data.dir");
1203         if (dataDirName == null) {
1204             URL resdirUrl = getClass().getClassLoader().getResource("serialized");
1205             if (resdirUrl != null) {
1206                 try {
1207                     dataDir = new File(resdirUrl.toURI());
1208                 } catch (URISyntaxException urie) {
1209                 }
1210             }
1211         } else {
1212             dataDir = new File(dataDirName);
1213         }
1214 
1215         if (dataDir == null) {
1216             fail("'dataDir' is null. serialized.data.dir Property value is "+dataDirName);
1217             return;
1218         } else if (!dataDir.isDirectory()) {
1219             fail("'dataDir' is not a directory. dataDir: "+dataDir.toString());
1220             return;
1221         }
1222 
1223         File[] files = dataDir.listFiles();
1224         for (File testfile : files) {
1225             if (testfile.isDirectory()) {
1226                 continue;
1227             }
1228             String name = testfile.getName();
1229             if (!name.startsWith(TESTFILEPREFIX)) {
1230                 continue;
1231             }
1232             Locale locale;
1233             String locStr = name.substring(TESTFILEPREFIX.length());
1234             if (locStr.equals("ROOT")) {
1235                 locale = Locale.ROOT;
1236             } else {
1237                 String[] fields = locStr.split("_", 3);
1238                 String lang = fields[0];
1239                 String country = (fields.length >= 2) ? fields[1] : "";
1240                 String variant = (fields.length == 3) ? fields[2] : "";
1241                 locale = Locale.of(lang, country, variant);
1242             }
1243 
1244             // deserialize
1245             try (FileInputStream fis = new FileInputStream(testfile);
1246                  ObjectInputStream ois = new ObjectInputStream(fis))
1247             {
1248                 Object o = ois.readObject();
1249                 assertEquals("Deserialize Java 6 Locale " + locale, o, locale);
1250             } catch (Exception e) {
1251                 fail("Exception while reading " + testfile.getAbsolutePath() + " - " + e.getMessage());
1252             }
1253         }
1254     }
1255 
1256     @Test
1257     public void testBug7002320() {
1258         // forLanguageTag() and Builder.setLanguageTag(String)
1259         // should add a location extension for following two cases.
1260         //
1261         // 1. language/country are "ja"/"JP" and the resolved variant (x-lvariant-*)
1262         //    is exactly "JP" and no BCP 47 extensions are available, then add
1263         //    a Unicode locale extension "ca-japanese".
1264         // 2. language/country are "th"/"TH" and the resolved variant is exactly
1265         //    "TH" and no BCP 47 extensions are available, then add a Unicode locale
1266         //    extension "nu-thai".
1267         //
1268         String[][] testdata = {
1269             {"ja-JP-x-lvariant-JP", "ja-JP-u-ca-japanese-x-lvariant-JP"},   // special case 1
1270             {"ja-JP-x-lvariant-JP-XXX"},
1271             {"ja-JP-u-ca-japanese-x-lvariant-JP"},
1272             {"ja-JP-u-ca-gregory-x-lvariant-JP"},
1273             {"ja-JP-u-cu-jpy-x-lvariant-JP"},
1274             {"ja-x-lvariant-JP"},
1275             {"th-TH-x-lvariant-TH", "th-TH-u-nu-thai-x-lvariant-TH"},   // special case 2
1276             {"th-TH-u-nu-thai-x-lvariant-TH"},

1280         Builder bldr = new Builder();
1281 
1282         for (String[] data : testdata) {
1283             String in = data[0];
1284             String expected = (data.length == 1) ? data[0] : data[1];
1285 
1286             // forLanguageTag
1287             Locale loc = Locale.forLanguageTag(in);
1288             String out = loc.toLanguageTag();
1289             assertEquals("Language tag roundtrip by forLanguageTag with input: " + in, expected, out);
1290 
1291             // setLanguageTag
1292             bldr.clear();
1293             bldr.setLanguageTag(in);
1294             loc = bldr.build();
1295             out = loc.toLanguageTag();
1296             assertEquals("Language tag roundtrip by Builder.setLanguageTag with input: " + in, expected, out);
1297         }
1298     }
1299 
1300     @Test
1301     public void testBug7023613() {
1302         String[][] testdata = {
1303             {"en-Latn", "en__#Latn"},
1304             {"en-u-ca-japanese", "en__#u-ca-japanese"},
1305         };
1306 
1307         for (String[] data : testdata) {
1308             String in = data[0];
1309             String expected = (data.length == 1) ? data[0] : data[1];
1310 
1311             Locale loc = Locale.forLanguageTag(in);
1312             String out = loc.toString();
1313             assertEquals("Empty country field with non-empty script/extension with input: " + in, expected, out);
1314         }
1315     }
1316 
1317     /*
1318      * 7033504: (lc) incompatible behavior change for ja_JP_JP and th_TH_TH locales
1319      */
1320     @Test
1321     public void testBug7033504() {
1322         checkCalendar(Locale.of("ja", "JP", "jp"), "java.util.GregorianCalendar");
1323         checkCalendar(Locale.of("ja", "jp", "jp"), "java.util.GregorianCalendar");
1324         checkCalendar(Locale.of("ja", "JP", "JP"), "java.util.JapaneseImperialCalendar");
1325         checkCalendar(Locale.of("ja", "jp", "JP"), "java.util.JapaneseImperialCalendar");
1326         checkCalendar(Locale.forLanguageTag("en-u-ca-japanese"),
1327                       "java.util.JapaneseImperialCalendar");
1328 
1329         checkDigit(Locale.of("th", "TH", "th"), '0');
1330         checkDigit(Locale.of("th", "th", "th"), '0');
1331         checkDigit(Locale.of("th", "TH", "TH"), '\u0e50');
1332         checkDigit(Locale.of("th", "TH", "TH"), '\u0e50');
1333         checkDigit(Locale.forLanguageTag("en-u-nu-thai"), '\u0e50');
1334     }
1335 
1336     private void checkCalendar(Locale loc, String expected) {
1337         Calendar cal = Calendar.getInstance(loc);
1338         assertEquals("Wrong calendar", expected, cal.getClass().getName());
1339     }
1340 
1341     private void checkDigit(Locale loc, Character expected) {
1342         DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(loc);
1343         Character zero = dfs.getZeroDigit();
1344         assertEquals("Wrong digit zero char", expected, zero);
1345     }
1346 
1347     ///
1348     /// utility asserts
1349     ///
1350 
1351     private void assertTrue(String msg, boolean v) {
1352         if (!v) {
1353             fail(msg + ": expected true");
1354         }
1355     }
1356 
1357     private void assertFalse(String msg, boolean v) {
1358         if (v) {
1359             fail(msg + ": expected false");
1360         }
1361     }
1362 
1363     private void assertEquals(String msg, Object e, Object v) {
1364         if (e == null ? v != null : !e.equals(v)) {
1365             if (e != null) {
1366                 e = "'" + e + "'";
1367             }
1368             if (v != null) {
1369                 v = "'" + v + "'";
1370             }
1371             fail(msg + ": expected " + e + " but got " + v);
1372         }
1373     }
1374 
1375     private void assertNotEquals(String msg, Object e, Object v) {
1376         if (e == null ? v == null : e.equals(v)) {
1377             if (e != null) {
1378                 e = "'" + e + "'";
1379             }
1380             fail(msg + ": expected not equal " + e);
1381         }
1382     }
1383 
1384     private void assertNull(String msg, Object o) {
1385         if (o != null) {
1386             fail(msg + ": expected null but got '" + o + "'");
1387         }
1388     }
1389 
1390     private void assertNotNull(String msg, Object o) {
1391         if (o == null) {
1392             fail(msg + ": expected non null");
1393         }
1394     }
1395 
1396     // not currently used, might get rid of exceptions from the API
1397     private abstract class ExceptionTest {
1398         private final Class<? extends Exception> exceptionClass;
1399 
1400         ExceptionTest(Class<? extends Exception> exceptionClass) {
1401             this.exceptionClass = exceptionClass;
1402         }
1403 
1404         public void run() {
1405             String failMsg = null;
1406             try {
1407                 call();
1408                 failMsg = "expected " + exceptionClass.getName() + "  but no exception thrown.";
1409             }
1410             catch (Exception e) {
1411                 if (!exceptionClass.isAssignableFrom(e.getClass())) {
1412                     failMsg = "expected " + exceptionClass.getName() + " but caught " + e;
1413                 }
1414             }
1415             if (failMsg != null) {
1416                 String msg = message();
1417                 msg = msg == null ? "" : msg + " ";
1418                 fail(msg + failMsg);
1419             }
1420         }
1421 
1422         public String message() {
1423             return null;
1424         }
1425 
1426         public abstract void call();
1427     }
1428 
1429     private abstract class ExpectNPE extends ExceptionTest {
1430         ExpectNPE() {
1431             super(NullPointerException.class);
1432             run();
1433         }
1434     }
1435 
1436     private abstract class BuilderNPE extends ExceptionTest {
1437         protected final String msg;
1438         protected final Builder b = new Builder();
< prev index next >