Path: blob/master/test/jdk/java/util/Locale/LocaleEnhanceTest.java
41149 views
/*1* Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.BufferedReader;24import java.io.ByteArrayInputStream;25import java.io.ByteArrayOutputStream;26import java.io.File;27import java.io.FileInputStream;28import java.io.InputStreamReader;29import java.io.ObjectInputStream;30import java.io.ObjectOutputStream;31import java.net.URISyntaxException;32import java.net.URL;33import java.text.DecimalFormatSymbols;34import java.util.ArrayList;35import java.util.Arrays;36import java.util.Calendar;37import java.util.IllformedLocaleException;38import java.util.List;39import java.util.Locale;40import java.util.Locale.Builder;41import java.util.Set;4243/**44* @test45* @bug 6875847 6992272 7002320 7015500 7023613 7032820 7033504 700460346* 7044019 8008577 8176853 8255086 826320247* @summary test API changes to Locale48* @library /java/text/testlib49* @modules jdk.localedata50* @compile LocaleEnhanceTest.java51* @run main/othervm -Djava.locale.providers=JRE,SPI -esa LocaleEnhanceTest52*/53public class LocaleEnhanceTest extends IntlTest {5455public static void main(String[] args) throws Exception {56List<String> argList = new ArrayList<String>();57argList.addAll(Arrays.asList(args));58argList.add("-nothrow");59new LocaleEnhanceTest().run(argList.toArray(new String[argList.size()]));60}6162public LocaleEnhanceTest() {63}6465///66/// Generic sanity tests67///6869/** A canonical language code. */70private static final String l = "en";7172/** A canonical script code.. */73private static final String s = "Latn";7475/** A canonical region code. */76private static final String c = "US";7778/** A canonical variant code. */79private static final String v = "NewYork";8081/**82* Ensure that Builder builds locales that have the expected83* tag and java6 ID. Note the odd cases for the ID.84*/85public void testCreateLocaleCanonicalValid() {86String[] valids = {87"en-Latn-US-NewYork", "en_US_NewYork_#Latn",88"en-Latn-US", "en_US_#Latn",89"en-Latn-NewYork", "en__NewYork_#Latn", // double underscore90"en-Latn", "en__#Latn", // double underscore91"en-US-NewYork", "en_US_NewYork",92"en-US", "en_US",93"en-NewYork", "en__NewYork", // double underscore94"en", "en",95"und-Latn-US-NewYork", "_US_NewYork_#Latn",96"und-Latn-US", "_US_#Latn",97"und-Latn-NewYork", "", // variant only not supported98"und-Latn", "",99"und-US-NewYork", "_US_NewYork",100"und-US", "_US",101"und-NewYork", "", // variant only not supported102"und", ""103};104105Builder builder = new Builder();106107for (int i = 0; i < valids.length; i += 2) {108String tag = valids[i];109String id = valids[i+1];110111String idl = (i & 16) == 0 ? l : "";112String ids = (i & 8) == 0 ? s : "";113String idc = (i & 4) == 0 ? c : "";114String idv = (i & 2) == 0 ? v : "";115116String msg = String.valueOf(i/2) + ": '" + tag + "' ";117118try {119Locale l = builder120.setLanguage(idl)121.setScript(ids)122.setRegion(idc)123.setVariant(idv)124.build();125assertEquals(msg + "language", idl, l.getLanguage());126assertEquals(msg + "script", ids, l.getScript());127assertEquals(msg + "country", idc, l.getCountry());128assertEquals(msg + "variant", idv, l.getVariant());129assertEquals(msg + "tag", tag, l.toLanguageTag());130assertEquals(msg + "id", id, l.toString());131}132catch (IllegalArgumentException e) {133errln(msg + e.getMessage());134}135}136}137138/**139* Test that locale construction works with 'multiple variants'.140* <p>141* The string "Newer__Yorker" is treated as three subtags,142* "Newer", "", and "Yorker", and concatenated into one143* subtag by omitting empty subtags and joining the remainer144* with underscores. So the resulting variant tag is "Newer_Yorker".145* Note that 'New' and 'York' are invalid BCP47 variant subtags146* because they are too short.147*/148public void testCreateLocaleMultipleVariants() {149150String[] valids = {151"en-Latn-US-Newer-Yorker", "en_US_Newer_Yorker_#Latn",152"en-Latn-Newer-Yorker", "en__Newer_Yorker_#Latn",153"en-US-Newer-Yorker", "en_US_Newer_Yorker",154"en-Newer-Yorker", "en__Newer_Yorker",155"und-Latn-US-Newer-Yorker", "_US_Newer_Yorker_#Latn",156"und-Latn-Newer-Yorker", "",157"und-US-Newer-Yorker", "_US_Newer_Yorker",158"und-Newer-Yorker", "",159};160161Builder builder = new Builder(); // lenient variant162163final String idv = "Newer_Yorker";164for (int i = 0; i < valids.length; i += 2) {165String tag = valids[i];166String id = valids[i+1];167168String idl = (i & 8) == 0 ? l : "";169String ids = (i & 4) == 0 ? s : "";170String idc = (i & 2) == 0 ? c : "";171172String msg = String.valueOf(i/2) + ": " + tag + " ";173try {174Locale l = builder175.setLanguage(idl)176.setScript(ids)177.setRegion(idc)178.setVariant(idv)179.build();180181assertEquals(msg + " language", idl, l.getLanguage());182assertEquals(msg + " script", ids, l.getScript());183assertEquals(msg + " country", idc, l.getCountry());184assertEquals(msg + " variant", idv, l.getVariant());185186assertEquals(msg + "tag", tag, l.toLanguageTag());187assertEquals(msg + "id", id, l.toString());188}189catch (IllegalArgumentException e) {190errln(msg + e.getMessage());191}192}193}194195/**196* Ensure that all these invalid formats are not recognized by197* forLanguageTag.198*/199public void testCreateLocaleCanonicalInvalidSeparator() {200String[] invalids = {201// trailing separator202"en_Latn_US_NewYork_",203"en_Latn_US_",204"en_Latn_",205"en_",206"_",207208// double separator209"en_Latn_US__NewYork",210"_Latn_US__NewYork",211"en_US__NewYork",212"_US__NewYork",213214// 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'219220// double separator without language or script221"__US",222"__NewYork",223224// triple separator anywhere except within variant225"en___NewYork",226"en_Latn___NewYork",227"_Latn___NewYork",228"___NewYork",229};230231for (int i = 0; i < invalids.length; ++i) {232String id = invalids[i];233Locale l = Locale.forLanguageTag(id);234assertEquals(id, "und", l.toLanguageTag());235}236}237238/**239* Ensure that all current locale ids parse. Use DateFormat as a proxy240* for all current locale ids.241*/242public void testCurrentLocales() {243Locale[] locales = java.text.DateFormat.getAvailableLocales();244Builder builder = new Builder();245246for (Locale target : locales) {247String tag = target.toLanguageTag();248249// the tag recreates the original locale,250// except no_NO_NY251Locale tagResult = Locale.forLanguageTag(tag);252if (!target.getVariant().equals("NY")) {253assertEquals("tagResult", target, tagResult);254}255256// the builder also recreates the original locale,257// except ja_JP_JP, th_TH_TH and no_NO_NY258Locale builderResult = builder.setLocale(target).build();259if (target.getVariant().length() != 2) {260assertEquals("builderResult", target, builderResult);261}262}263}264265/**266* Ensure that all icu locale ids parse.267*/268public void testIcuLocales() throws Exception {269BufferedReader br = new BufferedReader(270new InputStreamReader(271LocaleEnhanceTest.class.getResourceAsStream("icuLocales.txt"),272"UTF-8"));273String id = null;274while (null != (id = br.readLine())) {275Locale result = Locale.forLanguageTag(id);276assertEquals("ulocale", id, result.toLanguageTag());277}278}279280///281/// Compatibility tests282///283284public void testConstructor() {285// all the old weirdness still holds, no new weirdness286String[][] tests = {287// language to lower case, region to upper, variant unchanged288// short289{ "X", "y", "z", "x", "Y" },290// long291{ "xXxXxXxXxXxX", "yYyYyYyYyYyYyYyY", "zZzZzZzZzZzZzZzZ",292"xxxxxxxxxxxx", "YYYYYYYYYYYYYYYY" },293// mapped language ids294{ "he", "IL", "", "he" },295{ "iw", "IL", "", "he" },296{ "yi", "DE", "", "yi" },297{ "ji", "DE", "", "yi" },298{ "id", "ID", "", "id" },299{ "in", "ID", "", "id" },300// special variants301{ "ja", "JP", "JP" },302{ "th", "TH", "TH" },303{ "no", "NO", "NY" },304{ "no", "NO", "NY" },305// no canonicalization of 3-letter language codes306{ "eng", "US", "" }307};308for (int i = 0; i < tests.length; ++ i) {309String[] test = tests[i];310String id = String.valueOf(i);311Locale locale = new Locale(test[0], test[1], test[2]);312assertEquals(id + " lang", test.length > 3 ? test[3] : test[0], locale.getLanguage());313assertEquals(id + " region", test.length > 4 ? test[4] : test[1], locale.getCountry());314assertEquals(id + " variant", test.length > 5 ? test[5] : test[2], locale.getVariant());315}316}317318///319/// Locale API tests.320///321322public void testGetScript() {323// forLanguageTag normalizes case324Locale locale = Locale.forLanguageTag("und-latn");325assertEquals("forLanguageTag", "Latn", locale.getScript());326327// Builder normalizes case328locale = new Builder().setScript("LATN").build();329assertEquals("builder", "Latn", locale.getScript());330331// empty string is returned, not null, if there is no script332locale = Locale.forLanguageTag("und");333assertEquals("script is empty string", "", locale.getScript());334}335336public void testGetExtension() {337// forLanguageTag does NOT normalize to hyphen338Locale locale = Locale.forLanguageTag("und-a-some_ex-tension");339assertEquals("some_ex-tension", null, locale.getExtension('a'));340341// regular extension342locale = new Builder().setExtension('a', "some-ex-tension").build();343assertEquals("builder", "some-ex-tension", locale.getExtension('a'));344345// returns null if extension is not present346assertEquals("empty b", null, locale.getExtension('b'));347348// throws exception if extension tag is illegal349new ExpectIAE() { public void call() { Locale.forLanguageTag("").getExtension('\uD800'); }};350351// 'x' is not an extension, it's a private use tag, but it's accessed through this API352locale = Locale.forLanguageTag("x-y-z-blork");353assertEquals("x", "y-z-blork", locale.getExtension('x'));354}355356public void testGetExtensionKeys() {357Locale locale = Locale.forLanguageTag("und-a-xx-yy-b-zz-ww");358Set<Character> result = locale.getExtensionKeys();359assertEquals("result size", 2, result.size());360assertTrue("'a','b'", result.contains('a') && result.contains('b'));361362// result is not mutable363try {364result.add('x');365errln("expected exception on add to extension key set");366}367catch (UnsupportedOperationException e) {368// ok369}370371// returns empty set if no extensions372locale = Locale.forLanguageTag("und");373assertTrue("empty result", locale.getExtensionKeys().isEmpty());374}375376public void testGetUnicodeLocaleAttributes() {377Locale locale = Locale.forLanguageTag("en-US-u-abc-def");378Set<String> attributes = locale.getUnicodeLocaleAttributes();379assertEquals("number of attributes", 2, attributes.size());380assertTrue("attribute abc", attributes.contains("abc"));381assertTrue("attribute def", attributes.contains("def"));382383locale = Locale.forLanguageTag("en-US-u-ca-gregory");384attributes = locale.getUnicodeLocaleAttributes();385assertTrue("empty attributes", attributes.isEmpty());386}387388public void testGetUnicodeLocaleType() {389Locale locale = Locale.forLanguageTag("und-u-co-japanese-nu-thai");390assertEquals("collation", "japanese", locale.getUnicodeLocaleType("co"));391assertEquals("numbers", "thai", locale.getUnicodeLocaleType("nu"));392393// Unicode locale extension key is case insensitive394assertEquals("key case", "japanese", locale.getUnicodeLocaleType("Co"));395396// if keyword is not present, returns null397assertEquals("locale keyword not present", null, locale.getUnicodeLocaleType("xx"));398399// if no locale extension is set, returns null400locale = Locale.forLanguageTag("und");401assertEquals("locale extension not present", null, locale.getUnicodeLocaleType("co"));402403// typeless keyword404locale = Locale.forLanguageTag("und-u-kn");405assertEquals("typeless keyword", "", locale.getUnicodeLocaleType("kn"));406407// invalid keys throw exception408new ExpectIAE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType("q"); }};409new ExpectIAE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType("abcdefghi"); }};410411// null argument throws exception412new ExpectNPE() { public void call() { Locale.forLanguageTag("").getUnicodeLocaleType(null); }};413}414415public void testGetUnicodeLocaleKeys() {416Locale locale = Locale.forLanguageTag("und-u-co-japanese-nu-thai");417Set<String> result = locale.getUnicodeLocaleKeys();418assertEquals("two keys", 2, result.size());419assertTrue("co and nu", result.contains("co") && result.contains("nu"));420421// result is not modifiable422try {423result.add("frobozz");424errln("expected exception when add to locale key set");425}426catch (UnsupportedOperationException e) {427// ok428}429}430431public void testPrivateUseExtension() {432Locale locale = Locale.forLanguageTag("x-y-x-blork-");433assertEquals("blork", "y-x-blork", locale.getExtension(Locale.PRIVATE_USE_EXTENSION));434435locale = Locale.forLanguageTag("und");436assertEquals("no privateuse", null, locale.getExtension(Locale.PRIVATE_USE_EXTENSION));437}438439public void testToLanguageTag() {440// lots of normalization to test here441// test locales created using the constructor442String[][] tests = {443// empty locale canonicalizes to 'und'444{ "", "", "", "und" },445// variant alone is not a valid Locale, but has a valid language tag446{ "", "", "NewYork", "und-NewYork" },447// standard valid locales448{ "", "Us", "", "und-US" },449{ "", "US", "NewYork", "und-US-NewYork" },450{ "EN", "", "", "en" },451{ "EN", "", "NewYork", "en-NewYork" },452{ "EN", "US", "", "en-US" },453{ "EN", "US", "NewYork", "en-US-NewYork" },454// underscore in variant will be emitted as multiple variant subtags455{ "en", "US", "Newer_Yorker", "en-US-Newer-Yorker" },456// invalid variant subtags are appended as private use457{ "en", "US", "new_yorker", "en-US-x-lvariant-new-yorker" },458// the first invalid variant subtags and following variant subtags are appended as private use459{ "en", "US", "Windows_XP_Home", "en-US-Windows-x-lvariant-XP-Home" },460// too long variant and following variant subtags disappear461{ "en", "US", "WindowsVista_SP2", "en-US" },462// invalid region subtag disappears463{ "en", "USA", "", "en" },464// invalid language tag disappears465{ "e", "US", "", "und-US" },466// three-letter language tags are not canonicalized467{ "Eng", "", "", "eng" },468// legacy languages canonicalize to modern equivalents469{ "he", "IL", "", "he-IL" },470{ "iw", "IL", "", "he-IL" },471{ "yi", "DE", "", "yi-DE" },472{ "ji", "DE", "", "yi-DE" },473{ "id", "ID", "", "id-ID" },474{ "in", "ID", "", "id-ID" },475// special values are converted on output476{ "ja", "JP", "JP", "ja-JP-u-ca-japanese-x-lvariant-JP" },477{ "th", "TH", "TH", "th-TH-u-nu-thai-x-lvariant-TH" },478{ "no", "NO", "NY", "nn-NO" }479};480for (int i = 0; i < tests.length; ++i) {481String[] test = tests[i];482Locale locale = new Locale(test[0], test[1], test[2]);483assertEquals("case " + i, test[3], locale.toLanguageTag());484}485486// test locales created from forLanguageTag487String[][] tests1 = {488// case is normalized during the round trip489{ "EN-us", "en-US" },490{ "en-Latn-US", "en-Latn-US" },491// reordering Unicode locale extensions492{ "de-u-co-phonebk-ca-gregory", "de-u-ca-gregory-co-phonebk" },493// private use only language tag is preserved (no extra "und")494{ "x-elmer", "x-elmer" },495{ "x-lvariant-JP", "x-lvariant-JP" },496};497for (String[] test : tests1) {498Locale locale = Locale.forLanguageTag(test[0]);499assertEquals("case " + test[0], test[1], locale.toLanguageTag());500}501502}503504public void testForLanguageTag() {505// forLanguageTag implements the 'Language-Tag' production of506// BCP47, so it handles private use and legacy language tags,507// unlike locale builder. Tags listed below (except for the508// sample private use tags) come from 4646bis Feb 29, 2009.509510String[][] tests = {511// private use tags only512{ "x-abc", "x-abc" },513{ "x-a-b-c", "x-a-b-c" },514{ "x-a-12345678", "x-a-12345678" },515516// legacy language tags with preferred mappings517{ "i-ami", "ami" },518{ "i-bnn", "bnn" },519{ "i-hak", "hak" },520{ "i-klingon", "tlh" },521{ "i-lux", "lb" }, // two-letter tag522{ "i-navajo", "nv" }, // two-letter tag523{ "i-pwn", "pwn" },524{ "i-tao", "tao" },525{ "i-tay", "tay" },526{ "i-tsu", "tsu" },527{ "art-lojban", "jbo" },528{ "no-bok", "nb" },529{ "no-nyn", "nn" },530{ "sgn-BE-FR", "sfb" },531{ "sgn-BE-NL", "vgt" },532{ "sgn-CH-DE", "sgg" },533{ "zh-guoyu", "cmn" },534{ "zh-hakka", "hak" },535{ "zh-min-nan", "nan" },536{ "zh-xiang", "hsn" },537538// irregular legacy language tags, no preferred mappings, drop illegal fields539// from end. If no subtag is mappable, fallback to 'und'540{ "i-default", "en-x-i-default" },541{ "i-enochian", "x-i-enochian" },542{ "i-mingo", "see-x-i-mingo" },543{ "en-GB-oed", "en-GB-x-oed" },544{ "zh-min", "nan-x-zh-min" },545{ "cel-gaulish", "xtg-x-cel-gaulish" },546};547for (int i = 0; i < tests.length; ++i) {548String[] test = tests[i];549Locale locale = Locale.forLanguageTag(test[0]);550assertEquals("legacy language tag case " + i, test[1], locale.toLanguageTag());551}552553// forLanguageTag ignores everything past the first place it encounters554// a syntax error555tests = new String[][] {556{ "valid",557"en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y-12345678-z",558"en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y-12345678-z" },559{ "segment of private use tag too long",560"en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y-123456789-z",561"en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y" },562{ "segment of private use tag is empty",563"en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y--12345678-z",564"en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x-y" },565{ "first segment of private use tag is empty",566"en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-x--y-12345678-z",567"en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def" },568{ "illegal extension tag",569"en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def-\uD800-y-12345678-z",570"en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-def" },571{ "locale subtag with no value",572"en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-x-y-12345678-z",573"en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-bb-x-y-12345678-z" },574{ "locale key subtag invalid",575"en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc-123456789-def-x-y-12345678-z",576"en-US-Newer-Yorker-a-bb-cc-dd-u-aa-abc" },577// locale key subtag invalid in earlier position, all following subtags578// dropped (and so the locale extension dropped as well)579{ "locale key subtag invalid in earlier position",580"en-US-Newer-Yorker-a-bb-cc-dd-u-123456789-abc-bb-def-x-y-12345678-z",581"en-US-Newer-Yorker-a-bb-cc-dd" },582};583for (int i = 0; i < tests.length; ++i) {584String[] test = tests[i];585String msg = "syntax error case " + i + " " + test[0];586try {587Locale locale = Locale.forLanguageTag(test[1]);588assertEquals(msg, test[2], locale.toLanguageTag());589}590catch (IllegalArgumentException e) {591errln(msg + " caught exception: " + e);592}593}594595// duplicated extension are just ignored596Locale locale = Locale.forLanguageTag("und-d-aa-00-bb-01-D-AA-10-cc-11-c-1234");597assertEquals("extension", "aa-00-bb-01", locale.getExtension('d'));598assertEquals("extension c", "1234", locale.getExtension('c'));599600locale = Locale.forLanguageTag("und-U-ca-gregory-u-ca-japanese");601assertEquals("Unicode extension", "ca-gregory", locale.getExtension(Locale.UNICODE_LOCALE_EXTENSION));602603// redundant Unicode locale keys in an extension are ignored604locale = Locale.forLanguageTag("und-u-aa-000-bb-001-bB-002-cc-003-c-1234");605assertEquals("Unicode keywords", "aa-000-bb-001-cc-003", locale.getExtension(Locale.UNICODE_LOCALE_EXTENSION));606assertEquals("Duplicated Unicode locake key followed by an extension", "1234", locale.getExtension('c'));607}608609public void testGetDisplayScript() {610Locale latnLocale = Locale.forLanguageTag("und-latn");611Locale hansLocale = Locale.forLanguageTag("und-hans");612613Locale oldLocale = Locale.getDefault();614615Locale.setDefault(Locale.US);616assertEquals("latn US", "Latin", latnLocale.getDisplayScript());617assertEquals("hans US", "Simplified", hansLocale.getDisplayScript());618619Locale.setDefault(Locale.GERMANY);620assertEquals("latn DE", "Lateinisch", latnLocale.getDisplayScript());621assertEquals("hans DE", "Vereinfachte Chinesische Schrift", hansLocale.getDisplayScript());622623Locale.setDefault(oldLocale);624}625626public void testGetDisplayScriptWithLocale() {627Locale latnLocale = Locale.forLanguageTag("und-latn");628Locale hansLocale = Locale.forLanguageTag("und-hans");629630assertEquals("latn US", "Latin", latnLocale.getDisplayScript(Locale.US));631assertEquals("hans US", "Simplified", hansLocale.getDisplayScript(Locale.US));632633assertEquals("latn DE", "Lateinisch", latnLocale.getDisplayScript(Locale.GERMANY));634assertEquals("hans DE", "Vereinfachte Chinesische Schrift", hansLocale.getDisplayScript(Locale.GERMANY));635}636637public void testGetDisplayName() {638final Locale[] testLocales = {639Locale.ROOT,640new Locale("en"),641new Locale("en", "US"),642new Locale("", "US"),643new Locale("no", "NO", "NY"),644new Locale("", "", "NY"),645Locale.forLanguageTag("zh-Hans"),646Locale.forLanguageTag("zh-Hant"),647Locale.forLanguageTag("zh-Hans-CN"),648Locale.forLanguageTag("und-Hans"),649};650651final String[] displayNameEnglish = {652"",653"English",654"English (United States)",655"United States",656"Norwegian (Norway,Nynorsk)",657"Nynorsk",658"Chinese (Simplified)",659"Chinese (Traditional)",660"Chinese (Simplified,China)",661"Simplified",662};663664final String[] displayNameSimplifiedChinese = {665"",666"\u82f1\u6587",667"\u82f1\u6587 (\u7f8e\u56fd)",668"\u7f8e\u56fd",669"\u632a\u5a01\u6587 (\u632a\u5a01,Nynorsk)",670"Nynorsk",671"\u4e2d\u6587 (\u7b80\u4f53\u4e2d\u6587)",672"\u4e2d\u6587 (\u7e41\u4f53\u4e2d\u6587)",673"\u4e2d\u6587 (\u7b80\u4f53\u4e2d\u6587,\u4e2d\u56fd)",674"\u7b80\u4f53\u4e2d\u6587",675};676677for (int i = 0; i < testLocales.length; i++) {678Locale loc = testLocales[i];679assertEquals("English display name for " + loc.toLanguageTag(),680displayNameEnglish[i], loc.getDisplayName(Locale.ENGLISH));681assertEquals("Simplified Chinese display name for " + loc.toLanguageTag(),682displayNameSimplifiedChinese[i], loc.getDisplayName(Locale.CHINA));683}684}685686///687/// Builder tests688///689690public void testBuilderSetLocale() {691Builder builder = new Builder();692Builder lenientBuilder = new Builder();693694String languageTag = "en-Latn-US-NewYork-a-bb-ccc-u-co-japanese-x-y-z";695String target = "en-Latn-US-NewYork-a-bb-ccc-u-co-japanese-x-y-z";696697Locale locale = Locale.forLanguageTag(languageTag);698Locale result = lenientBuilder699.setLocale(locale)700.build();701assertEquals("long tag", target, result.toLanguageTag());702assertEquals("long tag", locale, result);703704// null is illegal705new BuilderNPE("locale") {706public void call() { b.setLocale(null); }707};708709// builder canonicalizes the three legacy locales:710// ja_JP_JP, th_TH_TH, no_NY_NO.711locale = builder.setLocale(new Locale("ja", "JP", "JP")).build();712assertEquals("ja_JP_JP languagetag", "ja-JP-u-ca-japanese", locale.toLanguageTag());713assertEquals("ja_JP_JP variant", "", locale.getVariant());714715locale = builder.setLocale(new Locale("th", "TH", "TH")).build();716assertEquals("th_TH_TH languagetag", "th-TH-u-nu-thai", locale.toLanguageTag());717assertEquals("th_TH_TH variant", "", locale.getVariant());718719locale = builder.setLocale(new Locale("no", "NO", "NY")).build();720assertEquals("no_NO_NY languagetag", "nn-NO", locale.toLanguageTag());721assertEquals("no_NO_NY language", "nn", locale.getLanguage());722assertEquals("no_NO_NY variant", "", locale.getVariant());723724// non-canonical, non-legacy locales are invalid725new BuilderILE("123_4567_89") {726public void call() {727b.setLocale(new Locale("123", "4567", "89"));728}729};730}731732public void testBuilderSetLanguageTag() {733String source = "eN-LaTn-Us-NewYork-A-Xx-B-Yy-X-1-2-3";734String target = "en-Latn-US-NewYork-a-xx-b-yy-x-1-2-3";735Builder builder = new Builder();736String result = builder737.setLanguageTag(source)738.build()739.toLanguageTag();740assertEquals("language", target, result);741742// redundant extensions cause a failure743new BuilderILE() { public void call() { b.setLanguageTag("und-a-xx-yy-b-ww-A-00-11-c-vv"); }};744745// redundant Unicode locale extension keys within an Unicode locale extension cause a failure746new BuilderILE() { public void call() { b.setLanguageTag("und-u-nu-thai-NU-chinese-xx-1234"); }};747}748749public void testBuilderSetLanguage() {750// language is normalized to lower case751String source = "eN";752String target = "en";753String defaulted = "";754Builder builder = new Builder();755String result = builder756.setLanguage(source)757.build()758.getLanguage();759assertEquals("en", target, result);760761// setting with empty resets762result = builder763.setLanguage(target)764.setLanguage("")765.build()766.getLanguage();767assertEquals("empty", defaulted, result);768769// setting with null resets too770result = builder771.setLanguage(target)772.setLanguage(null)773.build()774.getLanguage();775assertEquals("null", defaulted, result);776777// language codes must be 2-8 alpha778// for forwards compatibility, 4-alpha and 5-8 alpha (registered)779// languages are accepted syntax780new BuilderILE("q", "abcdefghi", "13") { public void call() { b.setLanguage(arg); }};781782// language code validation is NOT performed, any 2-8-alpha passes783assertNotNull("2alpha", builder.setLanguage("zz").build());784assertNotNull("8alpha", builder.setLanguage("abcdefgh").build());785786// three-letter language codes are NOT canonicalized to two-letter787result = builder788.setLanguage("eng")789.build()790.getLanguage();791assertEquals("eng", "eng", result);792}793794public void testBuilderSetScript() {795// script is normalized to title case796String source = "lAtN";797String target = "Latn";798String defaulted = "";799Builder builder = new Builder();800String result = builder801.setScript(source)802.build()803.getScript();804assertEquals("script", target, result);805806// setting with empty resets807result = builder808.setScript(target)809.setScript("")810.build()811.getScript();812assertEquals("empty", defaulted, result);813814// settting with null also resets815result = builder816.setScript(target)817.setScript(null)818.build()819.getScript();820assertEquals("null", defaulted, result);821822// ill-formed script codes throw IAE823// must be 4alpha824new BuilderILE("abc", "abcde", "l3tn") { public void call() { b.setScript(arg); }};825826// script code validation is NOT performed, any 4-alpha passes827assertEquals("4alpha", "Wxyz", builder.setScript("wxyz").build().getScript());828}829830public void testBuilderSetRegion() {831// region is normalized to upper case832String source = "uS";833String target = "US";834String defaulted = "";835Builder builder = new Builder();836String result = builder837.setRegion(source)838.build()839.getCountry();840assertEquals("us", target, result);841842// setting with empty resets843result = builder844.setRegion(target)845.setRegion("")846.build()847.getCountry();848assertEquals("empty", defaulted, result);849850// setting with null also resets851result = builder852.setRegion(target)853.setRegion(null)854.build()855.getCountry();856assertEquals("null", defaulted, result);857858// ill-formed region codes throw IAE859// 2 alpha or 3 numeric860new BuilderILE("q", "abc", "12", "1234", "a3", "12a") { public void call() { b.setRegion(arg); }};861862// region code validation is NOT performed, any 2-alpha or 3-digit passes863assertEquals("2alpha", "ZZ", builder.setRegion("ZZ").build().getCountry());864assertEquals("3digit", "000", builder.setRegion("000").build().getCountry());865}866867public void testBuilderSetVariant() {868// Variant case is not normalized in lenient variant mode869String source = "NewYork";870String target = source;871String defaulted = "";872Builder builder = new Builder();873String result = builder874.setVariant(source)875.build()876.getVariant();877assertEquals("NewYork", target, result);878879result = builder880.setVariant("NeWeR_YoRkEr")881.build()882.toLanguageTag();883assertEquals("newer yorker", "und-NeWeR-YoRkEr", result);884885// subtags of variant are NOT reordered886result = builder887.setVariant("zzzzz_yyyyy_xxxxx")888.build()889.getVariant();890assertEquals("zyx", "zzzzz_yyyyy_xxxxx", result);891892// setting to empty resets893result = builder894.setVariant(target)895.setVariant("")896.build()897.getVariant();898assertEquals("empty", defaulted, result);899900// setting to null also resets901result = builder902.setVariant(target)903.setVariant(null)904.build()905.getVariant();906assertEquals("null", defaulted, result);907908// ill-formed variants throw IAE909// digit followed by 3-7 characters, or alpha followed by 4-8 characters.910new BuilderILE("abcd", "abcdefghi", "1ab", "1abcdefgh") { public void call() { b.setVariant(arg); }};911912// 4 characters is ok as long as the first is a digit913assertEquals("digit+3alpha", "1abc", builder.setVariant("1abc").build().getVariant());914915// all subfields must conform916new BuilderILE("abcde-fg") { public void call() { b.setVariant(arg); }};917}918919public void testBuilderSetExtension() {920// upper case characters are normalized to lower case921final char sourceKey = 'a';922final String sourceValue = "aB-aBcdefgh-12-12345678";923String target = "ab-abcdefgh-12-12345678";924Builder builder = new Builder();925String result = builder926.setExtension(sourceKey, sourceValue)927.build()928.getExtension(sourceKey);929assertEquals("extension", target, result);930931// setting with empty resets932result = builder933.setExtension(sourceKey, sourceValue)934.setExtension(sourceKey, "")935.build()936.getExtension(sourceKey);937assertEquals("empty", null, result);938939// setting with null also resets940result = builder941.setExtension(sourceKey, sourceValue)942.setExtension(sourceKey, null)943.build()944.getExtension(sourceKey);945assertEquals("null", null, result);946947// ill-formed extension keys throw IAE948// must be in [0-9a-ZA-Z]949new BuilderILE("$") { public void call() { b.setExtension('$', sourceValue); }};950951// each segment of value must be 2-8 alphanum952new BuilderILE("ab-cd-123456789") { public void call() { b.setExtension(sourceKey, arg); }};953954// no multiple hyphens.955new BuilderILE("ab--cd") { public void call() { b.setExtension(sourceKey, arg); }};956957// locale extension key has special handling958Locale locale = builder959.setExtension('u', "co-japanese")960.build();961assertEquals("locale extension", "japanese", locale.getUnicodeLocaleType("co"));962963// locale extension has same behavior with set locale keyword964Locale locale2 = builder965.setUnicodeLocaleKeyword("co", "japanese")966.build();967assertEquals("locales with extension", locale, locale2);968969// setting locale extension overrides all previous calls to setLocaleKeyword970Locale locale3 = builder971.setExtension('u', "xxx-nu-thai")972.build();973assertEquals("remove co", null, locale3.getUnicodeLocaleType("co"));974assertEquals("override thai", "thai", locale3.getUnicodeLocaleType("nu"));975assertEquals("override attribute", 1, locale3.getUnicodeLocaleAttributes().size());976977// setting locale keyword extends values already set by the locale extension978Locale locale4 = builder979.setUnicodeLocaleKeyword("co", "japanese")980.build();981assertEquals("extend", "japanese", locale4.getUnicodeLocaleType("co"));982assertEquals("extend", "thai", locale4.getUnicodeLocaleType("nu"));983984// locale extension subtags are reordered985result = builder986.clear()987.setExtension('u', "456-123-zz-123-yy-456-xx-789")988.build()989.toLanguageTag();990assertEquals("reorder", "und-u-123-456-xx-789-yy-456-zz-123", result);991992// multiple keyword types993result = builder994.clear()995.setExtension('u', "nu-thai-foobar")996.build()997.getUnicodeLocaleType("nu");998assertEquals("multiple types", "thai-foobar", result);9991000// redundant locale extensions are ignored1001result = builder1002.clear()1003.setExtension('u', "nu-thai-NU-chinese-xx-1234")1004.build()1005.toLanguageTag();1006assertEquals("duplicate keys", "und-u-nu-thai-xx-1234", result);1007}10081009public void testBuilderAddUnicodeLocaleAttribute() {1010Builder builder = new Builder();1011Locale locale = builder1012.addUnicodeLocaleAttribute("def")1013.addUnicodeLocaleAttribute("abc")1014.build();10151016Set<String> uattrs = locale.getUnicodeLocaleAttributes();1017assertEquals("number of attributes", 2, uattrs.size());1018assertTrue("attribute abc", uattrs.contains("abc"));1019assertTrue("attribute def", uattrs.contains("def"));10201021// remove attribute1022locale = builder.removeUnicodeLocaleAttribute("xxx")1023.build();10241025assertEquals("remove bogus", 2, uattrs.size());10261027// add duplicate1028locale = builder.addUnicodeLocaleAttribute("abc")1029.build();1030assertEquals("add duplicate", 2, uattrs.size());10311032// null attribute throws NPE1033new BuilderNPE("null attribute") { public void call() { b.addUnicodeLocaleAttribute(null); }};1034new BuilderNPE("null attribute removal") { public void call() { b.removeUnicodeLocaleAttribute(null); }};10351036// illformed attribute throws IllformedLocaleException1037new BuilderILE("invalid attribute") { public void call() { b.addUnicodeLocaleAttribute("ca"); }};1038}10391040public void testBuildersetUnicodeLocaleKeyword() {1041// Note: most behavior is tested in testBuilderSetExtension1042Builder builder = new Builder();1043Locale locale = builder1044.setUnicodeLocaleKeyword("co", "japanese")1045.setUnicodeLocaleKeyword("nu", "thai")1046.build();1047assertEquals("co", "japanese", locale.getUnicodeLocaleType("co"));1048assertEquals("nu", "thai", locale.getUnicodeLocaleType("nu"));1049assertEquals("keys", 2, locale.getUnicodeLocaleKeys().size());10501051// can clear a keyword by setting to null, others remain1052String result = builder1053.setUnicodeLocaleKeyword("co", null)1054.build()1055.toLanguageTag();1056assertEquals("empty co", "und-u-nu-thai", result);10571058// locale keyword extension goes when all keywords are gone1059result = builder1060.setUnicodeLocaleKeyword("nu", null)1061.build()1062.toLanguageTag();1063assertEquals("empty nu", "und", result);10641065// locale keywords are ordered independent of order of addition1066result = builder1067.setUnicodeLocaleKeyword("zz", "012")1068.setUnicodeLocaleKeyword("aa", "345")1069.build()1070.toLanguageTag();1071assertEquals("reordered", "und-u-aa-345-zz-012", result);10721073// null keyword throws NPE1074new BuilderNPE("keyword") { public void call() { b.setUnicodeLocaleKeyword(null, "thai"); }};10751076// well-formed keywords are two alphanum1077new BuilderILE("a", "abc") { public void call() { b.setUnicodeLocaleKeyword(arg, "value"); }};10781079// well-formed values are 3-8 alphanum1080new BuilderILE("ab", "abcdefghi") { public void call() { b.setUnicodeLocaleKeyword("ab", arg); }};1081}10821083public void testBuilderPrivateUseExtension() {1084// normalizes hyphens to underscore, case to lower1085String source = "c-B-a";1086String target = "c-b-a";1087Builder builder = new Builder();1088String result = builder1089.setExtension(Locale.PRIVATE_USE_EXTENSION, source)1090.build()1091.getExtension(Locale.PRIVATE_USE_EXTENSION);1092assertEquals("abc", target, result);10931094// multiple hyphens are ill-formed1095new BuilderILE("a--b") { public void call() { b.setExtension(Locale.PRIVATE_USE_EXTENSION, arg); }};1096}10971098public void testBuilderClear() {1099String monster = "en-latn-US-NewYork-a-bb-cc-u-co-japanese-x-z-y-x-x";1100Builder builder = new Builder();1101Locale locale = Locale.forLanguageTag(monster);1102String result = builder1103.setLocale(locale)1104.clear()1105.build()1106.toLanguageTag();1107assertEquals("clear", "und", result);1108}11091110public void testBuilderRemoveUnicodeAttribute() {1111// tested in testBuilderAddUnicodeAttribute1112}11131114public void testBuilderBuild() {1115// tested in other test methods1116}11171118public void testSerialize() {1119final Locale[] testLocales = {1120Locale.ROOT,1121new Locale("en"),1122new Locale("en", "US"),1123new Locale("en", "US", "Win"),1124new Locale("en", "US", "Win_XP"),1125new Locale("ja", "JP"),1126new Locale("ja", "JP", "JP"),1127new Locale("th", "TH"),1128new Locale("th", "TH", "TH"),1129new Locale("no", "NO"),1130new Locale("nb", "NO"),1131new Locale("nn", "NO"),1132new Locale("no", "NO", "NY"),1133new Locale("nn", "NO", "NY"),1134new Locale("he", "IL"),1135new Locale("he", "IL", "var"),1136new Locale("Language", "Country", "Variant"),1137new Locale("", "US"),1138new Locale("", "", "Java"),1139Locale.forLanguageTag("en-Latn-US"),1140Locale.forLanguageTag("zh-Hans"),1141Locale.forLanguageTag("zh-Hant-TW"),1142Locale.forLanguageTag("ja-JP-u-ca-japanese"),1143Locale.forLanguageTag("und-Hant"),1144Locale.forLanguageTag("und-a-123-456"),1145Locale.forLanguageTag("en-x-java"),1146Locale.forLanguageTag("th-TH-u-ca-buddist-nu-thai-x-lvariant-TH"),1147};11481149for (Locale locale : testLocales) {1150try {1151// write1152ByteArrayOutputStream bos = new ByteArrayOutputStream();1153ObjectOutputStream oos = new ObjectOutputStream(bos);1154oos.writeObject(locale);11551156// read1157ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());1158ObjectInputStream ois = new ObjectInputStream(bis);1159Object o = ois.readObject();11601161assertEquals("roundtrip " + locale, locale, o);1162} catch (Exception e) {1163errln(locale + " encountered exception:" + e.getLocalizedMessage());1164}1165}1166}11671168public void testDeserialize6() {1169final String TESTFILEPREFIX = "java6locale_";11701171File dataDir = null;1172String dataDirName = System.getProperty("serialized.data.dir");1173if (dataDirName == null) {1174URL resdirUrl = getClass().getClassLoader().getResource("serialized");1175if (resdirUrl != null) {1176try {1177dataDir = new File(resdirUrl.toURI());1178} catch (URISyntaxException urie) {1179}1180}1181} else {1182dataDir = new File(dataDirName);1183}11841185if (dataDir == null) {1186errln("'dataDir' is null. serialized.data.dir Property value is "+dataDirName);1187return;1188} else if (!dataDir.isDirectory()) {1189errln("'dataDir' is not a directory. dataDir: "+dataDir.toString());1190return;1191}11921193File[] files = dataDir.listFiles();1194for (File testfile : files) {1195if (testfile.isDirectory()) {1196continue;1197}1198String name = testfile.getName();1199if (!name.startsWith(TESTFILEPREFIX)) {1200continue;1201}1202Locale locale;1203String locStr = name.substring(TESTFILEPREFIX.length());1204if (locStr.equals("ROOT")) {1205locale = Locale.ROOT;1206} else {1207String[] fields = locStr.split("_", 3);1208String lang = fields[0];1209String country = (fields.length >= 2) ? fields[1] : "";1210String variant = (fields.length == 3) ? fields[2] : "";1211locale = new Locale(lang, country, variant);1212}12131214// deserialize1215try (FileInputStream fis = new FileInputStream(testfile);1216ObjectInputStream ois = new ObjectInputStream(fis))1217{1218Object o = ois.readObject();1219assertEquals("Deserialize Java 6 Locale " + locale, o, locale);1220} catch (Exception e) {1221errln("Exception while reading " + testfile.getAbsolutePath() + " - " + e.getMessage());1222}1223}1224}12251226public void testBug7002320() {1227// forLanguageTag() and Builder.setLanguageTag(String)1228// should add a location extension for following two cases.1229//1230// 1. language/country are "ja"/"JP" and the resolved variant (x-lvariant-*)1231// is exactly "JP" and no BCP 47 extensions are available, then add1232// a Unicode locale extension "ca-japanese".1233// 2. language/country are "th"/"TH" and the resolved variant is exactly1234// "TH" and no BCP 47 extensions are available, then add a Unicode locale1235// extension "nu-thai".1236//1237String[][] testdata = {1238{"ja-JP-x-lvariant-JP", "ja-JP-u-ca-japanese-x-lvariant-JP"}, // special case 11239{"ja-JP-x-lvariant-JP-XXX"},1240{"ja-JP-u-ca-japanese-x-lvariant-JP"},1241{"ja-JP-u-ca-gregory-x-lvariant-JP"},1242{"ja-JP-u-cu-jpy-x-lvariant-JP"},1243{"ja-x-lvariant-JP"},1244{"th-TH-x-lvariant-TH", "th-TH-u-nu-thai-x-lvariant-TH"}, // special case 21245{"th-TH-u-nu-thai-x-lvariant-TH"},1246{"en-US-x-lvariant-JP"},1247};12481249Builder bldr = new Builder();12501251for (String[] data : testdata) {1252String in = data[0];1253String expected = (data.length == 1) ? data[0] : data[1];12541255// forLanguageTag1256Locale loc = Locale.forLanguageTag(in);1257String out = loc.toLanguageTag();1258assertEquals("Language tag roundtrip by forLanguageTag with input: " + in, expected, out);12591260// setLanguageTag1261bldr.clear();1262bldr.setLanguageTag(in);1263loc = bldr.build();1264out = loc.toLanguageTag();1265assertEquals("Language tag roundtrip by Builder.setLanguageTag with input: " + in, expected, out);1266}1267}12681269public void testBug7023613() {1270String[][] testdata = {1271{"en-Latn", "en__#Latn"},1272{"en-u-ca-japanese", "en__#u-ca-japanese"},1273};12741275for (String[] data : testdata) {1276String in = data[0];1277String expected = (data.length == 1) ? data[0] : data[1];12781279Locale loc = Locale.forLanguageTag(in);1280String out = loc.toString();1281assertEquals("Empty country field with non-empty script/extension with input: " + in, expected, out);1282}1283}12841285/*1286* 7033504: (lc) incompatible behavior change for ja_JP_JP and th_TH_TH locales1287*/1288public void testBug7033504() {1289checkCalendar(new Locale("ja", "JP", "jp"), "java.util.GregorianCalendar");1290checkCalendar(new Locale("ja", "jp", "jp"), "java.util.GregorianCalendar");1291checkCalendar(new Locale("ja", "JP", "JP"), "java.util.JapaneseImperialCalendar");1292checkCalendar(new Locale("ja", "jp", "JP"), "java.util.JapaneseImperialCalendar");1293checkCalendar(Locale.forLanguageTag("en-u-ca-japanese"),1294"java.util.JapaneseImperialCalendar");12951296checkDigit(new Locale("th", "TH", "th"), '0');1297checkDigit(new Locale("th", "th", "th"), '0');1298checkDigit(new Locale("th", "TH", "TH"), '\u0e50');1299checkDigit(new Locale("th", "TH", "TH"), '\u0e50');1300checkDigit(Locale.forLanguageTag("en-u-nu-thai"), '\u0e50');1301}13021303private void checkCalendar(Locale loc, String expected) {1304Calendar cal = Calendar.getInstance(loc);1305assertEquals("Wrong calendar", expected, cal.getClass().getName());1306}13071308private void checkDigit(Locale loc, Character expected) {1309DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(loc);1310Character zero = dfs.getZeroDigit();1311assertEquals("Wrong digit zero char", expected, zero);1312}13131314///1315/// utility asserts1316///13171318private void assertTrue(String msg, boolean v) {1319if (!v) {1320errln(msg + ": expected true");1321}1322}13231324private void assertFalse(String msg, boolean v) {1325if (v) {1326errln(msg + ": expected false");1327}1328}13291330private void assertEquals(String msg, Object e, Object v) {1331if (e == null ? v != null : !e.equals(v)) {1332if (e != null) {1333e = "'" + e + "'";1334}1335if (v != null) {1336v = "'" + v + "'";1337}1338errln(msg + ": expected " + e + " but got " + v);1339}1340}13411342private void assertNotEquals(String msg, Object e, Object v) {1343if (e == null ? v == null : e.equals(v)) {1344if (e != null) {1345e = "'" + e + "'";1346}1347errln(msg + ": expected not equal " + e);1348}1349}13501351private void assertNull(String msg, Object o) {1352if (o != null) {1353errln(msg + ": expected null but got '" + o + "'");1354}1355}13561357private void assertNotNull(String msg, Object o) {1358if (o == null) {1359errln(msg + ": expected non null");1360}1361}13621363// not currently used, might get rid of exceptions from the API1364private abstract class ExceptionTest {1365private final Class<? extends Exception> exceptionClass;13661367ExceptionTest(Class<? extends Exception> exceptionClass) {1368this.exceptionClass = exceptionClass;1369}13701371public void run() {1372String failMsg = null;1373try {1374call();1375failMsg = "expected " + exceptionClass.getName() + " but no exception thrown.";1376}1377catch (Exception e) {1378if (!exceptionClass.isAssignableFrom(e.getClass())) {1379failMsg = "expected " + exceptionClass.getName() + " but caught " + e;1380}1381}1382if (failMsg != null) {1383String msg = message();1384msg = msg == null ? "" : msg + " ";1385errln(msg + failMsg);1386}1387}13881389public String message() {1390return null;1391}13921393public abstract void call();1394}13951396private abstract class ExpectNPE extends ExceptionTest {1397ExpectNPE() {1398super(NullPointerException.class);1399run();1400}1401}14021403private abstract class BuilderNPE extends ExceptionTest {1404protected final String msg;1405protected final Builder b = new Builder();14061407BuilderNPE(String msg) {1408super(NullPointerException.class);14091410this.msg = msg;14111412run();1413}14141415public String message() {1416return msg;1417}1418}14191420private abstract class ExpectIAE extends ExceptionTest {1421ExpectIAE() {1422super(IllegalArgumentException.class);1423run();1424}1425}14261427private abstract class BuilderILE extends ExceptionTest {1428protected final String[] args;1429protected final Builder b = new Builder();14301431protected String arg; // mutates during call14321433BuilderILE(String... args) {1434super(IllformedLocaleException.class);14351436this.args = args;14371438run();1439}14401441public void run() {1442for (String arg : args) {1443this.arg = arg;1444super.run();1445}1446}14471448public String message() {1449return "arg: '" + arg + "'";1450}1451}1452}145314541455