Home | History | Annotate | Download | only in util
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html#License
      3 /*
      4  ******************************************************************************
      5  * Copyright (C) 2003-2016, International Business Machines Corporation and
      6  * others. All Rights Reserved.
      7  ******************************************************************************
      8  */
      9 
     10 package com.ibm.icu.util;
     11 
     12 import java.io.Serializable;
     13 import java.lang.reflect.InvocationTargetException;
     14 import java.lang.reflect.Method;
     15 import java.text.ParseException;
     16 import java.util.Iterator;
     17 import java.util.List;
     18 import java.util.Locale;
     19 import java.util.Map;
     20 import java.util.Map.Entry;
     21 import java.util.MissingResourceException;
     22 import java.util.Set;
     23 import java.util.TreeMap;
     24 import java.util.TreeSet;
     25 
     26 import com.ibm.icu.impl.CacheBase;
     27 import com.ibm.icu.impl.ICUData;
     28 import com.ibm.icu.impl.ICUResourceBundle;
     29 import com.ibm.icu.impl.ICUResourceTableAccess;
     30 import com.ibm.icu.impl.LocaleIDParser;
     31 import com.ibm.icu.impl.LocaleIDs;
     32 import com.ibm.icu.impl.LocaleUtility;
     33 import com.ibm.icu.impl.SoftCache;
     34 import com.ibm.icu.impl.locale.AsciiUtil;
     35 import com.ibm.icu.impl.locale.BaseLocale;
     36 import com.ibm.icu.impl.locale.Extension;
     37 import com.ibm.icu.impl.locale.InternalLocaleBuilder;
     38 import com.ibm.icu.impl.locale.KeyTypeData;
     39 import com.ibm.icu.impl.locale.LanguageTag;
     40 import com.ibm.icu.impl.locale.LocaleExtensions;
     41 import com.ibm.icu.impl.locale.LocaleSyntaxException;
     42 import com.ibm.icu.impl.locale.ParseStatus;
     43 import com.ibm.icu.impl.locale.UnicodeLocaleExtension;
     44 import com.ibm.icu.lang.UScript;
     45 import com.ibm.icu.text.LocaleDisplayNames;
     46 import com.ibm.icu.text.LocaleDisplayNames.DialectHandling;
     47 
     48 /**
     49  * {@icuenhanced java.util.Locale}.{@icu _usage_}
     50  *
     51  * A class analogous to {@link java.util.Locale} that provides additional
     52  * support for ICU protocol.  In ICU 3.0 this class is enhanced to support
     53  * RFC 3066 language identifiers.
     54  *
     55  * <p>Many classes and services in ICU follow a factory idiom, in
     56  * which a factory method or object responds to a client request with
     57  * an object.  The request includes a locale (the <i>requested</i>
     58  * locale), and the returned object is constructed using data for that
     59  * locale.  The system may lack data for the requested locale, in
     60  * which case the locale fallback mechanism will be invoked until a
     61  * populated locale is found (the <i>valid</i> locale).  Furthermore,
     62  * even when a populated locale is found (the <i>valid</i> locale),
     63  * further fallback may be required to reach a locale containing the
     64  * specific data required by the service (the <i>actual</i> locale).
     65  *
     66  * <p>ULocale performs <b>'normalization'</b> and <b>'canonicalization'</b> of locale ids.
     67  * Normalization 'cleans up' ICU locale ids as follows:
     68  * <ul>
     69  * <li>language, script, country, variant, and keywords are properly cased<br>
     70  * (lower, title, upper, upper, and lower case respectively)</li>
     71  * <li>hyphens used as separators are converted to underscores</li>
     72  * <li>three-letter language and country ids are converted to two-letter
     73  * equivalents where available</li>
     74  * <li>surrounding spaces are removed from keywords and values</li>
     75  * <li>if there are multiple keywords, they are put in sorted order</li>
     76  * </ul>
     77  * Canonicalization additionally performs the following:
     78  * <ul>
     79  * <li>POSIX ids are converted to ICU format IDs</li>
     80  * <li>'grandfathered' 3066 ids are converted to ICU standard form</li>
     81  * <li>'PREEURO' and 'EURO' variants are converted to currency keyword form,
     82  * with the currency
     83  * id appropriate to the country of the locale (for PREEURO) or EUR (for EURO).
     84  * </ul>
     85  * All ULocale constructors automatically normalize the locale id.  To handle
     86  * POSIX ids, <code>canonicalize</code> can be called to convert the id
     87  * to canonical form, or the <code>canonicalInstance</code> factory method
     88  * can be called.
     89  *
     90  * <p>This class provides selectors {@link #VALID_LOCALE} and {@link
     91  * #ACTUAL_LOCALE} intended for use in methods named
     92  * <tt>getLocale()</tt>.  These methods exist in several ICU classes,
     93  * including {@link com.ibm.icu.util.Calendar}, {@link
     94  * com.ibm.icu.util.Currency}, {@link com.ibm.icu.text.UFormat},
     95  * {@link com.ibm.icu.text.BreakIterator},
     96  * {@link com.ibm.icu.text.Collator},
     97  * {@link com.ibm.icu.text.DateFormatSymbols}, and {@link
     98  * com.ibm.icu.text.DecimalFormatSymbols} and their subclasses, if
     99  * any. Once an object of one of these classes has been created,
    100  * <tt>getLocale()</tt> may be called on it to determine the valid and
    101  * actual locale arrived at during the object's construction.
    102  *
    103  * <p>Note: The <i>actual</i> locale is returned correctly, but the <i>valid</i>
    104  * locale is not, in most cases.
    105  *
    106  * @see java.util.Locale
    107  * @author weiv
    108  * @author Alan Liu
    109  * @author Ram Viswanadha
    110  * @stable ICU 2.8
    111  */
    112 @SuppressWarnings("javadoc")    // com.ibm.icu.text.Collator is in another project
    113 public final class ULocale implements Serializable, Comparable<ULocale> {
    114     // using serialver from jdk1.4.2_05
    115     private static final long serialVersionUID = 3715177670352309217L;
    116 
    117     private static CacheBase<String, String, Void> nameCache = new SoftCache<String, String, Void>() {
    118         @Override
    119         protected String createInstance(String tmpLocaleID, Void unused) {
    120             return new LocaleIDParser(tmpLocaleID).getName();
    121         }
    122     };
    123 
    124     /**
    125      * Useful constant for language.
    126      * @stable ICU 3.0
    127      */
    128     public static final ULocale ENGLISH = new ULocale("en", Locale.ENGLISH);
    129 
    130     /**
    131      * Useful constant for language.
    132      * @stable ICU 3.0
    133      */
    134     public static final ULocale FRENCH = new ULocale("fr", Locale.FRENCH);
    135 
    136     /**
    137      * Useful constant for language.
    138      * @stable ICU 3.0
    139      */
    140     public static final ULocale GERMAN = new ULocale("de", Locale.GERMAN);
    141 
    142     /**
    143      * Useful constant for language.
    144      * @stable ICU 3.0
    145      */
    146     public static final ULocale ITALIAN = new ULocale("it", Locale.ITALIAN);
    147 
    148     /**
    149      * Useful constant for language.
    150      * @stable ICU 3.0
    151      */
    152     public static final ULocale JAPANESE = new ULocale("ja", Locale.JAPANESE);
    153 
    154     /**
    155      * Useful constant for language.
    156      * @stable ICU 3.0
    157      */
    158     public static final ULocale KOREAN = new ULocale("ko", Locale.KOREAN);
    159 
    160     /**
    161      * Useful constant for language.
    162      * @stable ICU 3.0
    163      */
    164     public static final ULocale CHINESE = new ULocale("zh", Locale.CHINESE);
    165 
    166 
    167     // Special note about static initializer for
    168     //   - SIMPLIFIED_CHINESE
    169     //   - TRADTIONAL_CHINESE
    170     //   - CHINA
    171     //   - TAIWAN
    172     //
    173     // Equivalent JDK Locale for ULocale.SIMPLIFIED_CHINESE is different
    174     // by JRE version. JRE 7 or later supports a script tag "Hans", while
    175     // JRE 6 or older does not. JDK's Locale.SIMPLIFIED_CHINESE is actually
    176     // zh_CN, not zh_Hans. This is same in Java 7 or later versions.
    177     //
    178     // ULocale#toLocale() implementation create a Locale with a script tag.
    179     // When a new ULocale is constructed with the single arg
    180     // constructor, the volatile field 'Locale locale' is initialized by
    181     // #toLocale() method.
    182     //
    183     // Because we cannot hardcode corresponding JDK Locale representation below,
    184     // SIMPLIFIED_CHINESE is constructed without JDK Locale argument, and
    185     // #toLocale() is used for resolving the best matching JDK Locale at runtime.
    186     //
    187     // The same thing applies to TRADITIONAL_CHINESE.
    188 
    189     /**
    190      * Useful constant for language.
    191      * @stable ICU 3.0
    192      */
    193     public static final ULocale SIMPLIFIED_CHINESE = new ULocale("zh_Hans");
    194 
    195 
    196     /**
    197      * Useful constant for language.
    198      * @stable ICU 3.0
    199      */
    200     public static final ULocale TRADITIONAL_CHINESE = new ULocale("zh_Hant");
    201 
    202     /**
    203      * Useful constant for country/region.
    204      * @stable ICU 3.0
    205      */
    206     public static final ULocale FRANCE = new ULocale("fr_FR", Locale.FRANCE);
    207 
    208     /**
    209      * Useful constant for country/region.
    210      * @stable ICU 3.0
    211      */
    212     public static final ULocale GERMANY = new ULocale("de_DE", Locale.GERMANY);
    213 
    214     /**
    215      * Useful constant for country/region.
    216      * @stable ICU 3.0
    217      */
    218     public static final ULocale ITALY = new ULocale("it_IT", Locale.ITALY);
    219 
    220     /**
    221      * Useful constant for country/region.
    222      * @stable ICU 3.0
    223      */
    224     public static final ULocale JAPAN = new ULocale("ja_JP", Locale.JAPAN);
    225 
    226     /**
    227      * Useful constant for country/region.
    228      * @stable ICU 3.0
    229      */
    230     public static final ULocale KOREA = new ULocale("ko_KR", Locale.KOREA);
    231 
    232     /**
    233      * Useful constant for country/region.
    234      * @stable ICU 3.0
    235      */
    236     public static final ULocale CHINA = new ULocale("zh_Hans_CN");
    237 
    238     /**
    239      * Useful constant for country/region.
    240      * @stable ICU 3.0
    241      */
    242     public static final ULocale PRC = CHINA;
    243 
    244     /**
    245      * Useful constant for country/region.
    246      * @stable ICU 3.0
    247      */
    248     public static final ULocale TAIWAN = new ULocale("zh_Hant_TW");
    249 
    250     /**
    251      * Useful constant for country/region.
    252      * @stable ICU 3.0
    253      */
    254     public static final ULocale UK = new ULocale("en_GB", Locale.UK);
    255 
    256     /**
    257      * Useful constant for country/region.
    258      * @stable ICU 3.0
    259      */
    260     public static final ULocale US = new ULocale("en_US", Locale.US);
    261 
    262     /**
    263      * Useful constant for country/region.
    264      * @stable ICU 3.0
    265      */
    266     public static final ULocale CANADA = new ULocale("en_CA", Locale.CANADA);
    267 
    268     /**
    269      * Useful constant for country/region.
    270      * @stable ICU 3.0
    271      */
    272     public static final ULocale CANADA_FRENCH = new ULocale("fr_CA", Locale.CANADA_FRENCH);
    273 
    274     /**
    275      * Handy constant.
    276      */
    277     private static final String EMPTY_STRING = "";
    278 
    279     // Used in both ULocale and LocaleIDParser, so moved up here.
    280     private static final char UNDERSCORE            = '_';
    281 
    282     // default empty locale
    283     private static final Locale EMPTY_LOCALE = new Locale("", "");
    284 
    285     // special keyword key for Unicode locale attributes
    286     private static final String LOCALE_ATTRIBUTE_KEY = "attribute";
    287 
    288     /**
    289      * The root ULocale.
    290      * @stable ICU 2.8
    291      */
    292     public static final ULocale ROOT = new ULocale("", EMPTY_LOCALE);
    293 
    294     /**
    295      * Enum for locale categories. These locale categories are used to get/set the default locale for
    296      * the specific functionality represented by the category.
    297      * @stable ICU 49
    298      */
    299     public enum Category {
    300         /**
    301          * Category used to represent the default locale for displaying user interfaces.
    302          * @stable ICU 49
    303          */
    304         DISPLAY,
    305         /**
    306          * Category used to represent the default locale for formatting date, number and/or currency.
    307          * @stable ICU 49
    308          */
    309         FORMAT
    310     }
    311 
    312     private static final SoftCache<Locale, ULocale, Void> CACHE = new SoftCache<Locale, ULocale, Void>() {
    313         @Override
    314         protected ULocale createInstance(Locale key, Void unused) {
    315             return JDKLocaleHelper.toULocale(key);
    316         }
    317     };
    318 
    319     /**
    320      * Cache the locale.
    321      */
    322     private transient volatile Locale locale;
    323 
    324     /**
    325      * The raw localeID that we were passed in.
    326      */
    327     private String localeID;
    328 
    329     /**
    330      * Cache the locale data container fields.
    331      * In future, we want to use them as the primary locale identifier storage.
    332      */
    333     private transient volatile BaseLocale baseLocale;
    334     private transient volatile LocaleExtensions extensions;
    335 
    336     /**
    337      * This table lists pairs of locale ids for canonicalization.  The
    338      * The 1st item is the normalized id. The 2nd item is the
    339      * canonicalized id. The 3rd is the keyword. The 4th is the keyword value.
    340      */
    341     private static String[][] CANONICALIZE_MAP = {
    342         { "C",              "en_US_POSIX", null, null }, /* POSIX name */
    343         { "art_LOJBAN",     "jbo", null, null }, /* registered name */
    344         { "az_AZ_CYRL",     "az_Cyrl_AZ", null, null }, /* .NET name */
    345         { "az_AZ_LATN",     "az_Latn_AZ", null, null }, /* .NET name */
    346         { "ca_ES_PREEURO",  "ca_ES", "currency", "ESP" },
    347         { "cel_GAULISH",    "cel__GAULISH", null, null }, /* registered name */
    348         { "de_1901",        "de__1901", null, null }, /* registered name */
    349         { "de_1906",        "de__1906", null, null }, /* registered name */
    350         { "de__PHONEBOOK",  "de", "collation", "phonebook" }, /* Old ICU name */
    351         { "de_AT_PREEURO",  "de_AT", "currency", "ATS" },
    352         { "de_DE_PREEURO",  "de_DE", "currency", "DEM" },
    353         { "de_LU_PREEURO",  "de_LU", "currency", "EUR" },
    354         { "el_GR_PREEURO",  "el_GR", "currency", "GRD" },
    355         { "en_BOONT",       "en__BOONT", null, null }, /* registered name */
    356         { "en_SCOUSE",      "en__SCOUSE", null, null }, /* registered name */
    357         { "en_BE_PREEURO",  "en_BE", "currency", "BEF" },
    358         { "en_IE_PREEURO",  "en_IE", "currency", "IEP" },
    359         { "es__TRADITIONAL", "es", "collation", "traditional" }, /* Old ICU name */
    360         { "es_ES_PREEURO",  "es_ES", "currency", "ESP" },
    361         { "eu_ES_PREEURO",  "eu_ES", "currency", "ESP" },
    362         { "fi_FI_PREEURO",  "fi_FI", "currency", "FIM" },
    363         { "fr_BE_PREEURO",  "fr_BE", "currency", "BEF" },
    364         { "fr_FR_PREEURO",  "fr_FR", "currency", "FRF" },
    365         { "fr_LU_PREEURO",  "fr_LU", "currency", "LUF" },
    366         { "ga_IE_PREEURO",  "ga_IE", "currency", "IEP" },
    367         { "gl_ES_PREEURO",  "gl_ES", "currency", "ESP" },
    368         { "hi__DIRECT",     "hi", "collation", "direct" }, /* Old ICU name */
    369         { "it_IT_PREEURO",  "it_IT", "currency", "ITL" },
    370         { "ja_JP_TRADITIONAL", "ja_JP", "calendar", "japanese" },
    371       //{ "nb_NO_NY",       "nn_NO", null, null },
    372         { "nl_BE_PREEURO",  "nl_BE", "currency", "BEF" },
    373         { "nl_NL_PREEURO",  "nl_NL", "currency", "NLG" },
    374         { "pt_PT_PREEURO",  "pt_PT", "currency", "PTE" },
    375         { "sl_ROZAJ",       "sl__ROZAJ", null, null }, /* registered name */
    376         { "sr_SP_CYRL",     "sr_Cyrl_RS", null, null }, /* .NET name */
    377         { "sr_SP_LATN",     "sr_Latn_RS", null, null }, /* .NET name */
    378         { "sr_YU_CYRILLIC", "sr_Cyrl_RS", null, null }, /* Linux name */
    379         { "th_TH_TRADITIONAL", "th_TH", "calendar", "buddhist" }, /* Old ICU name */
    380         { "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", null, null }, /* Linux name */
    381         { "uz_UZ_CYRL",     "uz_Cyrl_UZ", null, null }, /* .NET name */
    382         { "uz_UZ_LATN",     "uz_Latn_UZ", null, null }, /* .NET name */
    383         { "zh_CHS",         "zh_Hans", null, null }, /* .NET name */
    384         { "zh_CHT",         "zh_Hant", null, null }, /* .NET name */
    385         { "zh_GAN",         "zh__GAN", null, null }, /* registered name */
    386         { "zh_GUOYU",       "zh", null, null }, /* registered name */
    387         { "zh_HAKKA",       "zh__HAKKA", null, null }, /* registered name */
    388         { "zh_MIN",         "zh__MIN", null, null }, /* registered name */
    389         { "zh_MIN_NAN",     "zh__MINNAN", null, null }, /* registered name */
    390         { "zh_WUU",         "zh__WUU", null, null }, /* registered name */
    391         { "zh_XIANG",       "zh__XIANG", null, null }, /* registered name */
    392         { "zh_YUE",         "zh__YUE", null, null } /* registered name */
    393     };
    394 
    395     /**
    396      * This table lists pairs of locale ids for canonicalization.
    397      * The first item is the normalized variant id.
    398      */
    399     private static String[][] variantsToKeywords = {
    400         { "EURO",   "currency", "EUR" },
    401         { "PINYIN", "collation", "pinyin" }, /* Solaris variant */
    402         { "STROKE", "collation", "stroke" }  /* Solaris variant */
    403     };
    404 
    405 
    406     /**
    407      * Private constructor used by static initializers.
    408      */
    409     private ULocale(String localeID, Locale locale) {
    410         this.localeID = localeID;
    411         this.locale = locale;
    412     }
    413 
    414     /**
    415      * Construct a ULocale object from a {@link java.util.Locale}.
    416      * @param loc a {@link java.util.Locale}
    417      */
    418     private ULocale(Locale loc) {
    419         this.localeID = getName(forLocale(loc).toString());
    420         this.locale = loc;
    421     }
    422 
    423     /**
    424      * {@icu} Returns a ULocale object for a {@link java.util.Locale}.
    425      * The ULocale is canonicalized.
    426      * @param loc a {@link java.util.Locale}
    427      * @stable ICU 3.2
    428      */
    429     public static ULocale forLocale(Locale loc) {
    430         if (loc == null) {
    431             return null;
    432         }
    433         return CACHE.getInstance(loc, null /* unused */);
    434     }
    435 
    436     /**
    437      * {@icu} Constructs a ULocale from a RFC 3066 locale ID. The locale ID consists
    438      * of optional language, script, country, and variant fields in that order,
    439      * separated by underscores, followed by an optional keyword list.  The
    440      * script, if present, is four characters long-- this distinguishes it
    441      * from a country code, which is two characters long.  Other fields
    442      * are distinguished by position as indicated by the underscores.  The
    443      * start of the keyword list is indicated by '@', and consists of two
    444      * or more keyword/value pairs separated by semicolons(';').
    445      *
    446      * <p>This constructor does not canonicalize the localeID.  So, for
    447      * example, "zh__pinyin" remains unchanged instead of converting
    448      * to "zh@collation=pinyin".  By default ICU only recognizes the
    449      * latter as specifying pinyin collation.  Use {@link #createCanonical}
    450      * or {@link #canonicalize} if you need to canonicalize the localeID.
    451      *
    452      * @param localeID string representation of the locale, e.g:
    453      * "en_US", "sy_Cyrl_YU", "zh__pinyin", "es_ES@currency=EUR;collation=traditional"
    454      * @stable ICU 2.8
    455      */
    456     public ULocale(String localeID) {
    457         this.localeID = getName(localeID);
    458     }
    459 
    460     /**
    461      * Convenience overload of ULocale(String, String, String) for
    462      * compatibility with java.util.Locale.
    463      * @see #ULocale(String, String, String)
    464      * @stable ICU 3.4
    465      */
    466     public ULocale(String a, String b) {
    467         this(a, b, null);
    468     }
    469 
    470     /**
    471      * Constructs a ULocale from a localeID constructed from the three 'fields' a, b, and
    472      * c.  These fields are concatenated using underscores to form a localeID of the form
    473      * a_b_c, which is then handled like the localeID passed to <code>ULocale(String
    474      * localeID)</code>.
    475      *
    476      * <p>Java locale strings consisting of language, country, and
    477      * variant will be handled by this form, since the country code
    478      * (being shorter than four letters long) will not be interpreted
    479      * as a script code.  If a script code is present, the final
    480      * argument ('c') will be interpreted as the country code.  It is
    481      * recommended that this constructor only be used to ease porting,
    482      * and that clients instead use the single-argument constructor
    483      * when constructing a ULocale from a localeID.
    484      * @param a first component of the locale id
    485      * @param b second component of the locale id
    486      * @param c third component of the locale id
    487      * @see #ULocale(String)
    488      * @stable ICU 3.0
    489      */
    490     public ULocale(String a, String b, String c) {
    491         localeID = getName(lscvToID(a, b, c, EMPTY_STRING));
    492     }
    493 
    494     /**
    495      * {@icu} Creates a ULocale from the id by first canonicalizing the id.
    496      * @param nonCanonicalID the locale id to canonicalize
    497      * @return the locale created from the canonical version of the ID.
    498      * @stable ICU 3.0
    499      */
    500     public static ULocale createCanonical(String nonCanonicalID) {
    501         return new ULocale(canonicalize(nonCanonicalID), (Locale)null);
    502     }
    503 
    504     private static String lscvToID(String lang, String script, String country, String variant) {
    505         StringBuilder buf = new StringBuilder();
    506 
    507         if (lang != null && lang.length() > 0) {
    508             buf.append(lang);
    509         }
    510         if (script != null && script.length() > 0) {
    511             buf.append(UNDERSCORE);
    512             buf.append(script);
    513         }
    514         if (country != null && country.length() > 0) {
    515             buf.append(UNDERSCORE);
    516             buf.append(country);
    517         }
    518         if (variant != null && variant.length() > 0) {
    519             if (country == null || country.length() == 0) {
    520                 buf.append(UNDERSCORE);
    521             }
    522             buf.append(UNDERSCORE);
    523             buf.append(variant);
    524         }
    525         return buf.toString();
    526     }
    527 
    528     /**
    529      * {@icu} Converts this ULocale object to a {@link java.util.Locale}.
    530      * @return a {@link java.util.Locale} that either exactly represents this object
    531      * or is the closest approximation.
    532      * @stable ICU 2.8
    533      */
    534     public Locale toLocale() {
    535         if (locale == null) {
    536             locale = JDKLocaleHelper.toLocale(this);
    537         }
    538         return locale;
    539     }
    540 
    541     /**
    542      * Keep our own default ULocale.
    543      */
    544     private static Locale defaultLocale = Locale.getDefault();
    545     private static ULocale defaultULocale;
    546 
    547     private static Locale[] defaultCategoryLocales = new Locale[Category.values().length];
    548     private static ULocale[] defaultCategoryULocales = new ULocale[Category.values().length];
    549 
    550     static {
    551         defaultULocale = forLocale(defaultLocale);
    552 
    553         if (JDKLocaleHelper.hasLocaleCategories()) {
    554             for (Category cat: Category.values()) {
    555                 int idx = cat.ordinal();
    556                 defaultCategoryLocales[idx] = JDKLocaleHelper.getDefault(cat);
    557                 defaultCategoryULocales[idx] = forLocale(defaultCategoryLocales[idx]);
    558             }
    559         } else {
    560             // Android API level 21..23 does not have separate category locales,
    561             // use the non-category default for all.
    562             for (Category cat: Category.values()) {
    563                 int idx = cat.ordinal();
    564                 defaultCategoryLocales[idx] = defaultLocale;
    565                 defaultCategoryULocales[idx] = defaultULocale;
    566             }
    567         }
    568     }
    569 
    570     /**
    571      * Returns the current default ULocale.
    572      * <p>
    573      * The default ULocale is synchronized to the default Java Locale. This method checks
    574      * the current default Java Locale and returns an equivalent ULocale.
    575      *
    576      * @return the default ULocale.
    577      * @stable ICU 2.8
    578      */
    579     public static ULocale getDefault() {
    580         synchronized (ULocale.class) {
    581             if (defaultULocale == null) {
    582                 // When Java's default locale has extensions (such as ja-JP-u-ca-japanese),
    583                 // Locale -> ULocale mapping requires BCP47 keyword mapping data that is currently
    584                 // stored in a resource bundle. However, UResourceBundle currently requires
    585                 // non-null default ULocale. For now, this implementation returns ULocale.ROOT
    586                 // to avoid the problem.
    587 
    588                 // TODO: Consider moving BCP47 mapping data out of resource bundle later.
    589 
    590                 return ULocale.ROOT;
    591             }
    592             Locale currentDefault = Locale.getDefault();
    593             if (!defaultLocale.equals(currentDefault)) {
    594                 defaultLocale = currentDefault;
    595                 defaultULocale = forLocale(currentDefault);
    596 
    597                 if (!JDKLocaleHelper.hasLocaleCategories()) {
    598                     // Detected Java default Locale change.
    599                     // We need to update category defaults to match
    600                     // Java 7's behavior on Android API level 21..23.
    601                     for (Category cat : Category.values()) {
    602                         int idx = cat.ordinal();
    603                         defaultCategoryLocales[idx] = currentDefault;
    604                         defaultCategoryULocales[idx] = forLocale(currentDefault);
    605                     }
    606                 }            }
    607             return defaultULocale;
    608         }
    609     }
    610 
    611     /**
    612      * Sets the default ULocale.  This also sets the default Locale.
    613      * If the caller does not have write permission to the
    614      * user.language property, a security exception will be thrown,
    615      * and the default ULocale will remain unchanged.
    616      * <p>
    617      * By setting the default ULocale with this method, all of the default categoy locales
    618      * are also set to the specified default ULocale.
    619      * @param newLocale the new default locale
    620      * @throws SecurityException if a security manager exists and its
    621      *        <code>checkPermission</code> method doesn't allow the operation.
    622      * @throws NullPointerException if <code>newLocale</code> is null
    623      * @see SecurityManager#checkPermission(java.security.Permission)
    624      * @see java.util.PropertyPermission
    625      * @see ULocale#setDefault(Category, ULocale)
    626      * @stable ICU 3.0
    627      */
    628     public static synchronized void setDefault(ULocale newLocale){
    629         defaultLocale = newLocale.toLocale();
    630         Locale.setDefault(defaultLocale);
    631         defaultULocale = newLocale;
    632         // This method also updates all category default locales
    633         for (Category cat : Category.values()) {
    634             setDefault(cat, newLocale);
    635         }
    636     }
    637 
    638     /**
    639      * Returns the current default ULocale for the specified category.
    640      *
    641      * @param category the category
    642      * @return the default ULocale for the specified category.
    643      * @stable ICU 49
    644      */
    645     public static ULocale getDefault(Category category) {
    646         synchronized (ULocale.class) {
    647             int idx = category.ordinal();
    648             if (defaultCategoryULocales[idx] == null) {
    649                 // Just in case this method is called during ULocale class
    650                 // initialization. Unlike getDefault(), we do not have
    651                 // cyclic dependency for category default.
    652                 return ULocale.ROOT;
    653             }
    654             if (JDKLocaleHelper.hasLocaleCategories()) {
    655                 Locale currentCategoryDefault = JDKLocaleHelper.getDefault(category);
    656                 if (!defaultCategoryLocales[idx].equals(currentCategoryDefault)) {
    657                     defaultCategoryLocales[idx] = currentCategoryDefault;
    658                     defaultCategoryULocales[idx] = forLocale(currentCategoryDefault);
    659                 }
    660             } else {
    661                 // java.util.Locale.setDefault(Locale) in Java 7 updates
    662                 // category locale defaults. On Android API level 21..23
    663                 // ICU4J checks if the default locale has changed and update
    664                 // category ULocales here if necessary.
    665 
    666                 // Note: When java.util.Locale.setDefault(Locale) is called
    667                 // with a Locale same with the previous one, Java 7 still
    668                 // updates category locale defaults. On Android API level 21..23
    669                 // there is no good way to detect the event, ICU4J simply
    670                 // checks if the default Java Locale has changed since last
    671                 // time.
    672 
    673                 Locale currentDefault = Locale.getDefault();
    674                 if (!defaultLocale.equals(currentDefault)) {
    675                     defaultLocale = currentDefault;
    676                     defaultULocale = forLocale(currentDefault);
    677 
    678                     for (Category cat : Category.values()) {
    679                         int tmpIdx = cat.ordinal();
    680                         defaultCategoryLocales[tmpIdx] = currentDefault;
    681                         defaultCategoryULocales[tmpIdx] = forLocale(currentDefault);
    682                     }
    683                 }
    684 
    685                 // No synchronization with JDK Locale, because category default
    686                 // is not supported in Android API level 21..23.
    687             }
    688             return defaultCategoryULocales[idx];
    689         }
    690     }
    691 
    692     /**
    693      * Sets the default <code>ULocale</code> for the specified <code>Category</code>.
    694      * This also sets the default <code>Locale</code> for the specified <code>Category</code>
    695      * of the JVM. If the caller does not have write permission to the
    696      * user.language property, a security exception will be thrown,
    697      * and the default ULocale for the specified Category will remain unchanged.
    698      *
    699      * @param category the specified category to set the default locale
    700      * @param newLocale the new default locale
    701      * @see SecurityManager#checkPermission(java.security.Permission)
    702      * @see java.util.PropertyPermission
    703      * @stable ICU 49
    704      */
    705     public static synchronized void setDefault(Category category, ULocale newLocale) {
    706         Locale newJavaDefault = newLocale.toLocale();
    707         int idx = category.ordinal();
    708         defaultCategoryULocales[idx] = newLocale;
    709         defaultCategoryLocales[idx] = newJavaDefault;
    710         JDKLocaleHelper.setDefault(category, newJavaDefault);
    711     }
    712 
    713     /**
    714      * This is for compatibility with Locale-- in actuality, since ULocale is
    715      * immutable, there is no reason to clone it, so this API returns 'this'.
    716      * @stable ICU 2.8
    717      */
    718     @Override
    719     public Object clone() {
    720         return this;
    721     }
    722 
    723     /**
    724      * Returns the hashCode.
    725      * @return a hash code value for this object.
    726      * @stable ICU 2.8
    727      */
    728     @Override
    729     public int hashCode() {
    730         return localeID.hashCode();
    731     }
    732 
    733     /**
    734      * Returns true if the other object is another ULocale with the
    735      * same full name.
    736      * Note that since names are not canonicalized, two ULocales that
    737      * function identically might not compare equal.
    738      *
    739      * @return true if this Locale is equal to the specified object.
    740      * @stable ICU 2.8
    741      */
    742     @Override
    743     public boolean equals(Object obj) {
    744         if (this == obj) {
    745             return true;
    746         }
    747         if (obj instanceof ULocale) {
    748             return localeID.equals(((ULocale)obj).localeID);
    749         }
    750         return false;
    751     }
    752 
    753     /**
    754      * Compares two ULocale for ordering.
    755      * <p><b>Note:</b> The order might change in future.
    756      *
    757      * @param other the ULocale to be compared.
    758      * @return a negative integer, zero, or a positive integer as this ULocale is less than, equal to, or greater
    759      * than the specified ULocale.
    760      * @throws NullPointerException if <code>other</code> is null.
    761      *
    762      * @stable ICU 53
    763      */
    764     @Override
    765     public int compareTo(ULocale other) {
    766         if (this == other) {
    767             return 0;
    768         }
    769 
    770         int cmp = 0;
    771 
    772         // Language
    773         cmp = getLanguage().compareTo(other.getLanguage());
    774         if (cmp == 0) {
    775             // Script
    776             cmp = getScript().compareTo(other.getScript());
    777             if (cmp == 0) {
    778                 // Region
    779                 cmp = getCountry().compareTo(other.getCountry());
    780                 if (cmp == 0) {
    781                     // Variant
    782                     cmp = getVariant().compareTo(other.getVariant());
    783                     if (cmp == 0) {
    784                         // Keywords
    785                         Iterator<String> thisKwdItr = getKeywords();
    786                         Iterator<String> otherKwdItr = other.getKeywords();
    787 
    788                         if (thisKwdItr == null) {
    789                             cmp = otherKwdItr == null ? 0 : -1;
    790                         } else if (otherKwdItr == null) {
    791                             cmp = 1;
    792                         } else {
    793                             // Both have keywords
    794                             while (cmp == 0 && thisKwdItr.hasNext()) {
    795                                 if (!otherKwdItr.hasNext()) {
    796                                     cmp = 1;
    797                                     break;
    798                                 }
    799                                 // Compare keyword keys
    800                                 String thisKey = thisKwdItr.next();
    801                                 String otherKey = otherKwdItr.next();
    802                                 cmp = thisKey.compareTo(otherKey);
    803                                 if (cmp == 0) {
    804                                     // Compare keyword values
    805                                     String thisVal = getKeywordValue(thisKey);
    806                                     String otherVal = other.getKeywordValue(otherKey);
    807                                     if (thisVal == null) {
    808                                         cmp = otherVal == null ? 0 : -1;
    809                                     } else if (otherVal == null) {
    810                                         cmp = 1;
    811                                     } else {
    812                                         cmp = thisVal.compareTo(otherVal);
    813                                     }
    814                                 }
    815                             }
    816                             if (cmp == 0 && otherKwdItr.hasNext()) {
    817                                 cmp = -1;
    818                             }
    819                         }
    820                     }
    821                 }
    822             }
    823         }
    824 
    825         // Normalize the result value:
    826         // Note: String.compareTo() may return value other than -1, 0, 1.
    827         // A value other than those are OK by the definition, but we don't want
    828         // associate any semantics other than negative/zero/positive.
    829         return (cmp < 0) ? -1 : ((cmp > 0) ? 1 : 0);
    830     }
    831 
    832     /**
    833      * {@icunote} Unlike the Locale API, this returns an array of <code>ULocale</code>,
    834      * not <code>Locale</code>.  Returns a list of all installed locales.
    835      * @stable ICU 3.0
    836      */
    837     public static ULocale[] getAvailableLocales() {
    838         return ICUResourceBundle.getAvailableULocales();
    839     }
    840 
    841     /**
    842      * Returns a list of all 2-letter country codes defined in ISO 3166.
    843      * Can be used to create Locales.
    844      * @stable ICU 3.0
    845      */
    846     public static String[] getISOCountries() {
    847         return LocaleIDs.getISOCountries();
    848     }
    849 
    850     /**
    851      * Returns a list of all 2-letter language codes defined in ISO 639.
    852      * Can be used to create Locales.
    853      * [NOTE:  ISO 639 is not a stable standard-- some languages' codes have changed.
    854      * The list this function returns includes both the new and the old codes for the
    855      * languages whose codes have changed.]
    856      * @stable ICU 3.0
    857      */
    858     public static String[] getISOLanguages() {
    859         return LocaleIDs.getISOLanguages();
    860     }
    861 
    862     /**
    863      * Returns the language code for this locale, which will either be the empty string
    864      * or a lowercase ISO 639 code.
    865      * @see #getDisplayLanguage()
    866      * @see #getDisplayLanguage(ULocale)
    867      * @stable ICU 3.0
    868      */
    869     public String getLanguage() {
    870         return base().getLanguage();
    871     }
    872 
    873     /**
    874      * Returns the language code for the locale ID,
    875      * which will either be the empty string
    876      * or a lowercase ISO 639 code.
    877      * @see #getDisplayLanguage()
    878      * @see #getDisplayLanguage(ULocale)
    879      * @stable ICU 3.0
    880      */
    881     public static String getLanguage(String localeID) {
    882         return new LocaleIDParser(localeID).getLanguage();
    883     }
    884 
    885     /**
    886      * Returns the script code for this locale, which might be the empty string.
    887      * @see #getDisplayScript()
    888      * @see #getDisplayScript(ULocale)
    889      * @stable ICU 3.0
    890      */
    891     public String getScript() {
    892         return base().getScript();
    893     }
    894 
    895     /**
    896      * {@icu} Returns the script code for the specified locale, which might be the empty
    897      * string.
    898      * @see #getDisplayScript()
    899      * @see #getDisplayScript(ULocale)
    900      * @stable ICU 3.0
    901      */
    902     public static String getScript(String localeID) {
    903         return new LocaleIDParser(localeID).getScript();
    904     }
    905 
    906     /**
    907      * Returns the country/region code for this locale, which will either be the empty string
    908      * or an uppercase ISO 3166 2-letter code.
    909      * @see #getDisplayCountry()
    910      * @see #getDisplayCountry(ULocale)
    911      * @stable ICU 3.0
    912      */
    913     public String getCountry() {
    914         return base().getRegion();
    915     }
    916 
    917     /**
    918      * {@icu} Returns the country/region code for this locale, which will either be the empty string
    919      * or an uppercase ISO 3166 2-letter code.
    920      * @param localeID The locale identification string.
    921      * @see #getDisplayCountry()
    922      * @see #getDisplayCountry(ULocale)
    923      * @stable ICU 3.0
    924      */
    925     public static String getCountry(String localeID) {
    926         return new LocaleIDParser(localeID).getCountry();
    927     }
    928 
    929     /**
    930      * {@icu} Get the region to use for supplemental data lookup.
    931      * Uses
    932      * (1) any region specified by locale tag "rg"; if none then
    933      * (2) any unicode_region_tag in the locale ID; if none then
    934      * (3) if inferRegion is TRUE, the region suggested by
    935      *     getLikelySubtags on the localeID.
    936      * If no region is found, returns empty string ""
    937      *
    938      * @param locale
    939      *     The locale (includes any keywords) from which
    940      *     to get the region to use for supplemental data.
    941      * @param inferRegion
    942      *     If TRUE, will try to infer region from other
    943      *     locale elements if not found any other way.
    944      * @return
    945      *     String with region to use ("" if none found).
    946      * @internal ICU 57
    947      * @deprecated This API is ICU internal only.
    948      */
    949     @Deprecated
    950     public static String getRegionForSupplementalData(
    951                             ULocale locale, boolean inferRegion) {
    952         String region = locale.getKeywordValue("rg");
    953         if (region != null && region.length() == 6) {
    954             String regionUpper = AsciiUtil.toUpperString(region);
    955             if (regionUpper.endsWith("ZZZZ")) {
    956             	return regionUpper.substring(0,2);
    957             }
    958         }
    959         region = locale.getCountry();
    960         if (region.length() == 0 && inferRegion) {
    961             ULocale maximized = addLikelySubtags(locale);
    962             region = maximized.getCountry();
    963         }
    964         return region;
    965     }
    966 
    967     /**
    968      * Returns the variant code for this locale, which might be the empty string.
    969      * @see #getDisplayVariant()
    970      * @see #getDisplayVariant(ULocale)
    971      * @stable ICU 3.0
    972      */
    973     public String getVariant() {
    974         return base().getVariant();
    975     }
    976 
    977     /**
    978      * {@icu} Returns the variant code for the specified locale, which might be the empty string.
    979      * @see #getDisplayVariant()
    980      * @see #getDisplayVariant(ULocale)
    981      * @stable ICU 3.0
    982      */
    983     public static String getVariant(String localeID) {
    984         return new LocaleIDParser(localeID).getVariant();
    985     }
    986 
    987     /**
    988      * {@icu} Returns the fallback locale for the specified locale, which might be the
    989      * empty string.
    990      * @stable ICU 3.2
    991      */
    992     public static String getFallback(String localeID) {
    993         return getFallbackString(getName(localeID));
    994     }
    995 
    996     /**
    997      * {@icu} Returns the fallback locale for this locale.  If this locale is root,
    998      * returns null.
    999      * @stable ICU 3.2
   1000      */
   1001     public ULocale getFallback() {
   1002         if (localeID.length() == 0 || localeID.charAt(0) == '@') {
   1003             return null;
   1004         }
   1005         return new ULocale(getFallbackString(localeID), (Locale)null);
   1006     }
   1007 
   1008     /**
   1009      * Returns the given (canonical) locale id minus the last part before the tags.
   1010      */
   1011     private static String getFallbackString(String fallback) {
   1012         int extStart = fallback.indexOf('@');
   1013         if (extStart == -1) {
   1014             extStart = fallback.length();
   1015         }
   1016         int last = fallback.lastIndexOf('_', extStart);
   1017         if (last == -1) {
   1018             last = 0;
   1019         } else {
   1020             // truncate empty segment
   1021             while (last > 0) {
   1022                 if (fallback.charAt(last - 1) != '_') {
   1023                     break;
   1024                 }
   1025                 last--;
   1026             }
   1027         }
   1028         return fallback.substring(0, last) + fallback.substring(extStart);
   1029     }
   1030 
   1031     /**
   1032      * {@icu} Returns the (normalized) base name for this locale,
   1033      * like {@link #getName()}, but without keywords.
   1034      *
   1035      * @return the base name as a String.
   1036      * @stable ICU 3.0
   1037      */
   1038     public String getBaseName() {
   1039         return getBaseName(localeID);
   1040     }
   1041 
   1042     /**
   1043      * {@icu} Returns the (normalized) base name for the specified locale,
   1044      * like {@link #getName(String)}, but without keywords.
   1045      *
   1046      * @param localeID the locale ID as a string
   1047      * @return the base name as a String.
   1048      * @stable ICU 3.0
   1049      */
   1050     public static String getBaseName(String localeID){
   1051         if (localeID.indexOf('@') == -1) {
   1052             return localeID;
   1053         }
   1054         return new LocaleIDParser(localeID).getBaseName();
   1055     }
   1056 
   1057     /**
   1058      * {@icu} Returns the (normalized) full name for this locale.
   1059      *
   1060      * @return String the full name of the localeID
   1061      * @stable ICU 3.0
   1062      */
   1063     public String getName() {
   1064         return localeID; // always normalized
   1065     }
   1066 
   1067     /**
   1068      * Gets the shortest length subtag's size.
   1069      *
   1070      * @param localeID
   1071      * @return The size of the shortest length subtag
   1072      **/
   1073     private static int getShortestSubtagLength(String localeID) {
   1074         int localeIDLength = localeID.length();
   1075         int length = localeIDLength;
   1076         boolean reset = true;
   1077         int tmpLength = 0;
   1078 
   1079         for (int i = 0; i < localeIDLength; i++) {
   1080             if (localeID.charAt(i) != '_' && localeID.charAt(i) != '-') {
   1081                 if (reset) {
   1082                     reset = false;
   1083                     tmpLength = 0;
   1084                 }
   1085                 tmpLength++;
   1086             } else {
   1087                 if (tmpLength != 0 && tmpLength < length) {
   1088                     length = tmpLength;
   1089                 }
   1090                 reset = true;
   1091             }
   1092         }
   1093 
   1094         return length;
   1095     }
   1096 
   1097     /**
   1098      * {@icu} Returns the (normalized) full name for the specified locale.
   1099      *
   1100      * @param localeID the localeID as a string
   1101      * @return String the full name of the localeID
   1102      * @stable ICU 3.0
   1103      */
   1104     public static String getName(String localeID){
   1105         String tmpLocaleID;
   1106         // Convert BCP47 id if necessary
   1107         if (localeID != null && !localeID.contains("@") && getShortestSubtagLength(localeID) == 1) {
   1108             tmpLocaleID = forLanguageTag(localeID).getName();
   1109             if (tmpLocaleID.length() == 0) {
   1110                 tmpLocaleID = localeID;
   1111             }
   1112         } else {
   1113             tmpLocaleID = localeID;
   1114         }
   1115         return nameCache.getInstance(tmpLocaleID, null /* unused */);
   1116     }
   1117 
   1118     /**
   1119      * Returns a string representation of this object.
   1120      * @return a string representation of the object.
   1121      * @stable ICU 2.8
   1122      */
   1123     @Override
   1124     public String toString() {
   1125         return localeID;
   1126     }
   1127 
   1128     /**
   1129      * {@icu} Returns an iterator over keywords for this locale.  If there
   1130      * are no keywords, returns null.
   1131      * @return iterator over keywords, or null if there are no keywords.
   1132      * @stable ICU 3.0
   1133      */
   1134     public Iterator<String> getKeywords() {
   1135         return getKeywords(localeID);
   1136     }
   1137 
   1138     /**
   1139      * {@icu} Returns an iterator over keywords for the specified locale.  If there
   1140      * are no keywords, returns null.
   1141      * @return an iterator over the keywords in the specified locale, or null
   1142      * if there are no keywords.
   1143      * @stable ICU 3.0
   1144      */
   1145     public static Iterator<String> getKeywords(String localeID){
   1146         return new LocaleIDParser(localeID).getKeywords();
   1147     }
   1148 
   1149     /**
   1150      * {@icu} Returns the value for a keyword in this locale. If the keyword is not
   1151      * defined, returns null.
   1152      * @param keywordName name of the keyword whose value is desired. Case insensitive.
   1153      * @return the value of the keyword, or null.
   1154      * @stable ICU 3.0
   1155      */
   1156     public String getKeywordValue(String keywordName){
   1157         return getKeywordValue(localeID, keywordName);
   1158     }
   1159 
   1160     /**
   1161      * {@icu} Returns the value for a keyword in the specified locale. If the keyword is
   1162      * not defined, returns null.  The locale name does not need to be normalized.
   1163      * @param keywordName name of the keyword whose value is desired. Case insensitive.
   1164      * @return String the value of the keyword as a string
   1165      * @stable ICU 3.0
   1166      */
   1167     public static String getKeywordValue(String localeID, String keywordName) {
   1168         return new LocaleIDParser(localeID).getKeywordValue(keywordName);
   1169     }
   1170 
   1171     /**
   1172      * {@icu} Returns the canonical name for the specified locale ID.  This is used to
   1173      * convert POSIX and other grandfathered IDs to standard ICU form.
   1174      * @param localeID the locale id
   1175      * @return the canonicalized id
   1176      * @stable ICU 3.0
   1177      */
   1178     public static String canonicalize(String localeID){
   1179         LocaleIDParser parser = new LocaleIDParser(localeID, true);
   1180         String baseName = parser.getBaseName();
   1181         boolean foundVariant = false;
   1182 
   1183         // formerly, we always set to en_US_POSIX if the basename was empty, but
   1184         // now we require that the entire id be empty, so that "@foo=bar"
   1185         // will pass through unchanged.
   1186         // {dlf} I'd rather keep "" unchanged.
   1187         if (localeID.equals("")) {
   1188             return "";
   1189             //              return "en_US_POSIX";
   1190         }
   1191 
   1192         // we have an ID in the form xx_Yyyy_ZZ_KKKKK
   1193 
   1194         /* convert the variants to appropriate ID */
   1195         for (int i = 0; i < variantsToKeywords.length; i++) {
   1196             String[] vals = variantsToKeywords[i];
   1197             int idx = baseName.lastIndexOf("_" + vals[0]);
   1198             if (idx > -1) {
   1199                 foundVariant = true;
   1200 
   1201                 baseName = baseName.substring(0, idx);
   1202                 if (baseName.endsWith("_")) {
   1203                     baseName = baseName.substring(0, --idx);
   1204                 }
   1205                 parser.setBaseName(baseName);
   1206                 parser.defaultKeywordValue(vals[1], vals[2]);
   1207                 break;
   1208             }
   1209         }
   1210 
   1211         /* See if this is an already known locale */
   1212         for (int i = 0; i < CANONICALIZE_MAP.length; i++) {
   1213             if (CANONICALIZE_MAP[i][0].equals(baseName)) {
   1214                 foundVariant = true;
   1215 
   1216                 String[] vals = CANONICALIZE_MAP[i];
   1217                 parser.setBaseName(vals[1]);
   1218                 if (vals[2] != null) {
   1219                     parser.defaultKeywordValue(vals[2], vals[3]);
   1220                 }
   1221                 break;
   1222             }
   1223         }
   1224 
   1225         /* total mondo hack for Norwegian, fortunately the main NY case is handled earlier */
   1226         if (!foundVariant) {
   1227             if (parser.getLanguage().equals("nb") && parser.getVariant().equals("NY")) {
   1228                 parser.setBaseName(lscvToID("nn", parser.getScript(), parser.getCountry(), null));
   1229             }
   1230         }
   1231 
   1232         return parser.getName();
   1233     }
   1234 
   1235     /**
   1236      * {@icu} Given a keyword and a value, return a new locale with an updated
   1237      * keyword and value.  If the keyword is null, this removes all keywords from the locale id.
   1238      * Otherwise, if the value is null, this removes the value for this keyword from the
   1239      * locale id.  Otherwise, this adds/replaces the value for this keyword in the locale id.
   1240      * The keyword and value must not be empty.
   1241      *
   1242      * <p>Related: {@link #getBaseName()} returns the locale ID string with all keywords removed.
   1243      *
   1244      * @param keyword the keyword to add/remove, or null to remove all keywords.
   1245      * @param value the value to add/set, or null to remove this particular keyword.
   1246      * @return the updated locale
   1247      * @stable ICU 3.2
   1248      */
   1249     public ULocale setKeywordValue(String keyword, String value) {
   1250         return new ULocale(setKeywordValue(localeID, keyword, value), (Locale)null);
   1251     }
   1252 
   1253     /**
   1254      * Given a locale id, a keyword, and a value, return a new locale id with an updated
   1255      * keyword and value.  If the keyword is null, this removes all keywords from the locale id.
   1256      * Otherwise, if the value is null, this removes the value for this keyword from the
   1257      * locale id.  Otherwise, this adds/replaces the value for this keyword in the locale id.
   1258      * The keyword and value must not be empty.
   1259      *
   1260      * <p>Related: {@link #getBaseName(String)} returns the locale ID string with all keywords removed.
   1261      *
   1262      * @param localeID the locale id to modify
   1263      * @param keyword the keyword to add/remove, or null to remove all keywords.
   1264      * @param value the value to add/set, or null to remove this particular keyword.
   1265      * @return the updated locale id
   1266      * @stable ICU 3.2
   1267      */
   1268     public static String setKeywordValue(String localeID, String keyword, String value) {
   1269         LocaleIDParser parser = new LocaleIDParser(localeID);
   1270         parser.setKeywordValue(keyword, value);
   1271         return parser.getName();
   1272     }
   1273 
   1274     /*
   1275      * Given a locale id, a keyword, and a value, return a new locale id with an updated
   1276      * keyword and value, if the keyword does not already have a value.  The keyword and
   1277      * value must not be null or empty.
   1278      * @param localeID the locale id to modify
   1279      * @param keyword the keyword to add, if not already present
   1280      * @param value the value to add, if not already present
   1281      * @return the updated locale id
   1282      */
   1283     /*    private static String defaultKeywordValue(String localeID, String keyword, String value) {
   1284         LocaleIDParser parser = new LocaleIDParser(localeID);
   1285         parser.defaultKeywordValue(keyword, value);
   1286         return parser.getName();
   1287     }*/
   1288 
   1289     /**
   1290      * Returns a three-letter abbreviation for this locale's language.  If the locale
   1291      * doesn't specify a language, returns the empty string.  Otherwise, returns
   1292      * a lowercase ISO 639-2/T language code.
   1293      * The ISO 639-2 language codes can be found on-line at
   1294      *   <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
   1295      * @exception MissingResourceException Throws MissingResourceException if the
   1296      * three-letter language abbreviation is not available for this locale.
   1297      * @stable ICU 3.0
   1298      */
   1299     public String getISO3Language(){
   1300         return getISO3Language(localeID);
   1301     }
   1302 
   1303     /**
   1304      * {@icu} Returns a three-letter abbreviation for this locale's language.  If the locale
   1305      * doesn't specify a language, returns the empty string.  Otherwise, returns
   1306      * a lowercase ISO 639-2/T language code.
   1307      * The ISO 639-2 language codes can be found on-line at
   1308      *   <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
   1309      * @exception MissingResourceException Throws MissingResourceException if the
   1310      * three-letter language abbreviation is not available for this locale.
   1311      * @stable ICU 3.0
   1312      */
   1313     public static String getISO3Language(String localeID) {
   1314         return LocaleIDs.getISO3Language(getLanguage(localeID));
   1315     }
   1316 
   1317     /**
   1318      * Returns a three-letter abbreviation for this locale's country/region.  If the locale
   1319      * doesn't specify a country, returns the empty string.  Otherwise, returns
   1320      * an uppercase ISO 3166 3-letter country code.
   1321      * @exception MissingResourceException Throws MissingResourceException if the
   1322      * three-letter country abbreviation is not available for this locale.
   1323      * @stable ICU 3.0
   1324      */
   1325     public String getISO3Country() {
   1326         return getISO3Country(localeID);
   1327     }
   1328 
   1329     /**
   1330      * {@icu} Returns a three-letter abbreviation for this locale's country/region.  If the locale
   1331      * doesn't specify a country, returns the empty string.  Otherwise, returns
   1332      * an uppercase ISO 3166 3-letter country code.
   1333      * @exception MissingResourceException Throws MissingResourceException if the
   1334      * three-letter country abbreviation is not available for this locale.
   1335      * @stable ICU 3.0
   1336      */
   1337     public static String getISO3Country(String localeID) {
   1338         return LocaleIDs.getISO3Country(getCountry(localeID));
   1339     }
   1340 
   1341     /**
   1342      * Pairs of (language subtag, + or -) for finding out fast if common languages
   1343      * are LTR (minus) or RTL (plus).
   1344      */
   1345     private static final String LANG_DIR_STRING =
   1346             "root-en-es-pt-zh-ja-ko-de-fr-it-ar+he+fa+ru-nl-pl-th-tr-";
   1347 
   1348     /**
   1349      * {@icu} Returns whether this locale's script is written right-to-left.
   1350      * If there is no script subtag, then the likely script is used,
   1351      * see {@link #addLikelySubtags(ULocale)}.
   1352      * If no likely script is known, then false is returned.
   1353      *
   1354      * <p>A script is right-to-left according to the CLDR script metadata
   1355      * which corresponds to whether the script's letters have Bidi_Class=R or AL.
   1356      *
   1357      * <p>Returns true for "ar" and "en-Hebr", false for "zh" and "fa-Cyrl".
   1358      *
   1359      * @return true if the locale's script is written right-to-left
   1360      * @stable ICU 54
   1361      */
   1362     public boolean isRightToLeft() {
   1363         String script = getScript();
   1364         if (script.length() == 0) {
   1365             // Fastpath: We know the likely scripts and their writing direction
   1366             // for some common languages.
   1367             String lang = getLanguage();
   1368             if (lang.length() == 0) {
   1369                 return false;
   1370             }
   1371             int langIndex = LANG_DIR_STRING.indexOf(lang);
   1372             if (langIndex >= 0) {
   1373                 switch (LANG_DIR_STRING.charAt(langIndex + lang.length())) {
   1374                 case '-': return false;
   1375                 case '+': return true;
   1376                 default: break;  // partial match of a longer code
   1377                 }
   1378             }
   1379             // Otherwise, find the likely script.
   1380             ULocale likely = addLikelySubtags(this);
   1381             script = likely.getScript();
   1382             if (script.length() == 0) {
   1383                 return false;
   1384             }
   1385         }
   1386         int scriptCode = UScript.getCodeFromName(script);
   1387         return UScript.isRightToLeft(scriptCode);
   1388     }
   1389 
   1390     // display names
   1391 
   1392     /**
   1393      * Returns this locale's language localized for display in the default <code>DISPLAY</code> locale.
   1394      * @return the localized language name.
   1395      * @see Category#DISPLAY
   1396      * @stable ICU 3.0
   1397      */
   1398     public String getDisplayLanguage() {
   1399         return getDisplayLanguageInternal(this, getDefault(Category.DISPLAY), false);
   1400     }
   1401 
   1402     /**
   1403      * Returns this locale's language localized for display in the provided locale.
   1404      * @param displayLocale the locale in which to display the name.
   1405      * @return the localized language name.
   1406      * @stable ICU 3.0
   1407      */
   1408     public String getDisplayLanguage(ULocale displayLocale) {
   1409         return getDisplayLanguageInternal(this, displayLocale, false);
   1410     }
   1411 
   1412     /**
   1413      * {@icu} Returns a locale's language localized for display in the provided locale.
   1414      * This is a cover for the ICU4C API.
   1415      * @param localeID the id of the locale whose language will be displayed
   1416      * @param displayLocaleID the id of the locale in which to display the name.
   1417      * @return the localized language name.
   1418      * @stable ICU 3.0
   1419      */
   1420     public static String getDisplayLanguage(String localeID, String displayLocaleID) {
   1421         return getDisplayLanguageInternal(new ULocale(localeID), new ULocale(displayLocaleID),
   1422                 false);
   1423     }
   1424 
   1425     /**
   1426      * {@icu} Returns a locale's language localized for display in the provided locale.
   1427      * This is a cover for the ICU4C API.
   1428      * @param localeID the id of the locale whose language will be displayed.
   1429      * @param displayLocale the locale in which to display the name.
   1430      * @return the localized language name.
   1431      * @stable ICU 3.0
   1432      */
   1433     public static String getDisplayLanguage(String localeID, ULocale displayLocale) {
   1434         return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, false);
   1435     }
   1436     /**
   1437      * {@icu} Returns this locale's language localized for display in the default <code>DISPLAY</code> locale.
   1438      * If a dialect name is present in the data, then it is returned.
   1439      * @return the localized language name.
   1440      * @see Category#DISPLAY
   1441      * @stable ICU 4.4
   1442      */
   1443     public String getDisplayLanguageWithDialect() {
   1444         return getDisplayLanguageInternal(this, getDefault(Category.DISPLAY), true);
   1445     }
   1446 
   1447     /**
   1448      * {@icu} Returns this locale's language localized for display in the provided locale.
   1449      * If a dialect name is present in the data, then it is returned.
   1450      * @param displayLocale the locale in which to display the name.
   1451      * @return the localized language name.
   1452      * @stable ICU 4.4
   1453      */
   1454     public String getDisplayLanguageWithDialect(ULocale displayLocale) {
   1455         return getDisplayLanguageInternal(this, displayLocale, true);
   1456     }
   1457 
   1458     /**
   1459      * {@icu} Returns a locale's language localized for display in the provided locale.
   1460      * If a dialect name is present in the data, then it is returned.
   1461      * This is a cover for the ICU4C API.
   1462      * @param localeID the id of the locale whose language will be displayed
   1463      * @param displayLocaleID the id of the locale in which to display the name.
   1464      * @return the localized language name.
   1465      * @stable ICU 4.4
   1466      */
   1467     public static String getDisplayLanguageWithDialect(String localeID, String displayLocaleID) {
   1468         return getDisplayLanguageInternal(new ULocale(localeID), new ULocale(displayLocaleID),
   1469                 true);
   1470     }
   1471 
   1472     /**
   1473      * {@icu} Returns a locale's language localized for display in the provided locale.
   1474      * If a dialect name is present in the data, then it is returned.
   1475      * This is a cover for the ICU4C API.
   1476      * @param localeID the id of the locale whose language will be displayed.
   1477      * @param displayLocale the locale in which to display the name.
   1478      * @return the localized language name.
   1479      * @stable ICU 4.4
   1480      */
   1481     public static String getDisplayLanguageWithDialect(String localeID, ULocale displayLocale) {
   1482         return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, true);
   1483     }
   1484 
   1485     private static String getDisplayLanguageInternal(ULocale locale, ULocale displayLocale,
   1486             boolean useDialect) {
   1487         String lang = useDialect ? locale.getBaseName() : locale.getLanguage();
   1488         return LocaleDisplayNames.getInstance(displayLocale).languageDisplayName(lang);
   1489     }
   1490 
   1491     /**
   1492      * Returns this locale's script localized for display in the default <code>DISPLAY</code> locale.
   1493      * @return the localized script name.
   1494      * @see Category#DISPLAY
   1495      * @stable ICU 3.0
   1496      */
   1497     public String getDisplayScript() {
   1498         return getDisplayScriptInternal(this, getDefault(Category.DISPLAY));
   1499     }
   1500 
   1501     /**
   1502      * {@icu} Returns this locale's script localized for display in the default <code>DISPLAY</code> locale.
   1503      * @return the localized script name.
   1504      * @see Category#DISPLAY
   1505      * @internal
   1506      * @deprecated This API is ICU internal only.
   1507      */
   1508     @Deprecated
   1509     public String getDisplayScriptInContext() {
   1510         return getDisplayScriptInContextInternal(this, getDefault(Category.DISPLAY));
   1511     }
   1512 
   1513     /**
   1514      * Returns this locale's script localized for display in the provided locale.
   1515      * @param displayLocale the locale in which to display the name.
   1516      * @return the localized script name.
   1517      * @stable ICU 3.0
   1518      */
   1519     public String getDisplayScript(ULocale displayLocale) {
   1520         return getDisplayScriptInternal(this, displayLocale);
   1521     }
   1522 
   1523     /**
   1524      * {@icu} Returns this locale's script localized for display in the provided locale.
   1525      * @param displayLocale the locale in which to display the name.
   1526      * @return the localized script name.
   1527      * @internal
   1528      * @deprecated This API is ICU internal only.
   1529      */
   1530     @Deprecated
   1531     public String getDisplayScriptInContext(ULocale displayLocale) {
   1532         return getDisplayScriptInContextInternal(this, displayLocale);
   1533     }
   1534 
   1535     /**
   1536      * {@icu} Returns a locale's script localized for display in the provided locale.
   1537      * This is a cover for the ICU4C API.
   1538      * @param localeID the id of the locale whose script will be displayed
   1539      * @param displayLocaleID the id of the locale in which to display the name.
   1540      * @return the localized script name.
   1541      * @stable ICU 3.0
   1542      */
   1543     public static String getDisplayScript(String localeID, String displayLocaleID) {
   1544         return getDisplayScriptInternal(new ULocale(localeID), new ULocale(displayLocaleID));
   1545     }
   1546     /**
   1547      * {@icu} Returns a locale's script localized for display in the provided locale.
   1548      * This is a cover for the ICU4C API.
   1549      * @param localeID the id of the locale whose script will be displayed
   1550      * @param displayLocaleID the id of the locale in which to display the name.
   1551      * @return the localized script name.
   1552      * @internal
   1553      * @deprecated This API is ICU internal only.
   1554      */
   1555     @Deprecated
   1556     public static String getDisplayScriptInContext(String localeID, String displayLocaleID) {
   1557         return getDisplayScriptInContextInternal(new ULocale(localeID), new ULocale(displayLocaleID));
   1558     }
   1559 
   1560     /**
   1561      * {@icu} Returns a locale's script localized for display in the provided locale.
   1562      * @param localeID the id of the locale whose script will be displayed.
   1563      * @param displayLocale the locale in which to display the name.
   1564      * @return the localized script name.
   1565      * @stable ICU 3.0
   1566      */
   1567     public static String getDisplayScript(String localeID, ULocale displayLocale) {
   1568         return getDisplayScriptInternal(new ULocale(localeID), displayLocale);
   1569     }
   1570     /**
   1571      * {@icu} Returns a locale's script localized for display in the provided locale.
   1572      * @param localeID the id of the locale whose script will be displayed.
   1573      * @param displayLocale the locale in which to display the name.
   1574      * @return the localized script name.
   1575      * @internal
   1576      * @deprecated This API is ICU internal only.
   1577      */
   1578     @Deprecated
   1579     public static String getDisplayScriptInContext(String localeID, ULocale displayLocale) {
   1580         return getDisplayScriptInContextInternal(new ULocale(localeID), displayLocale);
   1581     }
   1582 
   1583     // displayLocaleID is canonical, localeID need not be since parsing will fix this.
   1584     private static String getDisplayScriptInternal(ULocale locale, ULocale displayLocale) {
   1585         return LocaleDisplayNames.getInstance(displayLocale)
   1586                 .scriptDisplayName(locale.getScript());
   1587     }
   1588 
   1589     private static String getDisplayScriptInContextInternal(ULocale locale, ULocale displayLocale) {
   1590         return LocaleDisplayNames.getInstance(displayLocale)
   1591                 .scriptDisplayNameInContext(locale.getScript());
   1592     }
   1593 
   1594     /**
   1595      * Returns this locale's country localized for display in the default <code>DISPLAY</code> locale.
   1596      * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR").
   1597      * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead.
   1598      * @return the localized country name.
   1599      * @see Category#DISPLAY
   1600      * @stable ICU 3.0
   1601      */
   1602     public String getDisplayCountry() {
   1603         return getDisplayCountryInternal(this, getDefault(Category.DISPLAY));
   1604     }
   1605 
   1606     /**
   1607      * Returns this locale's country localized for display in the provided locale.
   1608      * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR").
   1609      * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead.
   1610      * @param displayLocale the locale in which to display the name.
   1611      * @return the localized country name.
   1612      * @stable ICU 3.0
   1613      */
   1614     public String getDisplayCountry(ULocale displayLocale){
   1615         return getDisplayCountryInternal(this, displayLocale);
   1616     }
   1617 
   1618     /**
   1619      * {@icu} Returns a locale's country localized for display in the provided locale.
   1620      * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR").
   1621      * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead.
   1622      * This is a cover for the ICU4C API.
   1623      * @param localeID the id of the locale whose country will be displayed
   1624      * @param displayLocaleID the id of the locale in which to display the name.
   1625      * @return the localized country name.
   1626      * @stable ICU 3.0
   1627      */
   1628     public static String getDisplayCountry(String localeID, String displayLocaleID) {
   1629         return getDisplayCountryInternal(new ULocale(localeID), new ULocale(displayLocaleID));
   1630     }
   1631 
   1632     /**
   1633      * {@icu} Returns a locale's country localized for display in the provided locale.
   1634      * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR").
   1635      * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead.
   1636      * This is a cover for the ICU4C API.
   1637      * @param localeID the id of the locale whose country will be displayed.
   1638      * @param displayLocale the locale in which to display the name.
   1639      * @return the localized country name.
   1640      * @stable ICU 3.0
   1641      */
   1642     public static String getDisplayCountry(String localeID, ULocale displayLocale) {
   1643         return getDisplayCountryInternal(new ULocale(localeID), displayLocale);
   1644     }
   1645 
   1646     // displayLocaleID is canonical, localeID need not be since parsing will fix this.
   1647     private static String getDisplayCountryInternal(ULocale locale, ULocale displayLocale) {
   1648         return LocaleDisplayNames.getInstance(displayLocale)
   1649                 .regionDisplayName(locale.getCountry());
   1650     }
   1651 
   1652     /**
   1653      * Returns this locale's variant localized for display in the default <code>DISPLAY</code> locale.
   1654      * @return the localized variant name.
   1655      * @see Category#DISPLAY
   1656      * @stable ICU 3.0
   1657      */
   1658     public String getDisplayVariant() {
   1659         return getDisplayVariantInternal(this, getDefault(Category.DISPLAY));
   1660     }
   1661 
   1662     /**
   1663      * Returns this locale's variant localized for display in the provided locale.
   1664      * @param displayLocale the locale in which to display the name.
   1665      * @return the localized variant name.
   1666      * @stable ICU 3.0
   1667      */
   1668     public String getDisplayVariant(ULocale displayLocale) {
   1669         return getDisplayVariantInternal(this, displayLocale);
   1670     }
   1671 
   1672     /**
   1673      * {@icu} Returns a locale's variant localized for display in the provided locale.
   1674      * This is a cover for the ICU4C API.
   1675      * @param localeID the id of the locale whose variant will be displayed
   1676      * @param displayLocaleID the id of the locale in which to display the name.
   1677      * @return the localized variant name.
   1678      * @stable ICU 3.0
   1679      */
   1680     public static String getDisplayVariant(String localeID, String displayLocaleID){
   1681         return getDisplayVariantInternal(new ULocale(localeID), new ULocale(displayLocaleID));
   1682     }
   1683 
   1684     /**
   1685      * {@icu} Returns a locale's variant localized for display in the provided locale.
   1686      * This is a cover for the ICU4C API.
   1687      * @param localeID the id of the locale whose variant will be displayed.
   1688      * @param displayLocale the locale in which to display the name.
   1689      * @return the localized variant name.
   1690      * @stable ICU 3.0
   1691      */
   1692     public static String getDisplayVariant(String localeID, ULocale displayLocale) {
   1693         return getDisplayVariantInternal(new ULocale(localeID), displayLocale);
   1694     }
   1695 
   1696     private static String getDisplayVariantInternal(ULocale locale, ULocale displayLocale) {
   1697         return LocaleDisplayNames.getInstance(displayLocale)
   1698                 .variantDisplayName(locale.getVariant());
   1699     }
   1700 
   1701     /**
   1702      * {@icu} Returns a keyword localized for display in the default <code>DISPLAY</code> locale.
   1703      * @param keyword the keyword to be displayed.
   1704      * @return the localized keyword name.
   1705      * @see #getKeywords()
   1706      * @see Category#DISPLAY
   1707      * @stable ICU 3.0
   1708      */
   1709     public static String getDisplayKeyword(String keyword) {
   1710         return getDisplayKeywordInternal(keyword, getDefault(Category.DISPLAY));
   1711     }
   1712 
   1713     /**
   1714      * {@icu} Returns a keyword localized for display in the specified locale.
   1715      * @param keyword the keyword to be displayed.
   1716      * @param displayLocaleID the id of the locale in which to display the keyword.
   1717      * @return the localized keyword name.
   1718      * @see #getKeywords(String)
   1719      * @stable ICU 3.0
   1720      */
   1721     public static String getDisplayKeyword(String keyword, String displayLocaleID) {
   1722         return getDisplayKeywordInternal(keyword, new ULocale(displayLocaleID));
   1723     }
   1724 
   1725     /**
   1726      * {@icu} Returns a keyword localized for display in the specified locale.
   1727      * @param keyword the keyword to be displayed.
   1728      * @param displayLocale the locale in which to display the keyword.
   1729      * @return the localized keyword name.
   1730      * @see #getKeywords(String)
   1731      * @stable ICU 3.0
   1732      */
   1733     public static String getDisplayKeyword(String keyword, ULocale displayLocale) {
   1734         return getDisplayKeywordInternal(keyword, displayLocale);
   1735     }
   1736 
   1737     private static String getDisplayKeywordInternal(String keyword, ULocale displayLocale) {
   1738         return LocaleDisplayNames.getInstance(displayLocale).keyDisplayName(keyword);
   1739     }
   1740 
   1741     /**
   1742      * {@icu} Returns a keyword value localized for display in the default <code>DISPLAY</code> locale.
   1743      * @param keyword the keyword whose value is to be displayed.
   1744      * @return the localized value name.
   1745      * @see Category#DISPLAY
   1746      * @stable ICU 3.0
   1747      */
   1748     public String getDisplayKeywordValue(String keyword) {
   1749         return getDisplayKeywordValueInternal(this, keyword, getDefault(Category.DISPLAY));
   1750     }
   1751 
   1752     /**
   1753      * {@icu} Returns a keyword value localized for display in the specified locale.
   1754      * @param keyword the keyword whose value is to be displayed.
   1755      * @param displayLocale the locale in which to display the value.
   1756      * @return the localized value name.
   1757      * @stable ICU 3.0
   1758      */
   1759     public String getDisplayKeywordValue(String keyword, ULocale displayLocale) {
   1760         return getDisplayKeywordValueInternal(this, keyword, displayLocale);
   1761     }
   1762 
   1763     /**
   1764      * {@icu} Returns a keyword value localized for display in the specified locale.
   1765      * This is a cover for the ICU4C API.
   1766      * @param localeID the id of the locale whose keyword value is to be displayed.
   1767      * @param keyword the keyword whose value is to be displayed.
   1768      * @param displayLocaleID the id of the locale in which to display the value.
   1769      * @return the localized value name.
   1770      * @stable ICU 3.0
   1771      */
   1772     public static String getDisplayKeywordValue(String localeID, String keyword,
   1773             String displayLocaleID) {
   1774         return getDisplayKeywordValueInternal(new ULocale(localeID), keyword,
   1775                 new ULocale(displayLocaleID));
   1776     }
   1777 
   1778     /**
   1779      * {@icu} Returns a keyword value localized for display in the specified locale.
   1780      * This is a cover for the ICU4C API.
   1781      * @param localeID the id of the locale whose keyword value is to be displayed.
   1782      * @param keyword the keyword whose value is to be displayed.
   1783      * @param displayLocale the id of the locale in which to display the value.
   1784      * @return the localized value name.
   1785      * @stable ICU 3.0
   1786      */
   1787     public static String getDisplayKeywordValue(String localeID, String keyword,
   1788             ULocale displayLocale) {
   1789         return getDisplayKeywordValueInternal(new ULocale(localeID), keyword, displayLocale);
   1790     }
   1791 
   1792     // displayLocaleID is canonical, localeID need not be since parsing will fix this.
   1793     private static String getDisplayKeywordValueInternal(ULocale locale, String keyword,
   1794             ULocale displayLocale) {
   1795         keyword = AsciiUtil.toLowerString(keyword.trim());
   1796         String value = locale.getKeywordValue(keyword);
   1797         return LocaleDisplayNames.getInstance(displayLocale).keyValueDisplayName(keyword, value);
   1798     }
   1799 
   1800     /**
   1801      * Returns this locale name localized for display in the default <code>DISPLAY</code> locale.
   1802      * @return the localized locale name.
   1803      * @see Category#DISPLAY
   1804      * @stable ICU 3.0
   1805      */
   1806     public String getDisplayName() {
   1807         return getDisplayNameInternal(this, getDefault(Category.DISPLAY));
   1808     }
   1809 
   1810     /**
   1811      * Returns this locale name localized for display in the provided locale.
   1812      * @param displayLocale the locale in which to display the locale name.
   1813      * @return the localized locale name.
   1814      * @stable ICU 3.0
   1815      */
   1816     public String getDisplayName(ULocale displayLocale) {
   1817         return getDisplayNameInternal(this, displayLocale);
   1818     }
   1819 
   1820     /**
   1821      * {@icu} Returns the locale ID localized for display in the provided locale.
   1822      * This is a cover for the ICU4C API.
   1823      * @param localeID the locale whose name is to be displayed.
   1824      * @param displayLocaleID the id of the locale in which to display the locale name.
   1825      * @return the localized locale name.
   1826      * @stable ICU 3.0
   1827      */
   1828     public static String getDisplayName(String localeID, String displayLocaleID) {
   1829         return getDisplayNameInternal(new ULocale(localeID), new ULocale(displayLocaleID));
   1830     }
   1831 
   1832     /**
   1833      * {@icu} Returns the locale ID localized for display in the provided locale.
   1834      * This is a cover for the ICU4C API.
   1835      * @param localeID the locale whose name is to be displayed.
   1836      * @param displayLocale the locale in which to display the locale name.
   1837      * @return the localized locale name.
   1838      * @stable ICU 3.0
   1839      */
   1840     public static String getDisplayName(String localeID, ULocale displayLocale) {
   1841         return getDisplayNameInternal(new ULocale(localeID), displayLocale);
   1842     }
   1843 
   1844     private static String getDisplayNameInternal(ULocale locale, ULocale displayLocale) {
   1845         return LocaleDisplayNames.getInstance(displayLocale).localeDisplayName(locale);
   1846     }
   1847 
   1848     /**
   1849      * {@icu} Returns this locale name localized for display in the default <code>DISPLAY</code> locale.
   1850      * If a dialect name is present in the locale data, then it is returned.
   1851      * @return the localized locale name.
   1852      * @see Category#DISPLAY
   1853      * @stable ICU 4.4
   1854      */
   1855     public String getDisplayNameWithDialect() {
   1856         return getDisplayNameWithDialectInternal(this, getDefault(Category.DISPLAY));
   1857     }
   1858 
   1859     /**
   1860      * {@icu} Returns this locale name localized for display in the provided locale.
   1861      * If a dialect name is present in the locale data, then it is returned.
   1862      * @param displayLocale the locale in which to display the locale name.
   1863      * @return the localized locale name.
   1864      * @stable ICU 4.4
   1865      */
   1866     public String getDisplayNameWithDialect(ULocale displayLocale) {
   1867         return getDisplayNameWithDialectInternal(this, displayLocale);
   1868     }
   1869 
   1870     /**
   1871      * {@icu} Returns the locale ID localized for display in the provided locale.
   1872      * If a dialect name is present in the locale data, then it is returned.
   1873      * This is a cover for the ICU4C API.
   1874      * @param localeID the locale whose name is to be displayed.
   1875      * @param displayLocaleID the id of the locale in which to display the locale name.
   1876      * @return the localized locale name.
   1877      * @stable ICU 4.4
   1878      */
   1879     public static String getDisplayNameWithDialect(String localeID, String displayLocaleID) {
   1880         return getDisplayNameWithDialectInternal(new ULocale(localeID),
   1881                 new ULocale(displayLocaleID));
   1882     }
   1883 
   1884     /**
   1885      * {@icu} Returns the locale ID localized for display in the provided locale.
   1886      * If a dialect name is present in the locale data, then it is returned.
   1887      * This is a cover for the ICU4C API.
   1888      * @param localeID the locale whose name is to be displayed.
   1889      * @param displayLocale the locale in which to display the locale name.
   1890      * @return the localized locale name.
   1891      * @stable ICU 4.4
   1892      */
   1893     public static String getDisplayNameWithDialect(String localeID, ULocale displayLocale) {
   1894         return getDisplayNameWithDialectInternal(new ULocale(localeID), displayLocale);
   1895     }
   1896 
   1897     private static String getDisplayNameWithDialectInternal(ULocale locale, ULocale displayLocale) {
   1898         return LocaleDisplayNames.getInstance(displayLocale, DialectHandling.DIALECT_NAMES)
   1899                 .localeDisplayName(locale);
   1900     }
   1901 
   1902     /**
   1903      * {@icu} Returns this locale's layout orientation for characters.  The possible
   1904      * values are "left-to-right", "right-to-left", "top-to-bottom" or
   1905      * "bottom-to-top".
   1906      * @return The locale's layout orientation for characters.
   1907      * @stable ICU 4.0
   1908      */
   1909     public String getCharacterOrientation() {
   1910         return ICUResourceTableAccess.getTableString(ICUData.ICU_BASE_NAME, this,
   1911                 "layout", "characters", "characters");
   1912     }
   1913 
   1914     /**
   1915      * {@icu} Returns this locale's layout orientation for lines.  The possible
   1916      * values are "left-to-right", "right-to-left", "top-to-bottom" or
   1917      * "bottom-to-top".
   1918      * @return The locale's layout orientation for lines.
   1919      * @stable ICU 4.0
   1920      */
   1921     public String getLineOrientation() {
   1922         return ICUResourceTableAccess.getTableString(ICUData.ICU_BASE_NAME, this,
   1923                 "layout", "lines", "lines");
   1924     }
   1925 
   1926     /**
   1927      * {@icu} Selector for <tt>getLocale()</tt> indicating the locale of the
   1928      * resource containing the data.  This is always at or above the
   1929      * valid locale.  If the valid locale does not contain the
   1930      * specific data being requested, then the actual locale will be
   1931      * above the valid locale.  If the object was not constructed from
   1932      * locale data, then the valid locale is <i>null</i>.
   1933      *
   1934      * @draft ICU 2.8 (retain)
   1935      * @provisional This API might change or be removed in a future release.
   1936      */
   1937     public static Type ACTUAL_LOCALE = new Type();
   1938 
   1939     /**
   1940      * {@icu} Selector for <tt>getLocale()</tt> indicating the most specific
   1941      * locale for which any data exists.  This is always at or above
   1942      * the requested locale, and at or below the actual locale.  If
   1943      * the requested locale does not correspond to any resource data,
   1944      * then the valid locale will be above the requested locale.  If
   1945      * the object was not constructed from locale data, then the
   1946      * actual locale is <i>null</i>.
   1947      *
   1948      * <p>Note: The valid locale will be returned correctly in ICU
   1949      * 3.0 or later.  In ICU 2.8, it is not returned correctly.
   1950      * @draft ICU 2.8 (retain)
   1951      * @provisional This API might change or be removed in a future release.
   1952      */
   1953     public static Type VALID_LOCALE = new Type();
   1954 
   1955     /**
   1956      * Opaque selector enum for <tt>getLocale()</tt>.
   1957      * @see com.ibm.icu.util.ULocale
   1958      * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
   1959      * @see com.ibm.icu.util.ULocale#VALID_LOCALE
   1960      * @draft ICU 2.8 (retainAll)
   1961      * @provisional This API might change or be removed in a future release.
   1962      */
   1963     public static final class Type {
   1964         private Type() {}
   1965     }
   1966 
   1967     /**
   1968      * {@icu} Based on a HTTP formatted list of acceptable locales, determine an available
   1969      * locale for the user.  NullPointerException is thrown if acceptLanguageList or
   1970      * availableLocales is null.  If fallback is non-null, it will contain true if a
   1971      * fallback locale (one not in the acceptLanguageList) was returned.  The value on
   1972      * entry is ignored.  ULocale will be one of the locales in availableLocales, or the
   1973      * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
   1974      * availableLocales matched).  No ULocale array element should be null; behavior is
   1975      * undefined if this is the case.
   1976      * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
   1977      * @param availableLocales list of available locales. One of these will be returned.
   1978      * @param fallback if non-null, a 1-element array containing a boolean to be set with
   1979      * the fallback status
   1980      * @return one of the locales from the availableLocales list, or null if none match
   1981      * @stable ICU 3.4
   1982      */
   1983     public static ULocale acceptLanguage(String acceptLanguageList, ULocale[] availableLocales,
   1984             boolean[] fallback) {
   1985         if (acceptLanguageList == null) {
   1986             throw new NullPointerException();
   1987         }
   1988         ULocale acceptList[] = null;
   1989         try {
   1990             acceptList = parseAcceptLanguage(acceptLanguageList, true);
   1991         } catch (ParseException pe) {
   1992             acceptList = null;
   1993         }
   1994         if (acceptList == null) {
   1995             return null;
   1996         }
   1997         return acceptLanguage(acceptList, availableLocales, fallback);
   1998     }
   1999 
   2000     /**
   2001      * {@icu} Based on a list of acceptable locales, determine an available locale for the
   2002      * user.  NullPointerException is thrown if acceptLanguageList or availableLocales is
   2003      * null.  If fallback is non-null, it will contain true if a fallback locale (one not
   2004      * in the acceptLanguageList) was returned.  The value on entry is ignored.  ULocale
   2005      * will be one of the locales in availableLocales, or the ROOT ULocale if if a ROOT
   2006      * locale was used as a fallback (because nothing else in availableLocales matched).
   2007      * No ULocale array element should be null; behavior is undefined if this is the case.
   2008      * @param acceptLanguageList list of acceptable locales
   2009      * @param availableLocales list of available locales. One of these will be returned.
   2010      * @param fallback if non-null, a 1-element array containing a boolean to be set with
   2011      * the fallback status
   2012      * @return one of the locales from the availableLocales list, or null if none match
   2013      * @stable ICU 3.4
   2014      */
   2015 
   2016     public static ULocale acceptLanguage(ULocale[] acceptLanguageList, ULocale[]
   2017             availableLocales, boolean[] fallback) {
   2018         // fallbacklist
   2019         int i,j;
   2020         if(fallback != null) {
   2021             fallback[0]=true;
   2022         }
   2023         for(i=0;i<acceptLanguageList.length;i++) {
   2024             ULocale aLocale = acceptLanguageList[i];
   2025             boolean[] setFallback = fallback;
   2026             do {
   2027                 for(j=0;j<availableLocales.length;j++) {
   2028                     if(availableLocales[j].equals(aLocale)) {
   2029                         if(setFallback != null) {
   2030                             setFallback[0]=false; // first time with this locale - not a fallback.
   2031                         }
   2032                         return availableLocales[j];
   2033                     }
   2034                     // compare to scriptless alias, so locales such as
   2035                     // zh_TW, zh_CN are considered as available locales - see #7190
   2036                     if (aLocale.getScript().length() == 0
   2037                             && availableLocales[j].getScript().length() > 0
   2038                             && availableLocales[j].getLanguage().equals(aLocale.getLanguage())
   2039                             && availableLocales[j].getCountry().equals(aLocale.getCountry())
   2040                             && availableLocales[j].getVariant().equals(aLocale.getVariant())) {
   2041                         ULocale minAvail = ULocale.minimizeSubtags(availableLocales[j]);
   2042                         if (minAvail.getScript().length() == 0) {
   2043                             if(setFallback != null) {
   2044                                 setFallback[0] = false; // not a fallback.
   2045                             }
   2046                             return aLocale;
   2047                         }
   2048                     }
   2049                 }
   2050                 Locale loc = aLocale.toLocale();
   2051                 Locale parent = LocaleUtility.fallback(loc);
   2052                 if(parent != null) {
   2053                     aLocale = new ULocale(parent);
   2054                 } else {
   2055                     aLocale = null;
   2056                 }
   2057                 setFallback = null; // Do not set fallback in later iterations
   2058             } while (aLocale != null);
   2059         }
   2060         return null;
   2061     }
   2062 
   2063     /**
   2064      * {@icu} Based on a HTTP formatted list of acceptable locales, determine an available
   2065      * locale for the user.  NullPointerException is thrown if acceptLanguageList or
   2066      * availableLocales is null.  If fallback is non-null, it will contain true if a
   2067      * fallback locale (one not in the acceptLanguageList) was returned.  The value on
   2068      * entry is ignored.  ULocale will be one of the locales in availableLocales, or the
   2069      * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
   2070      * availableLocales matched).  No ULocale array element should be null; behavior is
   2071      * undefined if this is the case.  This function will choose a locale from the
   2072      * ULocale.getAvailableLocales() list as available.
   2073      * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
   2074      * @param fallback if non-null, a 1-element array containing a boolean to be set with
   2075      * the fallback status
   2076      * @return one of the locales from the ULocale.getAvailableLocales() list, or null if
   2077      * none match
   2078      * @stable ICU 3.4
   2079      */
   2080     public static ULocale acceptLanguage(String acceptLanguageList, boolean[] fallback) {
   2081         return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
   2082                 fallback);
   2083     }
   2084 
   2085     /**
   2086      * {@icu} Based on an ordered array of acceptable locales, determine an available
   2087      * locale for the user.  NullPointerException is thrown if acceptLanguageList or
   2088      * availableLocales is null.  If fallback is non-null, it will contain true if a
   2089      * fallback locale (one not in the acceptLanguageList) was returned.  The value on
   2090      * entry is ignored.  ULocale will be one of the locales in availableLocales, or the
   2091      * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
   2092      * availableLocales matched).  No ULocale array element should be null; behavior is
   2093      * undefined if this is the case.  This function will choose a locale from the
   2094      * ULocale.getAvailableLocales() list as available.
   2095      * @param acceptLanguageList ordered array of acceptable locales (preferred are listed first)
   2096      * @param fallback if non-null, a 1-element array containing a boolean to be set with
   2097      * the fallback status
   2098      * @return one of the locales from the ULocale.getAvailableLocales() list, or null if none match
   2099      * @stable ICU 3.4
   2100      */
   2101     public static ULocale acceptLanguage(ULocale[] acceptLanguageList, boolean[] fallback) {
   2102         return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
   2103                 fallback);
   2104     }
   2105 
   2106     /**
   2107      * Package local method used for parsing Accept-Language string
   2108      */
   2109     static ULocale[] parseAcceptLanguage(String acceptLanguage, boolean isLenient)
   2110             throws ParseException {
   2111         class ULocaleAcceptLanguageQ implements Comparable<ULocaleAcceptLanguageQ> {
   2112             private double q;
   2113             private double serial;
   2114             public ULocaleAcceptLanguageQ(double theq, int theserial) {
   2115                 q = theq;
   2116                 serial = theserial;
   2117             }
   2118             @Override
   2119             public int compareTo(ULocaleAcceptLanguageQ other) {
   2120                 if (q > other.q) { // reverse - to sort in descending order
   2121                     return -1;
   2122                 } else if (q < other.q) {
   2123                     return 1;
   2124                 }
   2125                 if (serial < other.serial) {
   2126                     return -1;
   2127                 } else if (serial > other.serial) {
   2128                     return 1;
   2129                 } else {
   2130                     return 0; // same object
   2131                 }
   2132             }
   2133         }
   2134 
   2135         // parse out the acceptLanguage into an array
   2136         TreeMap<ULocaleAcceptLanguageQ, ULocale> map =
   2137                 new TreeMap<>();
   2138         StringBuilder languageRangeBuf = new StringBuilder();
   2139         StringBuilder qvalBuf = new StringBuilder();
   2140         int state = 0;
   2141         acceptLanguage += ","; // append comma to simplify the parsing code
   2142         int n;
   2143         boolean subTag = false;
   2144         boolean q1 = false;
   2145         for (n = 0; n < acceptLanguage.length(); n++) {
   2146             boolean gotLanguageQ = false;
   2147             char c = acceptLanguage.charAt(n);
   2148             switch (state) {
   2149             case 0: // before language-range start
   2150                 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
   2151                     // in language-range
   2152                     languageRangeBuf.append(c);
   2153                     state = 1;
   2154                     subTag = false;
   2155                 } else if (c == '*') {
   2156                     languageRangeBuf.append(c);
   2157                     state = 2;
   2158                 } else if (c != ' ' && c != '\t') {
   2159                     // invalid character
   2160                     state = -1;
   2161                 }
   2162                 break;
   2163             case 1: // in language-range
   2164                 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
   2165                     languageRangeBuf.append(c);
   2166                 } else if (c == '-') {
   2167                     subTag = true;
   2168                     languageRangeBuf.append(c);
   2169                 } else if (c == '_') {
   2170                     if (isLenient) {
   2171                         subTag = true;
   2172                         languageRangeBuf.append(c);
   2173                     } else {
   2174                         state = -1;
   2175                     }
   2176                 } else if ('0' <= c && c <= '9') {
   2177                     if (subTag) {
   2178                         languageRangeBuf.append(c);
   2179                     } else {
   2180                         // DIGIT is allowed only in language sub tag
   2181                         state = -1;
   2182                     }
   2183                 } else if (c == ',') {
   2184                     // language-q end
   2185                     gotLanguageQ = true;
   2186                 } else if (c == ' ' || c == '\t') {
   2187                     // language-range end
   2188                     state = 3;
   2189                 } else if (c == ';') {
   2190                     // before q
   2191                     state = 4;
   2192                 } else {
   2193                     // invalid character for language-range
   2194                     state = -1;
   2195                 }
   2196                 break;
   2197             case 2: // saw wild card range
   2198                 if (c == ',') {
   2199                     // language-q end
   2200                     gotLanguageQ = true;
   2201                 } else if (c == ' ' || c == '\t') {
   2202                     // language-range end
   2203                     state = 3;
   2204                 } else if (c == ';') {
   2205                     // before q
   2206                     state = 4;
   2207                 } else {
   2208                     // invalid
   2209                     state = -1;
   2210                 }
   2211                 break;
   2212             case 3: // language-range end
   2213                 if (c == ',') {
   2214                     // language-q end
   2215                     gotLanguageQ = true;
   2216                 } else if (c == ';') {
   2217                     // before q
   2218                     state =4;
   2219                 } else if (c != ' ' && c != '\t') {
   2220                     // invalid
   2221                     state = -1;
   2222                 }
   2223                 break;
   2224             case 4: // before q
   2225                 if (c == 'q') {
   2226                     // before equal
   2227                     state = 5;
   2228                 } else if (c != ' ' && c != '\t') {
   2229                     // invalid
   2230                     state = -1;
   2231                 }
   2232                 break;
   2233             case 5: // before equal
   2234                 if (c == '=') {
   2235                     // before q value
   2236                     state = 6;
   2237                 } else if (c != ' ' && c != '\t') {
   2238                     // invalid
   2239                     state = -1;
   2240                 }
   2241                 break;
   2242             case 6: // before q value
   2243                 if (c == '0') {
   2244                     // q value start with 0
   2245                     q1 = false;
   2246                     qvalBuf.append(c);
   2247                     state = 7;
   2248                 } else if (c == '1') {
   2249                     // q value start with 1
   2250                     qvalBuf.append(c);
   2251                     state = 7;
   2252                 } else if (c == '.') {
   2253                     if (isLenient) {
   2254                         qvalBuf.append(c);
   2255                         state = 8;
   2256                     } else {
   2257                         state = -1;
   2258                     }
   2259                 } else if (c != ' ' && c != '\t') {
   2260                     // invalid
   2261                     state = -1;
   2262                 }
   2263                 break;
   2264             case 7: // q value start
   2265                 if (c == '.') {
   2266                     // before q value fraction part
   2267                     qvalBuf.append(c);
   2268                     state = 8;
   2269                 } else if (c == ',') {
   2270                     // language-q end
   2271                     gotLanguageQ = true;
   2272                 } else if (c == ' ' || c == '\t') {
   2273                     // after q value
   2274                     state = 10;
   2275                 } else {
   2276                     // invalid
   2277                     state = -1;
   2278                 }
   2279                 break;
   2280             case 8: // before q value fraction part
   2281                 if ('0' <= c && c <= '9') {
   2282                     if (q1 && c != '0' && !isLenient) {
   2283                         // if q value starts with 1, the fraction part must be 0
   2284                         state = -1;
   2285                     } else {
   2286                         // in q value fraction part
   2287                         qvalBuf.append(c);
   2288                         state = 9;
   2289                     }
   2290                 } else {
   2291                     // invalid
   2292                     state = -1;
   2293                 }
   2294                 break;
   2295             case 9: // in q value fraction part
   2296                 if ('0' <= c && c <= '9') {
   2297                     if (q1 && c != '0') {
   2298                         // if q value starts with 1, the fraction part must be 0
   2299                         state = -1;
   2300                     } else {
   2301                         qvalBuf.append(c);
   2302                     }
   2303                 } else if (c == ',') {
   2304                     // language-q end
   2305                     gotLanguageQ = true;
   2306                 } else if (c == ' ' || c == '\t') {
   2307                     // after q value
   2308                     state = 10;
   2309                 } else {
   2310                     // invalid
   2311                     state = -1;
   2312                 }
   2313                 break;
   2314             case 10: // after q value
   2315                 if (c == ',') {
   2316                     // language-q end
   2317                     gotLanguageQ = true;
   2318                 } else if (c != ' ' && c != '\t') {
   2319                     // invalid
   2320                     state = -1;
   2321                 }
   2322                 break;
   2323             }
   2324             if (state == -1) {
   2325                 // error state
   2326                 throw new ParseException("Invalid Accept-Language", n);
   2327             }
   2328             if (gotLanguageQ) {
   2329                 double q = 1.0;
   2330                 if (qvalBuf.length() != 0) {
   2331                     try {
   2332                         q = Double.parseDouble(qvalBuf.toString());
   2333                     } catch (NumberFormatException nfe) {
   2334                         // Already validated, so it should never happen
   2335                         q = 1.0;
   2336                     }
   2337                     if (q > 1.0) {
   2338                         q = 1.0;
   2339                     }
   2340                 }
   2341                 if (languageRangeBuf.charAt(0) != '*') {
   2342                     int serial = map.size();
   2343                     ULocaleAcceptLanguageQ entry = new ULocaleAcceptLanguageQ(q, serial);
   2344                     // sort in reverse order..   1.0, 0.9, 0.8 .. etc
   2345                     map.put(entry, new ULocale(canonicalize(languageRangeBuf.toString())));
   2346                 }
   2347 
   2348                 // reset buffer and parse state
   2349                 languageRangeBuf.setLength(0);
   2350                 qvalBuf.setLength(0);
   2351                 state = 0;
   2352             }
   2353         }
   2354         if (state != 0) {
   2355             // Well, the parser should handle all cases.  So just in case.
   2356             throw new ParseException("Invalid AcceptlLanguage", n);
   2357         }
   2358 
   2359         // pull out the map
   2360         ULocale acceptList[] = map.values().toArray(new ULocale[map.size()]);
   2361         return acceptList;
   2362     }
   2363 
   2364     private static final String UNDEFINED_LANGUAGE = "und";
   2365     private static final String UNDEFINED_SCRIPT = "Zzzz";
   2366     private static final String UNDEFINED_REGION = "ZZ";
   2367 
   2368     /**
   2369      * {@icu} Adds the likely subtags for a provided locale ID, per the algorithm
   2370      * described in the following CLDR technical report:
   2371      *
   2372      *   http://www.unicode.org/reports/tr35/#Likely_Subtags
   2373      *
   2374      * If the provided ULocale instance is already in the maximal form, or there is no
   2375      * data available available for maximization, it will be returned.  For example,
   2376      * "und-Zzzz" cannot be maximized, since there is no reasonable maximization.
   2377      * Otherwise, a new ULocale instance with the maximal form is returned.
   2378      *
   2379      * Examples:
   2380      *
   2381      * "en" maximizes to "en_Latn_US"
   2382      *
   2383      * "de" maximizes to "de_Latn_US"
   2384      *
   2385      * "sr" maximizes to "sr_Cyrl_RS"
   2386      *
   2387      * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.)
   2388      *
   2389      * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.)
   2390      *
   2391      * @param loc The ULocale to maximize
   2392      * @return The maximized ULocale instance.
   2393      * @stable ICU 4.0
   2394      */
   2395     public static ULocale addLikelySubtags(ULocale loc) {
   2396         String[] tags = new String[3];
   2397         String trailing = null;
   2398 
   2399         int trailingIndex = parseTagString(
   2400                 loc.localeID,
   2401                 tags);
   2402 
   2403         if (trailingIndex < loc.localeID.length()) {
   2404             trailing = loc.localeID.substring(trailingIndex);
   2405         }
   2406 
   2407         String newLocaleID =
   2408                 createLikelySubtagsString(
   2409                         tags[0],
   2410                         tags[1],
   2411                         tags[2],
   2412                         trailing);
   2413 
   2414         return newLocaleID == null ? loc : new ULocale(newLocaleID);
   2415     }
   2416 
   2417     /**
   2418      * {@icu} Minimizes the subtags for a provided locale ID, per the algorithm described
   2419      * in the following CLDR technical report:<blockquote>
   2420      *
   2421      *   <a href="http://www.unicode.org/reports/tr35/#Likely_Subtags"
   2422      *>http://www.unicode.org/reports/tr35/#Likely_Subtags</a></blockquote>
   2423      *
   2424      * If the provided ULocale instance is already in the minimal form, or there
   2425      * is no data available for minimization, it will be returned.  Since the
   2426      * minimization algorithm relies on proper maximization, see the comments
   2427      * for addLikelySubtags for reasons why there might not be any data.
   2428      *
   2429      * Examples:<pre>
   2430      *
   2431      * "en_Latn_US" minimizes to "en"
   2432      *
   2433      * "de_Latn_US" minimizes to "de"
   2434      *
   2435      * "sr_Cyrl_RS" minimizes to "sr"
   2436      *
   2437      * "zh_Hant_TW" minimizes to "zh_TW" (The region is preferred to the
   2438      * script, and minimizing to "zh" would imply "zh_Hans_CN".) </pre>
   2439      *
   2440      * @param loc The ULocale to minimize
   2441      * @return The minimized ULocale instance.
   2442      * @stable ICU 4.0
   2443      */
   2444     public static ULocale minimizeSubtags(ULocale loc) {
   2445         return minimizeSubtags(loc, Minimize.FAVOR_REGION);
   2446     }
   2447 
   2448     /**
   2449      * Options for minimizeSubtags.
   2450      * @internal
   2451      * @deprecated This API is ICU internal only.
   2452      */
   2453     @Deprecated
   2454     public enum Minimize {
   2455         /**
   2456          * Favor including the script, when either the region <b>or</b> the script could be suppressed, but not both.
   2457          * @internal
   2458          * @deprecated This API is ICU internal only.
   2459          */
   2460         @Deprecated
   2461         FAVOR_SCRIPT,
   2462         /**
   2463          * Favor including the region, when either the region <b>or</b> the script could be suppressed, but not both.
   2464          * @internal
   2465          * @deprecated This API is ICU internal only.
   2466          */
   2467         @Deprecated
   2468         FAVOR_REGION
   2469     }
   2470 
   2471     /**
   2472      * {@icu} Minimizes the subtags for a provided locale ID, per the algorithm described
   2473      * in the following CLDR technical report:<blockquote>
   2474      *
   2475      *   <a href="http://www.unicode.org/reports/tr35/#Likely_Subtags"
   2476      *>http://www.unicode.org/reports/tr35/#Likely_Subtags</a></blockquote>
   2477      *
   2478      * If the provided ULocale instance is already in the minimal form, or there
   2479      * is no data available for minimization, it will be returned.  Since the
   2480      * minimization algorithm relies on proper maximization, see the comments
   2481      * for addLikelySubtags for reasons why there might not be any data.
   2482      *
   2483      * Examples:<pre>
   2484      *
   2485      * "en_Latn_US" minimizes to "en"
   2486      *
   2487      * "de_Latn_US" minimizes to "de"
   2488      *
   2489      * "sr_Cyrl_RS" minimizes to "sr"
   2490      *
   2491      * "zh_Hant_TW" minimizes to "zh_TW" if fieldToFavor == {@link Minimize#FAVOR_REGION}
   2492      * "zh_Hant_TW" minimizes to "zh_Hant" if fieldToFavor == {@link Minimize#FAVOR_SCRIPT}
   2493      * </pre>
   2494      * The fieldToFavor only has an effect if either the region or the script could be suppressed, but not both.
   2495      * @param loc The ULocale to minimize
   2496      * @param fieldToFavor Indicate which should be preferred, when either the region <b>or</b> the script could be suppressed, but not both.
   2497      * @return The minimized ULocale instance.
   2498      * @internal
   2499      * @deprecated This API is ICU internal only.
   2500      */
   2501     @Deprecated
   2502     public static ULocale minimizeSubtags(ULocale loc, Minimize fieldToFavor) {
   2503         String[] tags = new String[3];
   2504 
   2505         int trailingIndex = parseTagString(
   2506                 loc.localeID,
   2507                 tags);
   2508 
   2509         String originalLang = tags[0];
   2510         String originalScript = tags[1];
   2511         String originalRegion = tags[2];
   2512         String originalTrailing = null;
   2513 
   2514         if (trailingIndex < loc.localeID.length()) {
   2515             /*
   2516              * Create a String that contains everything
   2517              * after the language, script, and region.
   2518              */
   2519             originalTrailing = loc.localeID.substring(trailingIndex);
   2520         }
   2521 
   2522         /**
   2523          * First, we need to first get the maximization
   2524          * by adding any likely subtags.
   2525          **/
   2526         String maximizedLocaleID =
   2527                 createLikelySubtagsString(
   2528                         originalLang,
   2529                         originalScript,
   2530                         originalRegion,
   2531                         null);
   2532 
   2533         /**
   2534          * If maximization fails, there's nothing
   2535          * we can do.
   2536          **/
   2537         if (isEmptyString(maximizedLocaleID)) {
   2538             return loc;
   2539         }
   2540         else {
   2541             /**
   2542              * Start first with just the language.
   2543              **/
   2544             String tag =
   2545                     createLikelySubtagsString(
   2546                             originalLang,
   2547                             null,
   2548                             null,
   2549                             null);
   2550 
   2551             if (tag.equals(maximizedLocaleID)) {
   2552                 String newLocaleID =
   2553                         createTagString(
   2554                                 originalLang,
   2555                                 null,
   2556                                 null,
   2557                                 originalTrailing);
   2558 
   2559                 return new ULocale(newLocaleID);
   2560             }
   2561         }
   2562 
   2563         /**
   2564          * Next, try the language and region.
   2565          **/
   2566         if (fieldToFavor == Minimize.FAVOR_REGION) {
   2567             if (originalRegion.length() != 0) {
   2568                 String tag =
   2569                         createLikelySubtagsString(
   2570                                 originalLang,
   2571                                 null,
   2572                                 originalRegion,
   2573                                 null);
   2574 
   2575                 if (tag.equals(maximizedLocaleID)) {
   2576                     String newLocaleID =
   2577                             createTagString(
   2578                                     originalLang,
   2579                                     null,
   2580                                     originalRegion,
   2581                                     originalTrailing);
   2582 
   2583                     return new ULocale(newLocaleID);
   2584                 }
   2585             }
   2586             if (originalScript.length() != 0){
   2587                 String tag =
   2588                         createLikelySubtagsString(
   2589                                 originalLang,
   2590                                 originalScript,
   2591                                 null,
   2592                                 null);
   2593 
   2594                 if (tag.equals(maximizedLocaleID)) {
   2595                     String newLocaleID =
   2596                             createTagString(
   2597                                     originalLang,
   2598                                     originalScript,
   2599                                     null,
   2600                                     originalTrailing);
   2601 
   2602                     return new ULocale(newLocaleID);
   2603                 }
   2604             }
   2605         } else { // FAVOR_SCRIPT, so
   2606             if (originalScript.length() != 0){
   2607                 String tag =
   2608                         createLikelySubtagsString(
   2609                                 originalLang,
   2610                                 originalScript,
   2611                                 null,
   2612                                 null);
   2613 
   2614                 if (tag.equals(maximizedLocaleID)) {
   2615                     String newLocaleID =
   2616                             createTagString(
   2617                                     originalLang,
   2618                                     originalScript,
   2619                                     null,
   2620                                     originalTrailing);
   2621 
   2622                     return new ULocale(newLocaleID);
   2623                 }
   2624             }
   2625             if (originalRegion.length() != 0) {
   2626                 String tag =
   2627                         createLikelySubtagsString(
   2628                                 originalLang,
   2629                                 null,
   2630                                 originalRegion,
   2631                                 null);
   2632 
   2633                 if (tag.equals(maximizedLocaleID)) {
   2634                     String newLocaleID =
   2635                             createTagString(
   2636                                     originalLang,
   2637                                     null,
   2638                                     originalRegion,
   2639                                     originalTrailing);
   2640 
   2641                     return new ULocale(newLocaleID);
   2642                 }
   2643             }
   2644         }
   2645         return loc;
   2646     }
   2647 
   2648     /**
   2649      * A trivial utility function that checks for a null
   2650      * reference or checks the length of the supplied String.
   2651      *
   2652      *   @param string The string to check
   2653      *
   2654      *   @return true if the String is empty, or if the reference is null.
   2655      */
   2656     private static boolean isEmptyString(String string) {
   2657         return string == null || string.length() == 0;
   2658     }
   2659 
   2660     /**
   2661      * Append a tag to a StringBuilder, adding the separator if necessary.The tag must
   2662      * not be a zero-length string.
   2663      *
   2664      * @param tag The tag to add.
   2665      * @param buffer The output buffer.
   2666      **/
   2667     private static void appendTag(String tag, StringBuilder buffer) {
   2668         if (buffer.length() != 0) {
   2669             buffer.append(UNDERSCORE);
   2670         }
   2671 
   2672         buffer.append(tag);
   2673     }
   2674 
   2675     /**
   2676      * Create a tag string from the supplied parameters.  The lang, script and region
   2677      * parameters may be null references.
   2678      *
   2679      * If any of the language, script or region parameters are empty, and the alternateTags
   2680      * parameter is not null, it will be parsed for potential language, script and region tags
   2681      * to be used when constructing the new tag.  If the alternateTags parameter is null, or
   2682      * it contains no language tag, the default tag for the unknown language is used.
   2683      *
   2684      * @param lang The language tag to use.
   2685      * @param script The script tag to use.
   2686      * @param region The region tag to use.
   2687      * @param trailing Any trailing data to append to the new tag.
   2688      * @param alternateTags A string containing any alternate tags.
   2689      * @return The new tag string.
   2690      **/
   2691     private static String createTagString(String lang, String script, String region,
   2692             String trailing, String alternateTags) {
   2693 
   2694         LocaleIDParser parser = null;
   2695         boolean regionAppended = false;
   2696 
   2697         StringBuilder tag = new StringBuilder();
   2698 
   2699         if (!isEmptyString(lang)) {
   2700             appendTag(
   2701                     lang,
   2702                     tag);
   2703         }
   2704         else if (isEmptyString(alternateTags)) {
   2705             /*
   2706              * Append the value for an unknown language, if
   2707              * we found no language.
   2708              */
   2709             appendTag(
   2710                     UNDEFINED_LANGUAGE,
   2711                     tag);
   2712         }
   2713         else {
   2714             parser = new LocaleIDParser(alternateTags);
   2715 
   2716             String alternateLang = parser.getLanguage();
   2717 
   2718             /*
   2719              * Append the value for an unknown language, if
   2720              * we found no language.
   2721              */
   2722             appendTag(
   2723                     !isEmptyString(alternateLang) ? alternateLang : UNDEFINED_LANGUAGE,
   2724                             tag);
   2725         }
   2726 
   2727         if (!isEmptyString(script)) {
   2728             appendTag(
   2729                     script,
   2730                     tag);
   2731         }
   2732         else if (!isEmptyString(alternateTags)) {
   2733             /*
   2734              * Parse the alternateTags string for the script.
   2735              */
   2736             if (parser == null) {
   2737                 parser = new LocaleIDParser(alternateTags);
   2738             }
   2739 
   2740             String alternateScript = parser.getScript();
   2741 
   2742             if (!isEmptyString(alternateScript)) {
   2743                 appendTag(
   2744                         alternateScript,
   2745                         tag);
   2746             }
   2747         }
   2748 
   2749         if (!isEmptyString(region)) {
   2750             appendTag(
   2751                     region,
   2752                     tag);
   2753 
   2754             regionAppended = true;
   2755         }
   2756         else if (!isEmptyString(alternateTags)) {
   2757             /*
   2758              * Parse the alternateTags string for the region.
   2759              */
   2760             if (parser == null) {
   2761                 parser = new LocaleIDParser(alternateTags);
   2762             }
   2763 
   2764             String alternateRegion = parser.getCountry();
   2765 
   2766             if (!isEmptyString(alternateRegion)) {
   2767                 appendTag(
   2768                         alternateRegion,
   2769                         tag);
   2770 
   2771                 regionAppended = true;
   2772             }
   2773         }
   2774 
   2775         if (trailing != null && trailing.length() > 1) {
   2776             /*
   2777              * The current ICU format expects two underscores
   2778              * will separate the variant from the preceeding
   2779              * parts of the tag, if there is no region.
   2780              */
   2781             int separators = 0;
   2782 
   2783             if (trailing.charAt(0) == UNDERSCORE) {
   2784                 if (trailing.charAt(1) == UNDERSCORE) {
   2785                     separators = 2;
   2786                 }
   2787             }
   2788             else {
   2789                 separators = 1;
   2790             }
   2791 
   2792             if (regionAppended) {
   2793                 /*
   2794                  * If we appended a region, we may need to strip
   2795                  * the extra separator from the variant portion.
   2796                  */
   2797                 if (separators == 2) {
   2798                     tag.append(trailing.substring(1));
   2799                 }
   2800                 else {
   2801                     tag.append(trailing);
   2802                 }
   2803             }
   2804             else {
   2805                 /*
   2806                  * If we did not append a region, we may need to add
   2807                  * an extra separator to the variant portion.
   2808                  */
   2809                 if (separators == 1) {
   2810                     tag.append(UNDERSCORE);
   2811                 }
   2812                 tag.append(trailing);
   2813             }
   2814         }
   2815 
   2816         return tag.toString();
   2817     }
   2818 
   2819     /**
   2820      * Create a tag string from the supplied parameters.  The lang, script and region
   2821      * parameters may be null references.If the lang parameter is an empty string, the
   2822      * default value for an unknown language is written to the output buffer.
   2823      *
   2824      * @param lang The language tag to use.
   2825      * @param script The script tag to use.
   2826      * @param region The region tag to use.
   2827      * @param trailing Any trailing data to append to the new tag.
   2828      * @return The new String.
   2829      **/
   2830     static String createTagString(String lang, String script, String region, String trailing) {
   2831         return createTagString(lang, script, region, trailing, null);
   2832     }
   2833 
   2834     /**
   2835      * Parse the language, script, and region subtags from a tag string, and return the results.
   2836      *
   2837      * This function does not return the canonical strings for the unknown script and region.
   2838      *
   2839      * @param localeID The locale ID to parse.
   2840      * @param tags An array of three String references to return the subtag strings.
   2841      * @return The number of chars of the localeID parameter consumed.
   2842      **/
   2843     private static int parseTagString(String localeID, String tags[]) {
   2844         LocaleIDParser parser = new LocaleIDParser(localeID);
   2845 
   2846         String lang = parser.getLanguage();
   2847         String script = parser.getScript();
   2848         String region = parser.getCountry();
   2849 
   2850         if (isEmptyString(lang)) {
   2851             tags[0] = UNDEFINED_LANGUAGE;
   2852         }
   2853         else {
   2854             tags[0] = lang;
   2855         }
   2856 
   2857         if (script.equals(UNDEFINED_SCRIPT)) {
   2858             tags[1] = "";
   2859         }
   2860         else {
   2861             tags[1] = script;
   2862         }
   2863 
   2864         if (region.equals(UNDEFINED_REGION)) {
   2865             tags[2] = "";
   2866         }
   2867         else {
   2868             tags[2] = region;
   2869         }
   2870 
   2871         /*
   2872          * Search for the variant.  If there is one, then return the index of
   2873          * the preceeding separator.
   2874          * If there's no variant, search for the keyword delimiter,
   2875          * and return its index.  Otherwise, return the length of the
   2876          * string.
   2877          *
   2878          * $TOTO(dbertoni) we need to take into account that we might
   2879          * find a part of the language as the variant, since it can
   2880          * can have a variant portion that is long enough to contain
   2881          * the same characters as the variant.
   2882          */
   2883         String variant = parser.getVariant();
   2884 
   2885         if (!isEmptyString(variant)){
   2886             int index = localeID.indexOf(variant);
   2887 
   2888 
   2889             return  index > 0 ? index - 1 : index;
   2890         }
   2891         else
   2892         {
   2893             int index = localeID.indexOf('@');
   2894 
   2895             return index == -1 ? localeID.length() : index;
   2896         }
   2897     }
   2898 
   2899     private static String lookupLikelySubtags(String localeId) {
   2900         UResourceBundle bundle =
   2901                 UResourceBundle.getBundleInstance(
   2902                         ICUData.ICU_BASE_NAME, "likelySubtags");
   2903         try {
   2904             return bundle.getString(localeId);
   2905         }
   2906         catch(MissingResourceException e) {
   2907             return null;
   2908         }
   2909     }
   2910 
   2911     private static String createLikelySubtagsString(String lang, String script, String region,
   2912             String variants) {
   2913 
   2914         /**
   2915          * Try the language with the script and region first.
   2916          */
   2917         if (!isEmptyString(script) && !isEmptyString(region)) {
   2918 
   2919             String searchTag =
   2920                     createTagString(
   2921                             lang,
   2922                             script,
   2923                             region,
   2924                             null);
   2925 
   2926             String likelySubtags = lookupLikelySubtags(searchTag);
   2927 
   2928             /*
   2929             if (likelySubtags == null) {
   2930                 if (likelySubtags2 != null) {
   2931                     System.err.println("Tag mismatch: \"(null)\" \"" + likelySubtags2 + "\"");
   2932                 }
   2933             }
   2934             else if (likelySubtags2 == null) {
   2935                 System.err.println("Tag mismatch: \"" + likelySubtags + "\" \"(null)\"");
   2936             }
   2937             else if (!likelySubtags.equals(likelySubtags2)) {
   2938                 System.err.println("Tag mismatch: \"" + likelySubtags + "\" \"" + likelySubtags2
   2939                     + "\"");
   2940             }
   2941              */
   2942             if (likelySubtags != null) {
   2943                 // Always use the language tag from the
   2944                 // maximal string, since it may be more
   2945                 // specific than the one provided.
   2946                 return createTagString(
   2947                         null,
   2948                         null,
   2949                         null,
   2950                         variants,
   2951                         likelySubtags);
   2952             }
   2953         }
   2954 
   2955         /**
   2956          * Try the language with just the script.
   2957          **/
   2958         if (!isEmptyString(script)) {
   2959 
   2960             String searchTag =
   2961                     createTagString(
   2962                             lang,
   2963                             script,
   2964                             null,
   2965                             null);
   2966 
   2967             String likelySubtags = lookupLikelySubtags(searchTag);
   2968             if (likelySubtags != null) {
   2969                 // Always use the language tag from the
   2970                 // maximal string, since it may be more
   2971                 // specific than the one provided.
   2972                 return createTagString(
   2973                         null,
   2974                         null,
   2975                         region,
   2976                         variants,
   2977                         likelySubtags);
   2978             }
   2979         }
   2980 
   2981         /**
   2982          * Try the language with just the region.
   2983          **/
   2984         if (!isEmptyString(region)) {
   2985 
   2986             String searchTag =
   2987                     createTagString(
   2988                             lang,
   2989                             null,
   2990                             region,
   2991                             null);
   2992 
   2993             String likelySubtags = lookupLikelySubtags(searchTag);
   2994 
   2995             if (likelySubtags != null) {
   2996                 // Always use the language tag from the
   2997                 // maximal string, since it may be more
   2998                 // specific than the one provided.
   2999                 return createTagString(
   3000                         null,
   3001                         script,
   3002                         null,
   3003                         variants,
   3004                         likelySubtags);
   3005             }
   3006         }
   3007 
   3008         /**
   3009          * Finally, try just the language.
   3010          **/
   3011         {
   3012             String searchTag =
   3013                     createTagString(
   3014                             lang,
   3015                             null,
   3016                             null,
   3017                             null);
   3018 
   3019             String likelySubtags = lookupLikelySubtags(searchTag);
   3020 
   3021             if (likelySubtags != null) {
   3022                 // Always use the language tag from the
   3023                 // maximal string, since it may be more
   3024                 // specific than the one provided.
   3025                 return createTagString(
   3026                         null,
   3027                         script,
   3028                         region,
   3029                         variants,
   3030                         likelySubtags);
   3031             }
   3032         }
   3033 
   3034         return null;
   3035     }
   3036 
   3037     // --------------------------------
   3038     //      BCP47/OpenJDK APIs
   3039     // --------------------------------
   3040 
   3041     /**
   3042      * The key for the private use locale extension ('x').
   3043      *
   3044      * @see #getExtension(char)
   3045      * @see Builder#setExtension(char, String)
   3046      *
   3047      * @stable ICU 4.2
   3048      */
   3049     public static final char PRIVATE_USE_EXTENSION = 'x';
   3050 
   3051     /**
   3052      * The key for Unicode locale extension ('u').
   3053      *
   3054      * @see #getExtension(char)
   3055      * @see Builder#setExtension(char, String)
   3056      *
   3057      * @stable ICU 4.2
   3058      */
   3059     public static final char UNICODE_LOCALE_EXTENSION = 'u';
   3060 
   3061     /**
   3062      * Returns the extension (or private use) value associated with
   3063      * the specified key, or null if there is no extension
   3064      * associated with the key. To be well-formed, the key must be one
   3065      * of <code>[0-9A-Za-z]</code>. Keys are case-insensitive, so
   3066      * for example 'z' and 'Z' represent the same extension.
   3067      *
   3068      * @param key the extension key
   3069      * @return The extension, or null if this locale defines no
   3070      * extension for the specified key.
   3071      * @throws IllegalArgumentException if key is not well-formed
   3072      * @see #PRIVATE_USE_EXTENSION
   3073      * @see #UNICODE_LOCALE_EXTENSION
   3074      *
   3075      * @stable ICU 4.2
   3076      */
   3077     public String getExtension(char key) {
   3078         if (!LocaleExtensions.isValidKey(key)) {
   3079             throw new IllegalArgumentException("Invalid extension key: " + key);
   3080         }
   3081         return extensions().getExtensionValue(key);
   3082     }
   3083 
   3084     /**
   3085      * Returns the set of extension keys associated with this locale, or the
   3086      * empty set if it has no extensions. The returned set is unmodifiable.
   3087      * The keys will all be lower-case.
   3088      *
   3089      * @return the set of extension keys, or the empty set if this locale has
   3090      * no extensions
   3091      * @stable ICU 4.2
   3092      */
   3093     public Set<Character> getExtensionKeys() {
   3094         return extensions().getKeys();
   3095     }
   3096 
   3097     /**
   3098      * Returns the set of unicode locale attributes associated with
   3099      * this locale, or the empty set if it has no attributes. The
   3100      * returned set is unmodifiable.
   3101      *
   3102      * @return The set of attributes.
   3103      * @stable ICU 4.6
   3104      */
   3105     public Set<String> getUnicodeLocaleAttributes() {
   3106         return extensions().getUnicodeLocaleAttributes();
   3107     }
   3108 
   3109     /**
   3110      * Returns the Unicode locale type associated with the specified Unicode locale key
   3111      * for this locale. Returns the empty string for keys that are defined with no type.
   3112      * Returns null if the key is not defined. Keys are case-insensitive. The key must
   3113      * be two alphanumeric characters ([0-9a-zA-Z]), or an IllegalArgumentException is
   3114      * thrown.
   3115      *
   3116      * @param key the Unicode locale key
   3117      * @return The Unicode locale type associated with the key, or null if the
   3118      * locale does not define the key.
   3119      * @throws IllegalArgumentException if the key is not well-formed
   3120      * @throws NullPointerException if <code>key</code> is null
   3121      *
   3122      * @stable ICU 4.4
   3123      */
   3124     public String getUnicodeLocaleType(String key) {
   3125         if (!LocaleExtensions.isValidUnicodeLocaleKey(key)) {
   3126             throw new IllegalArgumentException("Invalid Unicode locale key: " + key);
   3127         }
   3128         return extensions().getUnicodeLocaleType(key);
   3129     }
   3130 
   3131     /**
   3132      * Returns the set of Unicode locale keys defined by this locale, or the empty set if
   3133      * this locale has none.  The returned set is immutable.  Keys are all lower case.
   3134      *
   3135      * @return The set of Unicode locale keys, or the empty set if this locale has
   3136      * no Unicode locale keywords.
   3137      *
   3138      * @stable ICU 4.4
   3139      */
   3140     public Set<String> getUnicodeLocaleKeys() {
   3141         return extensions().getUnicodeLocaleKeys();
   3142     }
   3143 
   3144     /**
   3145      * Returns a well-formed IETF BCP 47 language tag representing
   3146      * this locale.
   3147      *
   3148      * <p>If this <code>ULocale</code> has a language, script, country, or
   3149      * variant that does not satisfy the IETF BCP 47 language tag
   3150      * syntax requirements, this method handles these fields as
   3151      * described below:
   3152      *
   3153      * <p><b>Language:</b> If language is empty, or not well-formed
   3154      * (for example "a" or "e2"), it will be emitted as "und" (Undetermined).
   3155      *
   3156      * <p><b>Script:</b> If script is not well-formed (for example "12"
   3157      * or "Latin"), it will be omitted.
   3158      *
   3159      * <p><b>Country:</b> If country is not well-formed (for example "12"
   3160      * or "USA"), it will be omitted.
   3161      *
   3162      * <p><b>Variant:</b> If variant <b>is</b> well-formed, each sub-segment
   3163      * (delimited by '-' or '_') is emitted as a subtag.  Otherwise:
   3164      * <ul>
   3165      *
   3166      * <li>if all sub-segments match <code>[0-9a-zA-Z]{1,8}</code>
   3167      * (for example "WIN" or "Oracle_JDK_Standard_Edition"), the first
   3168      * ill-formed sub-segment and all following will be appended to
   3169      * the private use subtag.  The first appended subtag will be
   3170      * "lvariant", followed by the sub-segments in order, separated by
   3171      * hyphen. For example, "x-lvariant-WIN",
   3172      * "Oracle-x-lvariant-JDK-Standard-Edition".
   3173      *
   3174      * <li>if any sub-segment does not match
   3175      * <code>[0-9a-zA-Z]{1,8}</code>, the variant will be truncated
   3176      * and the problematic sub-segment and all following sub-segments
   3177      * will be omitted.  If the remainder is non-empty, it will be
   3178      * emitted as a private use subtag as above (even if the remainder
   3179      * turns out to be well-formed).  For example,
   3180      * "Solaris_isjustthecoolestthing" is emitted as
   3181      * "x-lvariant-Solaris", not as "solaris".</li></ul>
   3182      *
   3183      * <p><b>Note:</b> Although the language tag created by this
   3184      * method is well-formed (satisfies the syntax requirements
   3185      * defined by the IETF BCP 47 specification), it is not
   3186      * necessarily a valid BCP 47 language tag.  For example,
   3187      * <pre>
   3188      *   new Locale("xx", "YY").toLanguageTag();</pre>
   3189      *
   3190      * will return "xx-YY", but the language subtag "xx" and the
   3191      * region subtag "YY" are invalid because they are not registered
   3192      * in the IANA Language Subtag Registry.
   3193      *
   3194      * @return a BCP47 language tag representing the locale
   3195      * @see #forLanguageTag(String)
   3196      *
   3197      * @stable ICU 4.2
   3198      */
   3199     public String toLanguageTag() {
   3200         BaseLocale base = base();
   3201         LocaleExtensions exts = extensions();
   3202 
   3203         if (base.getVariant().equalsIgnoreCase("POSIX")) {
   3204             // special handling for variant POSIX
   3205             base = BaseLocale.getInstance(base.getLanguage(), base.getScript(), base.getRegion(), "");
   3206             if (exts.getUnicodeLocaleType("va") == null) {
   3207                 // add va-posix
   3208                 InternalLocaleBuilder ilocbld = new InternalLocaleBuilder();
   3209                 try {
   3210                     ilocbld.setLocale(BaseLocale.ROOT, exts);
   3211                     ilocbld.setUnicodeLocaleKeyword("va", "posix");
   3212                     exts = ilocbld.getLocaleExtensions();
   3213                 } catch (LocaleSyntaxException e) {
   3214                     // this should not happen
   3215                     throw new RuntimeException(e);
   3216                 }
   3217             }
   3218         }
   3219 
   3220         LanguageTag tag = LanguageTag.parseLocale(base, exts);
   3221 
   3222         StringBuilder buf = new StringBuilder();
   3223         String subtag = tag.getLanguage();
   3224         if (subtag.length() > 0) {
   3225             buf.append(LanguageTag.canonicalizeLanguage(subtag));
   3226         }
   3227 
   3228         subtag = tag.getScript();
   3229         if (subtag.length() > 0) {
   3230             buf.append(LanguageTag.SEP);
   3231             buf.append(LanguageTag.canonicalizeScript(subtag));
   3232         }
   3233 
   3234         subtag = tag.getRegion();
   3235         if (subtag.length() > 0) {
   3236             buf.append(LanguageTag.SEP);
   3237             buf.append(LanguageTag.canonicalizeRegion(subtag));
   3238         }
   3239 
   3240         List<String>subtags = tag.getVariants();
   3241         for (String s : subtags) {
   3242             buf.append(LanguageTag.SEP);
   3243             buf.append(LanguageTag.canonicalizeVariant(s));
   3244         }
   3245 
   3246         subtags = tag.getExtensions();
   3247         for (String s : subtags) {
   3248             buf.append(LanguageTag.SEP);
   3249             buf.append(LanguageTag.canonicalizeExtension(s));
   3250         }
   3251 
   3252         subtag = tag.getPrivateuse();
   3253         if (subtag.length() > 0) {
   3254             if (buf.length() > 0) {
   3255                 buf.append(LanguageTag.SEP);
   3256             }
   3257             buf.append(LanguageTag.PRIVATEUSE).append(LanguageTag.SEP);
   3258             buf.append(LanguageTag.canonicalizePrivateuse(subtag));
   3259         }
   3260 
   3261         return buf.toString();
   3262     }
   3263 
   3264     /**
   3265      * Returns a locale for the specified IETF BCP 47 language tag string.
   3266      *
   3267      * <p>If the specified language tag contains any ill-formed subtags,
   3268      * the first such subtag and all following subtags are ignored.  Compare
   3269      * to {@link ULocale.Builder#setLanguageTag} which throws an exception
   3270      * in this case.
   3271      *
   3272      * <p>The following <b>conversions</b> are performed:
   3273      * <ul>
   3274      *
   3275      * <li>The language code "und" is mapped to language "".
   3276      *
   3277      * <li>The portion of a private use subtag prefixed by "lvariant",
   3278      * if any, is removed and appended to the variant field in the
   3279      * result locale (without case normalization).  If it is then
   3280      * empty, the private use subtag is discarded:
   3281      *
   3282      * <pre>
   3283      *     ULocale loc;
   3284      *     loc = ULocale.forLanguageTag("en-US-x-lvariant-icu4j);
   3285      *     loc.getVariant(); // returns "ICU4J"
   3286      *     loc.getExtension('x'); // returns null
   3287      *
   3288      *     loc = Locale.forLanguageTag("de-icu4j-x-URP-lvariant-Abc-Def");
   3289      *     loc.getVariant(); // returns "ICU4J_ABC_DEF"
   3290      *     loc.getExtension('x'); // returns "urp"
   3291      * </pre>
   3292      *
   3293      * <li>When the languageTag argument contains an extlang subtag,
   3294      * the first such subtag is used as the language, and the primary
   3295      * language subtag and other extlang subtags are ignored:
   3296      *
   3297      * <pre>
   3298      *     ULocale.forLanguageTag("ar-aao").getLanguage(); // returns "aao"
   3299      *     ULocale.forLanguageTag("en-abc-def-us").toString(); // returns "abc_US"
   3300      * </pre>
   3301      *
   3302      * <li>Case is normalized. Language is normalized to lower case,
   3303      * script to title case, country to upper case, variant to upper case,
   3304      * and extensions to lower case.
   3305      *
   3306      * </ul>
   3307      *
   3308      * <p>This implements the 'Language-Tag' production of BCP47, and
   3309      * so supports grandfathered (regular and irregular) as well as
   3310      * private use language tags.  Stand alone private use tags are
   3311      * represented as empty language and extension 'x-whatever',
   3312      * and grandfathered tags are converted to their canonical replacements
   3313      * where they exist.
   3314      *
   3315      * <p>Grandfathered tags with canonical replacements are as follows:
   3316      *
   3317      * <table>
   3318      * <tbody align="center">
   3319      * <tr><th>grandfathered tag</th><th>&nbsp;</th><th>modern replacement</th></tr>
   3320      * <tr><td>art-lojban</td><td>&nbsp;</td><td>jbo</td></tr>
   3321      * <tr><td>i-ami</td><td>&nbsp;</td><td>ami</td></tr>
   3322      * <tr><td>i-bnn</td><td>&nbsp;</td><td>bnn</td></tr>
   3323      * <tr><td>i-hak</td><td>&nbsp;</td><td>hak</td></tr>
   3324      * <tr><td>i-klingon</td><td>&nbsp;</td><td>tlh</td></tr>
   3325      * <tr><td>i-lux</td><td>&nbsp;</td><td>lb</td></tr>
   3326      * <tr><td>i-navajo</td><td>&nbsp;</td><td>nv</td></tr>
   3327      * <tr><td>i-pwn</td><td>&nbsp;</td><td>pwn</td></tr>
   3328      * <tr><td>i-tao</td><td>&nbsp;</td><td>tao</td></tr>
   3329      * <tr><td>i-tay</td><td>&nbsp;</td><td>tay</td></tr>
   3330      * <tr><td>i-tsu</td><td>&nbsp;</td><td>tsu</td></tr>
   3331      * <tr><td>no-bok</td><td>&nbsp;</td><td>nb</td></tr>
   3332      * <tr><td>no-nyn</td><td>&nbsp;</td><td>nn</td></tr>
   3333      * <tr><td>sgn-BE-FR</td><td>&nbsp;</td><td>sfb</td></tr>
   3334      * <tr><td>sgn-BE-NL</td><td>&nbsp;</td><td>vgt</td></tr>
   3335      * <tr><td>sgn-CH-DE</td><td>&nbsp;</td><td>sgg</td></tr>
   3336      * <tr><td>zh-guoyu</td><td>&nbsp;</td><td>cmn</td></tr>
   3337      * <tr><td>zh-hakka</td><td>&nbsp;</td><td>hak</td></tr>
   3338      * <tr><td>zh-min-nan</td><td>&nbsp;</td><td>nan</td></tr>
   3339      * <tr><td>zh-xiang</td><td>&nbsp;</td><td>hsn</td></tr>
   3340      * </tbody>
   3341      * </table>
   3342      *
   3343      * <p>Grandfathered tags with no modern replacement will be
   3344      * converted as follows:
   3345      *
   3346      * <table>
   3347      * <tbody align="center">
   3348      * <tr><th>grandfathered tag</th><th>&nbsp;</th><th>converts to</th></tr>
   3349      * <tr><td>cel-gaulish</td><td>&nbsp;</td><td>xtg-x-cel-gaulish</td></tr>
   3350      * <tr><td>en-GB-oed</td><td>&nbsp;</td><td>en-GB-x-oed</td></tr>
   3351      * <tr><td>i-default</td><td>&nbsp;</td><td>en-x-i-default</td></tr>
   3352      * <tr><td>i-enochian</td><td>&nbsp;</td><td>und-x-i-enochian</td></tr>
   3353      * <tr><td>i-mingo</td><td>&nbsp;</td><td>see-x-i-mingo</td></tr>
   3354      * <tr><td>zh-min</td><td>&nbsp;</td><td>nan-x-zh-min</td></tr>
   3355      * </tbody>
   3356      * </table>
   3357      *
   3358      * <p>For a list of all grandfathered tags, see the
   3359      * IANA Language Subtag Registry (search for "Type: grandfathered").
   3360      *
   3361      * <p><b>Note</b>: there is no guarantee that <code>toLanguageTag</code>
   3362      * and <code>forLanguageTag</code> will round-trip.
   3363      *
   3364      * @param languageTag the language tag
   3365      * @return The locale that best represents the language tag.
   3366      * @throws NullPointerException if <code>languageTag</code> is <code>null</code>
   3367      * @see #toLanguageTag()
   3368      * @see ULocale.Builder#setLanguageTag(String)
   3369      *
   3370      * @stable ICU 4.2
   3371      */
   3372     public static ULocale forLanguageTag(String languageTag) {
   3373         LanguageTag tag = LanguageTag.parse(languageTag, null);
   3374         InternalLocaleBuilder bldr = new InternalLocaleBuilder();
   3375         bldr.setLanguageTag(tag);
   3376         return getInstance(bldr.getBaseLocale(), bldr.getLocaleExtensions());
   3377     }
   3378 
   3379     /**
   3380      * {@icu} Converts the specified keyword (legacy key, or BCP 47 Unicode locale
   3381      * extension key) to the equivalent BCP 47 Unicode locale extension key.
   3382      * For example, BCP 47 Unicode locale extension key "co" is returned for
   3383      * the input keyword "collation".
   3384      * <p>
   3385      * When the specified keyword is unknown, but satisfies the BCP syntax,
   3386      * then the lower-case version of the input keyword will be returned.
   3387      * For example,
   3388      * <code>toUnicodeLocaleKey("ZZ")</code> returns "zz".
   3389      *
   3390      * @param keyword       the input locale keyword (either legacy key
   3391      *                      such as "collation" or BCP 47 Unicode locale extension
   3392      *                      key such as "co").
   3393      * @return              the well-formed BCP 47 Unicode locale extension key,
   3394      *                      or null if the specified locale keyword cannot be mapped
   3395      *                      to a well-formed BCP 47 Unicode locale extension key.
   3396      * @see #toLegacyKey(String)
   3397      * @stable ICU 54
   3398      */
   3399     public static String toUnicodeLocaleKey(String keyword) {
   3400         String bcpKey = KeyTypeData.toBcpKey(keyword);
   3401         if (bcpKey == null && UnicodeLocaleExtension.isKey(keyword)) {
   3402             // unknown keyword, but syntax is fine..
   3403             bcpKey = AsciiUtil.toLowerString(keyword);
   3404         }
   3405         return bcpKey;
   3406     }
   3407 
   3408     /**
   3409      * {@icu} Converts the specified keyword value (legacy type, or BCP 47
   3410      * Unicode locale extension type) to the well-formed BCP 47 Unicode locale
   3411      * extension type for the specified keyword (category). For example, BCP 47
   3412      * Unicode locale extension type "phonebk" is returned for the input
   3413      * keyword value "phonebook", with the keyword "collation" (or "co").
   3414      * <p>
   3415      * When the specified keyword is not recognized, but the specified value
   3416      * satisfies the syntax of the BCP 47 Unicode locale extension type,
   3417      * or when the specified keyword allows 'variable' type and the specified
   3418      * value satisfies the syntax, the lower-case version of the input value
   3419      * will be returned. For example,
   3420      * <code>toUnicodeLocaleType("Foo", "Bar")</code> returns "bar",
   3421      * <code>toUnicodeLocaleType("variableTop", "00A4")</code> returns "00a4".
   3422      *
   3423      * @param keyword       the locale keyword (either legacy key such as
   3424      *                      "collation" or BCP 47 Unicode locale extension
   3425      *                      key such as "co").
   3426      * @param value         the locale keyword value (either legacy type
   3427      *                      such as "phonebook" or BCP 47 Unicode locale extension
   3428      *                      type such as "phonebk").
   3429      * @return              the well-formed BCP47 Unicode locale extension type,
   3430      *                      or null if the locale keyword value cannot be mapped to
   3431      *                      a well-formed BCP 47 Unicode locale extension type.
   3432      * @see #toLegacyType(String, String)
   3433      * @stable ICU 54
   3434      */
   3435     public static String toUnicodeLocaleType(String keyword, String value) {
   3436         String bcpType = KeyTypeData.toBcpType(keyword, value, null, null);
   3437         if (bcpType == null && UnicodeLocaleExtension.isType(value)) {
   3438             // unknown keyword, but syntax is fine..
   3439             bcpType = AsciiUtil.toLowerString(value);
   3440         }
   3441         return bcpType;
   3442     }
   3443 
   3444     /**
   3445      * {@icu} Converts the specified keyword (BCP 47 Unicode locale extension key, or
   3446      * legacy key) to the legacy key. For example, legacy key "collation" is
   3447      * returned for the input BCP 47 Unicode locale extension key "co".
   3448      *
   3449      * @param keyword       the input locale keyword (either BCP 47 Unicode locale
   3450      *                      extension key or legacy key).
   3451      * @return              the well-formed legacy key, or null if the specified
   3452      *                      keyword cannot be mapped to a well-formed legacy key.
   3453      * @see #toUnicodeLocaleKey(String)
   3454      * @stable ICU 54
   3455      */
   3456     public static String toLegacyKey(String keyword) {
   3457         String legacyKey = KeyTypeData.toLegacyKey(keyword);
   3458         if (legacyKey == null) {
   3459             // Checks if the specified locale key is well-formed with the legacy locale syntax.
   3460             //
   3461             // Note:
   3462             //  Neither ICU nor LDML/CLDR provides the definition of keyword syntax.
   3463             //  However, a key should not contain '=' obviously. For now, all existing
   3464             //  keys are using ASCII alphabetic letters only. We won't add any new key
   3465             //  that is not compatible with the BCP 47 syntax. Therefore, we assume
   3466             //  a valid key consist from [0-9a-zA-Z], no symbols.
   3467             if (keyword.matches("[0-9a-zA-Z]+")) {
   3468                 legacyKey = AsciiUtil.toLowerString(keyword);
   3469             }
   3470         }
   3471         return legacyKey;
   3472     }
   3473 
   3474     /**
   3475      * {@icu} Converts the specified keyword value (BCP 47 Unicode locale extension type,
   3476      * or legacy type or type alias) to the canonical legacy type. For example,
   3477      * the legacy type "phonebook" is returned for the input BCP 47 Unicode
   3478      * locale extension type "phonebk" with the keyword "collation" (or "co").
   3479      * <p>
   3480      * When the specified keyword is not recognized, but the specified value
   3481      * satisfies the syntax of legacy key, or when the specified keyword
   3482      * allows 'variable' type and the specified value satisfies the syntax,
   3483      * the lower-case version of the input value will be returned.
   3484      * For example,
   3485      * <code>toLegacyType("Foo", "Bar")</code> returns "bar",
   3486      * <code>toLegacyType("vt", "00A4")</code> returns "00a4".
   3487      *
   3488      * @param keyword       the locale keyword (either legacy keyword such as
   3489      *                      "collation" or BCP 47 Unicode locale extension
   3490      *                      key such as "co").
   3491      * @param value         the locale keyword value (either BCP 47 Unicode locale
   3492      *                      extension type such as "phonebk" or legacy keyword value
   3493      *                      such as "phonebook").
   3494      * @return              the well-formed legacy type, or null if the specified
   3495      *                      keyword value cannot be mapped to a well-formed legacy
   3496      *                      type.
   3497      * @see #toUnicodeLocaleType(String, String)
   3498      * @stable ICU 54
   3499      */
   3500     public static String toLegacyType(String keyword, String value) {
   3501         String legacyType = KeyTypeData.toLegacyType(keyword, value, null, null);
   3502         if (legacyType == null) {
   3503             // Checks if the specified locale type is well-formed with the legacy locale syntax.
   3504             //
   3505             // Note:
   3506             //  Neither ICU nor LDML/CLDR provides the definition of keyword syntax.
   3507             //  However, a type should not contain '=' obviously. For now, all existing
   3508             //  types are using ASCII alphabetic letters with a few symbol letters. We won't
   3509             //  add any new type that is not compatible with the BCP 47 syntax except timezone
   3510             //  IDs. For now, we assume a valid type start with [0-9a-zA-Z], but may contain
   3511             //  '-' '_' '/' in the middle.
   3512             if (value.matches("[0-9a-zA-Z]+([_/\\-][0-9a-zA-Z]+)*")) {
   3513                 legacyType = AsciiUtil.toLowerString(value);
   3514             }
   3515         }
   3516         return legacyType;
   3517     }
   3518 
   3519     /**
   3520      * <code>Builder</code> is used to build instances of <code>ULocale</code>
   3521      * from values configured by the setters.  Unlike the <code>ULocale</code>
   3522      * constructors, the <code>Builder</code> checks if a value configured by a
   3523      * setter satisfies the syntax requirements defined by the <code>ULocale</code>
   3524      * class.  A <code>ULocale</code> object created by a <code>Builder</code> is
   3525      * well-formed and can be transformed to a well-formed IETF BCP 47 language tag
   3526      * without losing information.
   3527      *
   3528      * <p><b>Note:</b> The <code>ULocale</code> class does not provide any
   3529      * syntactic restrictions on variant, while BCP 47 requires each variant
   3530      * subtag to be 5 to 8 alphanumerics or a single numeric followed by 3
   3531      * alphanumerics.  The method <code>setVariant</code> throws
   3532      * <code>IllformedLocaleException</code> for a variant that does not satisfy
   3533      * this restriction. If it is necessary to support such a variant, use a
   3534      * ULocale constructor.  However, keep in mind that a <code>ULocale</code>
   3535      * object created this way might lose the variant information when
   3536      * transformed to a BCP 47 language tag.
   3537      *
   3538      * <p>The following example shows how to create a <code>Locale</code> object
   3539      * with the <code>Builder</code>.
   3540      * <blockquote>
   3541      * <pre>
   3542      *     ULocale aLocale = new Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build();
   3543      * </pre>
   3544      * </blockquote>
   3545      *
   3546      * <p>Builders can be reused; <code>clear()</code> resets all
   3547      * fields to their default values.
   3548      *
   3549      * @see ULocale#toLanguageTag()
   3550      *
   3551      * @stable ICU 4.2
   3552      */
   3553     public static final class Builder {
   3554 
   3555         private final InternalLocaleBuilder _locbld;
   3556 
   3557         /**
   3558          * Constructs an empty Builder. The default value of all
   3559          * fields, extensions, and private use information is the
   3560          * empty string.
   3561          *
   3562          * @stable ICU 4.2
   3563          */
   3564         public Builder() {
   3565             _locbld = new InternalLocaleBuilder();
   3566         }
   3567 
   3568         /**
   3569          * Resets the <code>Builder</code> to match the provided
   3570          * <code>locale</code>.  Existing state is discarded.
   3571          *
   3572          * <p>All fields of the locale must be well-formed, see {@link Locale}.
   3573          *
   3574          * <p>Locales with any ill-formed fields cause
   3575          * <code>IllformedLocaleException</code> to be thrown.
   3576          *
   3577          * @param locale the locale
   3578          * @return This builder.
   3579          * @throws IllformedLocaleException if <code>locale</code> has
   3580          * any ill-formed fields.
   3581          * @throws NullPointerException if <code>locale</code> is null.
   3582          *
   3583          * @stable ICU 4.2
   3584          */
   3585         public Builder setLocale(ULocale locale) {
   3586             try {
   3587                 _locbld.setLocale(locale.base(), locale.extensions());
   3588             } catch (LocaleSyntaxException e) {
   3589                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   3590             }
   3591             return this;
   3592         }
   3593 
   3594         /**
   3595          * Resets the Builder to match the provided IETF BCP 47
   3596          * language tag.  Discards the existing state.  Null and the
   3597          * empty string cause the builder to be reset, like {@link
   3598          * #clear}.  Grandfathered tags (see {@link
   3599          * ULocale#forLanguageTag}) are converted to their canonical
   3600          * form before being processed.  Otherwise, the language tag
   3601          * must be well-formed (see {@link ULocale}) or an exception is
   3602          * thrown (unlike <code>ULocale.forLanguageTag</code>, which
   3603          * just discards ill-formed and following portions of the
   3604          * tag).
   3605          *
   3606          * @param languageTag the language tag
   3607          * @return This builder.
   3608          * @throws IllformedLocaleException if <code>languageTag</code> is ill-formed
   3609          * @see ULocale#forLanguageTag(String)
   3610          *
   3611          * @stable ICU 4.2
   3612          */
   3613         public Builder setLanguageTag(String languageTag) {
   3614             ParseStatus sts = new ParseStatus();
   3615             LanguageTag tag = LanguageTag.parse(languageTag, sts);
   3616             if (sts.isError()) {
   3617                 throw new IllformedLocaleException(sts.getErrorMessage(), sts.getErrorIndex());
   3618             }
   3619             _locbld.setLanguageTag(tag);
   3620 
   3621             return this;
   3622         }
   3623 
   3624         /**
   3625          * Sets the language.  If <code>language</code> is the empty string or
   3626          * null, the language in this <code>Builder</code> is removed.  Otherwise,
   3627          * the language must be <a href="./Locale.html#def_language">well-formed</a>
   3628          * or an exception is thrown.
   3629          *
   3630          * <p>The typical language value is a two or three-letter language
   3631          * code as defined in ISO639.
   3632          *
   3633          * @param language the language
   3634          * @return This builder.
   3635          * @throws IllformedLocaleException if <code>language</code> is ill-formed
   3636          *
   3637          * @stable ICU 4.2
   3638          */
   3639         public Builder setLanguage(String language) {
   3640             try {
   3641                 _locbld.setLanguage(language);
   3642             } catch (LocaleSyntaxException e) {
   3643                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   3644             }
   3645             return this;
   3646         }
   3647 
   3648         /**
   3649          * Sets the script. If <code>script</code> is null or the empty string,
   3650          * the script in this <code>Builder</code> is removed.
   3651          * Otherwise, the script must be well-formed or an exception is thrown.
   3652          *
   3653          * <p>The typical script value is a four-letter script code as defined by ISO 15924.
   3654          *
   3655          * @param script the script
   3656          * @return This builder.
   3657          * @throws IllformedLocaleException if <code>script</code> is ill-formed
   3658          *
   3659          * @stable ICU 4.2
   3660          */
   3661         public Builder setScript(String script) {
   3662             try {
   3663                 _locbld.setScript(script);
   3664             } catch (LocaleSyntaxException e) {
   3665                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   3666             }
   3667             return this;
   3668         }
   3669 
   3670         /**
   3671          * Sets the region.  If region is null or the empty string, the region
   3672          * in this <code>Builder</code> is removed.  Otherwise,
   3673          * the region must be well-formed or an exception is thrown.
   3674          *
   3675          * <p>The typical region value is a two-letter ISO 3166 code or a
   3676          * three-digit UN M.49 area code.
   3677          *
   3678          * <p>The country value in the <code>Locale</code> created by the
   3679          * <code>Builder</code> is always normalized to upper case.
   3680          *
   3681          * @param region the region
   3682          * @return This builder.
   3683          * @throws IllformedLocaleException if <code>region</code> is ill-formed
   3684          *
   3685          * @stable ICU 4.2
   3686          */
   3687         public Builder setRegion(String region) {
   3688             try {
   3689                 _locbld.setRegion(region);
   3690             } catch (LocaleSyntaxException e) {
   3691                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   3692             }
   3693             return this;
   3694         }
   3695 
   3696         /**
   3697          * Sets the variant.  If variant is null or the empty string, the
   3698          * variant in this <code>Builder</code> is removed.  Otherwise, it
   3699          * must consist of one or more well-formed subtags, or an exception is thrown.
   3700          *
   3701          * <p><b>Note:</b> This method checks if <code>variant</code>
   3702          * satisfies the IETF BCP 47 variant subtag's syntax requirements,
   3703          * and normalizes the value to lowercase letters.  However,
   3704          * the <code>ULocale</code> class does not impose any syntactic
   3705          * restriction on variant.  To set such a variant,
   3706          * use a ULocale constructor.
   3707          *
   3708          * @param variant the variant
   3709          * @return This builder.
   3710          * @throws IllformedLocaleException if <code>variant</code> is ill-formed
   3711          *
   3712          * @stable ICU 4.2
   3713          */
   3714         public Builder setVariant(String variant) {
   3715             try {
   3716                 _locbld.setVariant(variant);
   3717             } catch (LocaleSyntaxException e) {
   3718                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   3719             }
   3720             return this;
   3721         }
   3722 
   3723         /**
   3724          * Sets the extension for the given key. If the value is null or the
   3725          * empty string, the extension is removed.  Otherwise, the extension
   3726          * must be well-formed or an exception is thrown.
   3727          *
   3728          * <p><b>Note:</b> The key {@link ULocale#UNICODE_LOCALE_EXTENSION
   3729          * UNICODE_LOCALE_EXTENSION} ('u') is used for the Unicode locale extension.
   3730          * Setting a value for this key replaces any existing Unicode locale key/type
   3731          * pairs with those defined in the extension.
   3732          *
   3733          * <p><b>Note:</b> The key {@link ULocale#PRIVATE_USE_EXTENSION
   3734          * PRIVATE_USE_EXTENSION} ('x') is used for the private use code. To be
   3735          * well-formed, the value for this key needs only to have subtags of one to
   3736          * eight alphanumeric characters, not two to eight as in the general case.
   3737          *
   3738          * @param key the extension key
   3739          * @param value the extension value
   3740          * @return This builder.
   3741          * @throws IllformedLocaleException if <code>key</code> is illegal
   3742          * or <code>value</code> is ill-formed
   3743          * @see #setUnicodeLocaleKeyword(String, String)
   3744          *
   3745          * @stable ICU 4.2
   3746          */
   3747         public Builder setExtension(char key, String value) {
   3748             try {
   3749                 _locbld.setExtension(key, value);
   3750             } catch (LocaleSyntaxException e) {
   3751                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   3752             }
   3753             return this;
   3754         }
   3755 
   3756         /**
   3757          * Sets the Unicode locale keyword type for the given key.  If the type
   3758          * is null, the Unicode keyword is removed.  Otherwise, the key must be
   3759          * non-null and both key and type must be well-formed or an exception
   3760          * is thrown.
   3761          *
   3762          * <p>Keys and types are converted to lower case.
   3763          *
   3764          * <p><b>Note</b>:Setting the 'u' extension via {@link #setExtension}
   3765          * replaces all Unicode locale keywords with those defined in the
   3766          * extension.
   3767          *
   3768          * @param key the Unicode locale key
   3769          * @param type the Unicode locale type
   3770          * @return This builder.
   3771          * @throws IllformedLocaleException if <code>key</code> or <code>type</code>
   3772          * is ill-formed
   3773          * @throws NullPointerException if <code>key</code> is null
   3774          * @see #setExtension(char, String)
   3775          *
   3776          * @stable ICU 4.4
   3777          */
   3778         public Builder setUnicodeLocaleKeyword(String key, String type) {
   3779             try {
   3780                 _locbld.setUnicodeLocaleKeyword(key, type);
   3781             } catch (LocaleSyntaxException e) {
   3782                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   3783             }
   3784             return this;
   3785         }
   3786 
   3787         /**
   3788          * Adds a unicode locale attribute, if not already present, otherwise
   3789          * has no effect.  The attribute must not be null and must be well-formed
   3790          * or an exception is thrown.
   3791          *
   3792          * @param attribute the attribute
   3793          * @return This builder.
   3794          * @throws NullPointerException if <code>attribute</code> is null
   3795          * @throws IllformedLocaleException if <code>attribute</code> is ill-formed
   3796          * @see #setExtension(char, String)
   3797          *
   3798          * @stable ICU 4.6
   3799          */
   3800         public Builder addUnicodeLocaleAttribute(String attribute) {
   3801             try {
   3802                 _locbld.addUnicodeLocaleAttribute(attribute);
   3803             } catch (LocaleSyntaxException e) {
   3804                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   3805             }
   3806             return this;
   3807         }
   3808 
   3809         /**
   3810          * Removes a unicode locale attribute, if present, otherwise has no
   3811          * effect.  The attribute must not be null and must be well-formed
   3812          * or an exception is thrown.
   3813          *
   3814          * <p>Attribute comparision for removal is case-insensitive.
   3815          *
   3816          * @param attribute the attribute
   3817          * @return This builder.
   3818          * @throws NullPointerException if <code>attribute</code> is null
   3819          * @throws IllformedLocaleException if <code>attribute</code> is ill-formed
   3820          * @see #setExtension(char, String)
   3821          *
   3822          * @stable ICU 4.6
   3823          */
   3824         public Builder removeUnicodeLocaleAttribute(String attribute) {
   3825             try {
   3826                 _locbld.removeUnicodeLocaleAttribute(attribute);
   3827             } catch (LocaleSyntaxException e) {
   3828                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   3829             }
   3830             return this;
   3831         }
   3832 
   3833         /**
   3834          * Resets the builder to its initial, empty state.
   3835          *
   3836          * @return this builder
   3837          *
   3838          * @stable ICU 4.2
   3839          */
   3840         public Builder clear() {
   3841             _locbld.clear();
   3842             return this;
   3843         }
   3844 
   3845         /**
   3846          * Resets the extensions to their initial, empty state.
   3847          * Language, script, region and variant are unchanged.
   3848          *
   3849          * @return this builder
   3850          * @see #setExtension(char, String)
   3851          *
   3852          * @stable ICU 4.2
   3853          */
   3854         public Builder clearExtensions() {
   3855             _locbld.clearExtensions();
   3856             return this;
   3857         }
   3858 
   3859         /**
   3860          * Returns an instance of <code>ULocale</code> created from the fields set
   3861          * on this builder.
   3862          *
   3863          * @return a new Locale
   3864          *
   3865          * @stable ICU 4.4
   3866          */
   3867         public ULocale build() {
   3868             return getInstance(_locbld.getBaseLocale(), _locbld.getLocaleExtensions());
   3869         }
   3870     }
   3871 
   3872     private static ULocale getInstance(BaseLocale base, LocaleExtensions exts) {
   3873         String id = lscvToID(base.getLanguage(), base.getScript(), base.getRegion(),
   3874                 base.getVariant());
   3875 
   3876         Set<Character> extKeys = exts.getKeys();
   3877         if (!extKeys.isEmpty()) {
   3878             // legacy locale ID assume Unicode locale keywords and
   3879             // other extensions are at the same level.
   3880             // e.g. @a=ext-for-aa;calendar=japanese;m=ext-for-mm;x=priv-use
   3881 
   3882             TreeMap<String, String> kwds = new TreeMap<>();
   3883             for (Character key : extKeys) {
   3884                 Extension ext = exts.getExtension(key);
   3885                 if (ext instanceof UnicodeLocaleExtension) {
   3886                     UnicodeLocaleExtension uext = (UnicodeLocaleExtension)ext;
   3887                     Set<String> ukeys = uext.getUnicodeLocaleKeys();
   3888                     for (String bcpKey : ukeys) {
   3889                         String bcpType = uext.getUnicodeLocaleType(bcpKey);
   3890                         // convert to legacy key/type
   3891                         String lkey = toLegacyKey(bcpKey);
   3892                         String ltype = toLegacyType(bcpKey, ((bcpType.length() == 0) ? "yes" : bcpType)); // use "yes" as the value of typeless keywords
   3893                         // special handling for u-va-posix, since this is a variant, not a keyword
   3894                         if (lkey.equals("va") && ltype.equals("posix") && base.getVariant().length() == 0) {
   3895                             id = id + "_POSIX";
   3896                         } else {
   3897                             kwds.put(lkey, ltype);
   3898                         }
   3899                     }
   3900                     // Mapping Unicode locale attribute to the special keyword, attribute=xxx-yyy
   3901                     Set<String> uattributes = uext.getUnicodeLocaleAttributes();
   3902                     if (uattributes.size() > 0) {
   3903                         StringBuilder attrbuf = new StringBuilder();
   3904                         for (String attr : uattributes) {
   3905                             if (attrbuf.length() > 0) {
   3906                                 attrbuf.append('-');
   3907                             }
   3908                             attrbuf.append(attr);
   3909                         }
   3910                         kwds.put(LOCALE_ATTRIBUTE_KEY, attrbuf.toString());
   3911                     }
   3912                 } else {
   3913                     kwds.put(String.valueOf(key), ext.getValue());
   3914                 }
   3915             }
   3916 
   3917             if (!kwds.isEmpty()) {
   3918                 StringBuilder buf = new StringBuilder(id);
   3919                 buf.append("@");
   3920                 Set<Map.Entry<String, String>> kset = kwds.entrySet();
   3921                 boolean insertSep = false;
   3922                 for (Map.Entry<String, String> kwd : kset) {
   3923                     if (insertSep) {
   3924                         buf.append(";");
   3925                     } else {
   3926                         insertSep = true;
   3927                     }
   3928                     buf.append(kwd.getKey());
   3929                     buf.append("=");
   3930                     buf.append(kwd.getValue());
   3931                 }
   3932 
   3933                 id = buf.toString();
   3934             }
   3935         }
   3936         return new ULocale(id);
   3937     }
   3938 
   3939     private BaseLocale base() {
   3940         if (baseLocale == null) {
   3941             String language, script, region, variant;
   3942             language = script = region = variant = "";
   3943             if (!equals(ULocale.ROOT)) {
   3944                 LocaleIDParser lp = new LocaleIDParser(localeID);
   3945                 language = lp.getLanguage();
   3946                 script = lp.getScript();
   3947                 region = lp.getCountry();
   3948                 variant = lp.getVariant();
   3949             }
   3950             baseLocale = BaseLocale.getInstance(language, script, region, variant);
   3951         }
   3952         return baseLocale;
   3953     }
   3954 
   3955     private LocaleExtensions extensions() {
   3956         if (extensions == null) {
   3957             Iterator<String> kwitr = getKeywords();
   3958             if (kwitr == null) {
   3959                 extensions = LocaleExtensions.EMPTY_EXTENSIONS;
   3960             } else {
   3961                 InternalLocaleBuilder intbld = new InternalLocaleBuilder();
   3962                 while (kwitr.hasNext()) {
   3963                     String key = kwitr.next();
   3964                     if (key.equals(LOCALE_ATTRIBUTE_KEY)) {
   3965                         // special keyword used for representing Unicode locale attributes
   3966                         String[] uattributes = getKeywordValue(key).split("[-_]");
   3967                         for (String uattr : uattributes) {
   3968                             try {
   3969                                 intbld.addUnicodeLocaleAttribute(uattr);
   3970                             } catch (LocaleSyntaxException e) {
   3971                                 // ignore and fall through
   3972                             }
   3973                         }
   3974                     } else if (key.length() >= 2) {
   3975                         String bcpKey = toUnicodeLocaleKey(key);
   3976                         String bcpType = toUnicodeLocaleType(key, getKeywordValue(key));
   3977                         if (bcpKey != null && bcpType != null) {
   3978                             try {
   3979                                 intbld.setUnicodeLocaleKeyword(bcpKey, bcpType);
   3980                             } catch (LocaleSyntaxException e) {
   3981                                 // ignore and fall through
   3982                             }
   3983                         }
   3984                     } else if (key.length() == 1 && (key.charAt(0) != UNICODE_LOCALE_EXTENSION)) {
   3985                         try  {
   3986                             intbld.setExtension(key.charAt(0), getKeywordValue(key).replace("_",
   3987                                     LanguageTag.SEP));
   3988                         } catch (LocaleSyntaxException e) {
   3989                             // ignore and fall through
   3990                         }
   3991                     }
   3992                 }
   3993                 extensions = intbld.getLocaleExtensions();
   3994             }
   3995         }
   3996         return extensions;
   3997     }
   3998 
   3999     /*
   4000      * JDK Locale Helper
   4001      */
   4002     private static final class JDKLocaleHelper {
   4003         // Java 7 has java.util.Locale.Category.
   4004         // Android API level 21..23 do not yet have it; only API level 24 (Nougat) adds it.
   4005         // https://developer.android.com/reference/java/util/Locale.Category
   4006         private static boolean hasLocaleCategories = false;
   4007 
   4008         private static Method mGetDefault;
   4009         private static Method mSetDefault;
   4010         private static Object eDISPLAY;
   4011         private static Object eFORMAT;
   4012 
   4013         static {
   4014             do {
   4015                 try {
   4016                     Class<?> cCategory = null;
   4017                     Class<?>[] classes = Locale.class.getDeclaredClasses();
   4018                     for (Class<?> c : classes) {
   4019                         if (c.getName().equals("java.util.Locale$Category")) {
   4020                             cCategory = c;
   4021                             break;
   4022                         }
   4023                     }
   4024                     if (cCategory == null) {
   4025                         break;
   4026                     }
   4027                     mGetDefault = Locale.class.getDeclaredMethod("getDefault", cCategory);
   4028                     mSetDefault = Locale.class.getDeclaredMethod("setDefault", cCategory, Locale.class);
   4029 
   4030                     Method mName = cCategory.getMethod("name", (Class[]) null);
   4031                     Object[] enumConstants = cCategory.getEnumConstants();
   4032                     for (Object e : enumConstants) {
   4033                         String catVal = (String)mName.invoke(e, (Object[])null);
   4034                         if (catVal.equals("DISPLAY")) {
   4035                             eDISPLAY = e;
   4036                         } else if (catVal.equals("FORMAT")) {
   4037                             eFORMAT = e;
   4038                         }
   4039                     }
   4040                     if (eDISPLAY == null || eFORMAT == null) {
   4041                         break;
   4042                     }
   4043 
   4044                     hasLocaleCategories = true;
   4045                 } catch (NoSuchMethodException e) {
   4046                 } catch (IllegalArgumentException e) {
   4047                 } catch (IllegalAccessException e) {
   4048                 } catch (InvocationTargetException e) {
   4049                 } catch (SecurityException e) {
   4050                     // TODO : report?
   4051                 }
   4052             } while (false);
   4053         }
   4054 
   4055         private JDKLocaleHelper() {
   4056         }
   4057 
   4058         public static boolean hasLocaleCategories() {
   4059             return hasLocaleCategories;
   4060         }
   4061 
   4062         public static ULocale toULocale(Locale loc) {
   4063             String language = loc.getLanguage();
   4064             String script = "";
   4065             String country = loc.getCountry();
   4066             String variant = loc.getVariant();
   4067 
   4068             Set<String> attributes = null;
   4069             Map<String, String> keywords = null;
   4070 
   4071             script = loc.getScript();
   4072             Set<Character> extKeys = loc.getExtensionKeys();
   4073             if (!extKeys.isEmpty()) {
   4074                 for (Character extKey : extKeys) {
   4075                     if (extKey.charValue() == 'u') {
   4076                         // Found Unicode locale extension
   4077 
   4078                         // attributes
   4079                         @SuppressWarnings("unchecked")
   4080                         Set<String> uAttributes = loc.getUnicodeLocaleAttributes();
   4081                         if (!uAttributes.isEmpty()) {
   4082                             attributes = new TreeSet<>();
   4083                             for (String attr : uAttributes) {
   4084                                 attributes.add(attr);
   4085                             }
   4086                         }
   4087 
   4088                         // keywords
   4089                         Set<String> uKeys = loc.getUnicodeLocaleKeys();
   4090                         for (String kwKey : uKeys) {
   4091                             String kwVal = loc.getUnicodeLocaleType(kwKey);
   4092                             if (kwVal != null) {
   4093                                 if (kwKey.equals("va")) {
   4094                                     // va-* is interpreted as a variant
   4095                                     variant = (variant.length() == 0) ? kwVal : kwVal + "_" + variant;
   4096                                 } else {
   4097                                     if (keywords == null) {
   4098                                         keywords = new TreeMap<>();
   4099                                     }
   4100                                     keywords.put(kwKey, kwVal);
   4101                                 }
   4102                             }
   4103                         }
   4104                     } else {
   4105                         String extVal = loc.getExtension(extKey);
   4106                         if (extVal != null) {
   4107                             if (keywords == null) {
   4108                                 keywords = new TreeMap<>();
   4109                             }
   4110                             keywords.put(String.valueOf(extKey), extVal);
   4111                         }
   4112                     }
   4113                 }
   4114             }
   4115 
   4116             // JDK locale no_NO_NY is not interpreted as Nynorsk by ICU,
   4117             // and it should be transformed to nn_NO.
   4118 
   4119             // Note: JDK7+ unerstand both no_NO_NY and nn_NO. When convert
   4120             // ICU locale to JDK, we do not need to map nn_NO back to no_NO_NY.
   4121 
   4122             if (language.equals("no") && country.equals("NO") && variant.equals("NY")) {
   4123                 language = "nn";
   4124                 variant = "";
   4125             }
   4126 
   4127             // Constructing ID
   4128             StringBuilder buf = new StringBuilder(language);
   4129 
   4130             if (script.length() > 0) {
   4131                 buf.append('_');
   4132                 buf.append(script);
   4133             }
   4134 
   4135             if (country.length() > 0) {
   4136                 buf.append('_');
   4137                 buf.append(country);
   4138             }
   4139 
   4140             if (variant.length() > 0) {
   4141                 if (country.length() == 0) {
   4142                     buf.append('_');
   4143                 }
   4144                 buf.append('_');
   4145                 buf.append(variant);
   4146             }
   4147 
   4148             if (attributes != null) {
   4149                 // transform Unicode attributes into a keyword
   4150                 StringBuilder attrBuf = new StringBuilder();
   4151                 for (String attr : attributes) {
   4152                     if (attrBuf.length() != 0) {
   4153                         attrBuf.append('-');
   4154                     }
   4155                     attrBuf.append(attr);
   4156                 }
   4157                 if (keywords == null) {
   4158                     keywords = new TreeMap<>();
   4159                 }
   4160                 keywords.put(LOCALE_ATTRIBUTE_KEY, attrBuf.toString());
   4161             }
   4162 
   4163             if (keywords != null) {
   4164                 buf.append('@');
   4165                 boolean addSep = false;
   4166                 for (Entry<String, String> kwEntry : keywords.entrySet()) {
   4167                     String kwKey = kwEntry.getKey();
   4168                     String kwVal = kwEntry.getValue();
   4169 
   4170                     if (kwKey.length() != 1) {
   4171                         // Unicode locale key
   4172                         kwKey = toLegacyKey(kwKey);
   4173                         // use "yes" as the value of typeless keywords
   4174                         kwVal = toLegacyType(kwKey, ((kwVal.length() == 0) ? "yes" : kwVal));
   4175                     }
   4176 
   4177                     if (addSep) {
   4178                         buf.append(';');
   4179                     } else {
   4180                         addSep = true;
   4181                     }
   4182                     buf.append(kwKey);
   4183                     buf.append('=');
   4184                     buf.append(kwVal);
   4185                 }
   4186             }
   4187 
   4188             return new ULocale(getName(buf.toString()), loc);
   4189         }
   4190 
   4191         public static Locale toLocale(ULocale uloc) {
   4192             Locale loc = null;
   4193             String ulocStr = uloc.getName();
   4194             if (uloc.getScript().length() > 0 || ulocStr.contains("@")) {
   4195                 // With script or keywords available, the best way
   4196                 // to get a mapped Locale is to go through a language tag.
   4197                 // A Locale with script or keywords can only have variants
   4198                 // that is 1 to 8 alphanum. If this ULocale has a variant
   4199                 // subtag not satisfying the criteria, the variant subtag
   4200                 // will be lost.
   4201                 String tag = uloc.toLanguageTag();
   4202 
   4203                 // Workaround for variant casing problem:
   4204                 //
   4205                 // The variant field in ICU is case insensitive and normalized
   4206                 // to upper case letters by getVariant(), while
   4207                 // the variant field in JDK Locale is case sensitive.
   4208                 // ULocale#toLanguageTag use lower case characters for
   4209                 // BCP 47 variant and private use x-lvariant.
   4210                 //
   4211                 // Locale#forLanguageTag in JDK preserves character casing
   4212                 // for variant. Because ICU always normalizes variant to
   4213                 // upper case, we convert language tag to upper case here.
   4214                 tag = AsciiUtil.toUpperString(tag);
   4215                 loc = Locale.forLanguageTag(tag);
   4216             }
   4217             if (loc == null) {
   4218                 // Without script or keywords, use a Locale constructor,
   4219                 // so we can preserve any ill-formed variants.
   4220                 loc = new Locale(uloc.getLanguage(), uloc.getCountry(), uloc.getVariant());
   4221             }
   4222             return loc;
   4223         }
   4224 
   4225         public static Locale getDefault(Category category) {
   4226             if (hasLocaleCategories) {
   4227                 Object cat = null;
   4228                 switch (category) {
   4229                 case DISPLAY:
   4230                     cat = eDISPLAY;
   4231                     break;
   4232                 case FORMAT:
   4233                     cat = eFORMAT;
   4234                     break;
   4235                 }
   4236                 if (cat != null) {
   4237                     try {
   4238                         return (Locale)mGetDefault.invoke(null, cat);
   4239                     } catch (InvocationTargetException e) {
   4240                         // fall through - use the base default
   4241                     } catch (IllegalArgumentException e) {
   4242                         // fall through - use the base default
   4243                     } catch (IllegalAccessException e) {
   4244                         // fall through - use the base default
   4245                     }
   4246                 }
   4247             }
   4248             return Locale.getDefault();
   4249         }
   4250 
   4251         public static void setDefault(Category category, Locale newLocale) {
   4252             if (hasLocaleCategories) {
   4253                 Object cat = null;
   4254                 switch (category) {
   4255                 case DISPLAY:
   4256                     cat = eDISPLAY;
   4257                     break;
   4258                 case FORMAT:
   4259                     cat = eFORMAT;
   4260                     break;
   4261                 }
   4262                 if (cat != null) {
   4263                     try {
   4264                         mSetDefault.invoke(null, cat, newLocale);
   4265                     } catch (InvocationTargetException e) {
   4266                         // fall through - no effects
   4267                     } catch (IllegalArgumentException e) {
   4268                         // fall through - no effects
   4269                     } catch (IllegalAccessException e) {
   4270                         // fall through - no effects
   4271                     }
   4272                 }
   4273             }
   4274         }
   4275     }
   4276 }
   4277