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