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