Home | History | Annotate | Download | only in util
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html#License
      3 /*
      4 ******************************************************************************
      5 * Copyright (C) 2003-2012, International Business Machines Corporation and   *
      6 * others. All Rights Reserved.                                               *
      7 ******************************************************************************
      8 */
      9 
     10 package com.ibm.icu.util;
     11 
     12 import java.io.Serializable;
     13 import java.lang.reflect.InvocationTargetException;
     14 import java.lang.reflect.Method;
     15 import java.text.ParseException;
     16 import java.util.Iterator;
     17 import java.util.List;
     18 import java.util.Locale;
     19 import java.util.Map;
     20 import java.util.Map.Entry;
     21 import java.util.MissingResourceException;
     22 import java.util.Set;
     23 import java.util.TreeMap;
     24 import java.util.TreeSet;
     25 
     26 import com.ibm.icu.impl.ICUCache;
     27 import com.ibm.icu.impl.LocaleIDParser;
     28 import com.ibm.icu.impl.LocaleIDs;
     29 import com.ibm.icu.impl.LocaleUtility;
     30 import com.ibm.icu.impl.SimpleCache;
     31 import com.ibm.icu.impl.locale.AsciiUtil;
     32 import com.ibm.icu.impl.locale.BaseLocale;
     33 import com.ibm.icu.impl.locale.Extension;
     34 import com.ibm.icu.impl.locale.InternalLocaleBuilder;
     35 import com.ibm.icu.impl.locale.LanguageTag;
     36 import com.ibm.icu.impl.locale.LocaleExtensions;
     37 import com.ibm.icu.impl.locale.LocaleSyntaxException;
     38 import com.ibm.icu.impl.locale.ParseStatus;
     39 import com.ibm.icu.impl.locale.UnicodeLocaleExtension;
     40 
     41 /**
     42  * {@icuenhanced java.util.Locale}.{@icu _usage_}
     43  *
     44  * A class analogous to {@link java.util.Locale} that provides additional
     45  * support for ICU protocol.  In ICU 3.0 this class is enhanced to support
     46  * RFC 3066 language identifiers.
     47  *
     48  * <p>Many classes and services in ICU follow a factory idiom, in
     49  * which a factory method or object responds to a client request with
     50  * an object.  The request includes a locale (the <i>requested</i>
     51  * locale), and the returned object is constructed using data for that
     52  * locale.  The system may lack data for the requested locale, in
     53  * which case the locale fallback mechanism will be invoked until a
     54  * populated locale is found (the <i>valid</i> locale).  Furthermore,
     55  * even when a populated locale is found (the <i>valid</i> locale),
     56  * further fallback may be required to reach a locale containing the
     57  * specific data required by the service (the <i>actual</i> locale).
     58  *
     59  * <p>ULocale performs <b>'normalization'</b> and <b>'canonicalization'</b> of locale ids.
     60  * Normalization 'cleans up' ICU locale ids as follows:
     61  * <ul>
     62  * <li>language, script, country, variant, and keywords are properly cased<br>
     63  * (lower, title, upper, upper, and lower case respectively)</li>
     64  * <li>hyphens used as separators are converted to underscores</li>
     65  * <li>three-letter language and country ids are converted to two-letter
     66  * equivalents where available</li>
     67  * <li>surrounding spaces are removed from keywords and values</li>
     68  * <li>if there are multiple keywords, they are put in sorted order</li>
     69  * </ul>
     70  * Canonicalization additionally performs the following:
     71  * <ul>
     72  * <li>POSIX ids are converted to ICU format IDs</li>
     73  * <li>'grandfathered' 3066 ids are converted to ICU standard form</li>
     74  * <li>'PREEURO' and 'EURO' variants are converted to currency keyword form,
     75  * with the currency
     76  * id appropriate to the country of the locale (for PREEURO) or EUR (for EURO).
     77  * </ul>
     78  * All ULocale constructors automatically normalize the locale id.  To handle
     79  * POSIX ids, <code>canonicalize</code> can be called to convert the id
     80  * to canonical form, or the <code>canonicalInstance</code> factory method
     81  * can be called.</p>
     82  *
     83  * <p>This class provides selectors {@link #VALID_LOCALE} and {@link
     84  * #ACTUAL_LOCALE} intended for use in methods named
     85  * <tt>getLocale()</tt>.  These methods exist in several ICU classes,
     86  * including {@link com.ibm.icu.util.Calendar}, {@link
     87  * com.ibm.icu.util.Currency}, {@link com.ibm.icu.text.UFormat},
     88  * {@link com.ibm.icu.text.BreakIterator},
     89  * <a href="../text/Collator.html" title="class in com.ibm.icu.text"><code>Collator</code></a>,
     90  * {@link com.ibm.icu.text.DateFormatSymbols}, and {@link
     91  * com.ibm.icu.text.DecimalFormatSymbols} and their subclasses, if
     92  * any.  Once an object of one of these classes has been created,
     93  * <tt>getLocale()</tt> may be called on it to determine the valid and
     94  * actual locale arrived at during the object's construction.
     95  *
     96  * <p>Note: The <i>actual</i> locale is returned correctly, but the <i>valid</i>
     97  * locale is not, in most cases.
     98  *
     99  * @see java.util.Locale
    100  * @author weiv
    101  * @author Alan Liu
    102  * @author Ram Viswanadha
    103  * @stable ICU 2.8
    104  */
    105 public final class ULocale implements Serializable {
    106     // using serialver from jdk1.4.2_05
    107     private static final long serialVersionUID = 3715177670352309217L;
    108 
    109     /**
    110      * Useful constant for language.
    111      * @stable ICU 3.0
    112      */
    113     public static final ULocale ENGLISH = new ULocale("en", Locale.ENGLISH);
    114 
    115     /**
    116      * Useful constant for language.
    117      * @stable ICU 3.0
    118      */
    119     public static final ULocale FRENCH = new ULocale("fr", Locale.FRENCH);
    120 
    121     /**
    122      * Useful constant for language.
    123      * @stable ICU 3.0
    124      */
    125     public static final ULocale GERMAN = new ULocale("de", Locale.GERMAN);
    126 
    127     /**
    128      * Useful constant for language.
    129      * @stable ICU 3.0
    130      */
    131     public static final ULocale ITALIAN = new ULocale("it", Locale.ITALIAN);
    132 
    133     /**
    134      * Useful constant for language.
    135      * @stable ICU 3.0
    136      */
    137     public static final ULocale JAPANESE = new ULocale("ja", Locale.JAPANESE);
    138 
    139     /**
    140      * Useful constant for language.
    141      * @stable ICU 3.0
    142      */
    143     public static final ULocale KOREAN = new ULocale("ko", Locale.KOREAN);
    144 
    145     /**
    146      * Useful constant for language.
    147      * @stable ICU 3.0
    148      */
    149     public static final ULocale CHINESE = new ULocale("zh", Locale.CHINESE);
    150 
    151     /**
    152      * Useful constant for language.
    153      * @stable ICU 3.0
    154      */
    155     public static final ULocale SIMPLIFIED_CHINESE = new ULocale("zh_Hans", Locale.CHINESE);
    156 
    157     /**
    158      * Useful constant for language.
    159      * @stable ICU 3.0
    160      */
    161     public static final ULocale TRADITIONAL_CHINESE = new ULocale("zh_Hant", Locale.CHINESE);
    162 
    163     /**
    164      * Useful constant for country/region.
    165      * @stable ICU 3.0
    166      */
    167     public static final ULocale FRANCE = new ULocale("fr_FR", Locale.FRANCE);
    168 
    169     /**
    170      * Useful constant for country/region.
    171      * @stable ICU 3.0
    172      */
    173     public static final ULocale GERMANY = new ULocale("de_DE", Locale.GERMANY);
    174 
    175     /**
    176      * Useful constant for country/region.
    177      * @stable ICU 3.0
    178      */
    179     public static final ULocale ITALY = new ULocale("it_IT", Locale.ITALY);
    180 
    181     /**
    182      * Useful constant for country/region.
    183      * @stable ICU 3.0
    184      */
    185     public static final ULocale JAPAN = new ULocale("ja_JP", Locale.JAPAN);
    186 
    187     /**
    188      * Useful constant for country/region.
    189      * @stable ICU 3.0
    190      */
    191     public static final ULocale KOREA = new ULocale("ko_KR", Locale.KOREA);
    192 
    193     /**
    194      * Useful constant for country/region.
    195      * @stable ICU 3.0
    196      */
    197     public static final ULocale CHINA = new ULocale("zh_Hans_CN", Locale.CHINA);
    198 
    199     /**
    200      * Useful constant for country/region.
    201      * @stable ICU 3.0
    202      */
    203     public static final ULocale PRC = CHINA;
    204 
    205     /**
    206      * Useful constant for country/region.
    207      * @stable ICU 3.0
    208      */
    209     public static final ULocale TAIWAN = new ULocale("zh_Hant_TW", Locale.TAIWAN);
    210 
    211     /**
    212      * Useful constant for country/region.
    213      * @stable ICU 3.0
    214      */
    215     public static final ULocale UK = new ULocale("en_GB", Locale.UK);
    216 
    217     /**
    218      * Useful constant for country/region.
    219      * @stable ICU 3.0
    220      */
    221     public static final ULocale US = new ULocale("en_US", Locale.US);
    222 
    223     /**
    224      * Useful constant for country/region.
    225      * @stable ICU 3.0
    226      */
    227     public static final ULocale CANADA = new ULocale("en_CA", Locale.CANADA);
    228 
    229     /**
    230      * Useful constant for country/region.
    231      * @stable ICU 3.0
    232      */
    233     public static final ULocale CANADA_FRENCH = new ULocale("fr_CA", Locale.CANADA_FRENCH);
    234 
    235     /**
    236      * Handy constant.
    237      */
    238     private static final String EMPTY_STRING = "";
    239 
    240     // Used in both ULocale and LocaleIDParser, so moved up here.
    241     private static final char UNDERSCORE            = '_';
    242 
    243     // default empty locale
    244     private static final Locale EMPTY_LOCALE = new Locale("", "");
    245 
    246     // special keyword key for Unicode locale attributes
    247     private static final String LOCALE_ATTRIBUTE_KEY = "attribute";
    248 
    249     /**
    250      * The root ULocale.
    251      * @stable ICU 2.8
    252      */
    253     public static final ULocale ROOT = new ULocale("", EMPTY_LOCALE);
    254 
    255     /**
    256      * Enum for locale categories. These locale categories are used to get/set the default locale for
    257      * the specific functionality represented by the category.
    258      * @stable ICU 49
    259      */
    260     public enum Category {
    261         /**
    262          * Category used to represent the default locale for displaying user interfaces.
    263          * @stable ICU 49
    264          */
    265         DISPLAY,
    266         /**
    267          * Category used to represent the default locale for formatting date, number and/or currency.
    268          * @stable ICU 49
    269          */
    270         FORMAT
    271     }
    272 
    273     private static final SimpleCache<Locale, ULocale> CACHE = new SimpleCache<Locale, ULocale>();
    274 
    275     /**
    276      * Cache the locale.
    277      */
    278     private transient volatile Locale locale;
    279 
    280     /**
    281      * The raw localeID that we were passed in.
    282      */
    283     private String localeID;
    284 
    285     /**
    286      * Cache the locale data container fields.
    287      * In future, we want to use them as the primary locale identifier storage.
    288      */
    289     private transient volatile BaseLocale baseLocale;
    290     private transient volatile LocaleExtensions extensions;
    291 
    292 
    293     private static String[][] CANONICALIZE_MAP;
    294     private static String[][] variantsToKeywords;
    295 
    296     private static void initCANONICALIZE_MAP() {
    297         if (CANONICALIZE_MAP == null) {
    298             /**
    299              * This table lists pairs of locale ids for canonicalization.  The
    300              * The 1st item is the normalized id. The 2nd item is the
    301              * canonicalized id. The 3rd is the keyword. The 4th is the keyword value.
    302              */
    303             String[][] tempCANONICALIZE_MAP = {
    304 //              { EMPTY_STRING,     "en_US_POSIX", null, null }, /* .NET name */
    305                 { "C",              "en_US_POSIX", null, null }, /* POSIX name */
    306                 { "art_LOJBAN",     "jbo", null, null }, /* registered name */
    307                 { "az_AZ_CYRL",     "az_Cyrl_AZ", null, null }, /* .NET name */
    308                 { "az_AZ_LATN",     "az_Latn_AZ", null, null }, /* .NET name */
    309                 { "ca_ES_PREEURO",  "ca_ES", "currency", "ESP" },
    310                 { "cel_GAULISH",    "cel__GAULISH", null, null }, /* registered name */
    311                 { "de_1901",        "de__1901", null, null }, /* registered name */
    312                 { "de_1906",        "de__1906", null, null }, /* registered name */
    313                 { "de__PHONEBOOK",  "de", "collation", "phonebook" }, /* Old ICU name */
    314                 { "de_AT_PREEURO",  "de_AT", "currency", "ATS" },
    315                 { "de_DE_PREEURO",  "de_DE", "currency", "DEM" },
    316                 { "de_LU_PREEURO",  "de_LU", "currency", "EUR" },
    317                 { "el_GR_PREEURO",  "el_GR", "currency", "GRD" },
    318                 { "en_BOONT",       "en__BOONT", null, null }, /* registered name */
    319                 { "en_SCOUSE",      "en__SCOUSE", null, null }, /* registered name */
    320                 { "en_BE_PREEURO",  "en_BE", "currency", "BEF" },
    321                 { "en_IE_PREEURO",  "en_IE", "currency", "IEP" },
    322                 { "es__TRADITIONAL", "es", "collation", "traditional" }, /* Old ICU name */
    323                 { "es_ES_PREEURO",  "es_ES", "currency", "ESP" },
    324                 { "eu_ES_PREEURO",  "eu_ES", "currency", "ESP" },
    325                 { "fi_FI_PREEURO",  "fi_FI", "currency", "FIM" },
    326                 { "fr_BE_PREEURO",  "fr_BE", "currency", "BEF" },
    327                 { "fr_FR_PREEURO",  "fr_FR", "currency", "FRF" },
    328                 { "fr_LU_PREEURO",  "fr_LU", "currency", "LUF" },
    329                 { "ga_IE_PREEURO",  "ga_IE", "currency", "IEP" },
    330                 { "gl_ES_PREEURO",  "gl_ES", "currency", "ESP" },
    331                 { "hi__DIRECT",     "hi", "collation", "direct" }, /* Old ICU name */
    332                 { "it_IT_PREEURO",  "it_IT", "currency", "ITL" },
    333                 { "ja_JP_TRADITIONAL", "ja_JP", "calendar", "japanese" },
    334 //              { "nb_NO_NY",       "nn_NO", null, null },
    335                 { "nl_BE_PREEURO",  "nl_BE", "currency", "BEF" },
    336                 { "nl_NL_PREEURO",  "nl_NL", "currency", "NLG" },
    337                 { "pt_PT_PREEURO",  "pt_PT", "currency", "PTE" },
    338                 { "sl_ROZAJ",       "sl__ROZAJ", null, null }, /* registered name */
    339                 { "sr_SP_CYRL",     "sr_Cyrl_RS", null, null }, /* .NET name */
    340                 { "sr_SP_LATN",     "sr_Latn_RS", null, null }, /* .NET name */
    341                 { "sr_YU_CYRILLIC", "sr_Cyrl_RS", null, null }, /* Linux name */
    342                 { "th_TH_TRADITIONAL", "th_TH", "calendar", "buddhist" }, /* Old ICU name */
    343                 { "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", null, null }, /* Linux name */
    344                 { "uz_UZ_CYRL",     "uz_Cyrl_UZ", null, null }, /* .NET name */
    345                 { "uz_UZ_LATN",     "uz_Latn_UZ", null, null }, /* .NET name */
    346                 { "zh_CHS",         "zh_Hans", null, null }, /* .NET name */
    347                 { "zh_CHT",         "zh_Hant", null, null }, /* .NET name */
    348                 { "zh_GAN",         "zh__GAN", null, null }, /* registered name */
    349                 { "zh_GUOYU",       "zh", null, null }, /* registered name */
    350                 { "zh_HAKKA",       "zh__HAKKA", null, null }, /* registered name */
    351                 { "zh_MIN",         "zh__MIN", null, null }, /* registered name */
    352                 { "zh_MIN_NAN",     "zh__MINNAN", null, null }, /* registered name */
    353                 { "zh_WUU",         "zh__WUU", null, null }, /* registered name */
    354                 { "zh_XIANG",       "zh__XIANG", null, null }, /* registered name */
    355                 { "zh_YUE",         "zh__YUE", null, null } /* registered name */
    356             };
    357 
    358             synchronized (ULocale.class) {
    359                 if (CANONICALIZE_MAP == null) {
    360                     CANONICALIZE_MAP = tempCANONICALIZE_MAP;
    361                 }
    362             }
    363         }
    364         if (variantsToKeywords == null) {
    365             /**
    366              * This table lists pairs of locale ids for canonicalization.  The
    367              * The first item is the normalized variant id.
    368              */
    369             String[][] tempVariantsToKeywords = {
    370                     { "EURO",   "currency", "EUR" },
    371                     { "PINYIN", "collation", "pinyin" }, /* Solaris variant */
    372                     { "STROKE", "collation", "stroke" }  /* Solaris variant */
    373             };
    374 
    375             synchronized (ULocale.class) {
    376                 if (variantsToKeywords == null) {
    377                     variantsToKeywords = tempVariantsToKeywords;
    378                 }
    379             }
    380         }
    381     }
    382 
    383     /**
    384      * Private constructor used by static initializers.
    385      */
    386     private ULocale(String localeID, Locale locale) {
    387         this.localeID = localeID;
    388         this.locale = locale;
    389     }
    390 
    391     /**
    392      * Construct a ULocale object from a {@link java.util.Locale}.
    393      * @param loc a JDK locale
    394      */
    395     private ULocale(Locale loc) {
    396         this.localeID = getName(forLocale(loc).toString());
    397         this.locale = loc;
    398     }
    399 
    400     /**
    401      * {@icu} Returns a ULocale object for a {@link java.util.Locale}.
    402      * The ULocale is canonicalized.
    403      * @param loc a JDK locale
    404      * @stable ICU 3.2
    405      */
    406     public static ULocale forLocale(Locale loc) {
    407         if (loc == null) {
    408             return null;
    409         }
    410         ULocale result = CACHE.get(loc);
    411         if (result == null) {
    412             result = JDKLocaleHelper.toULocale(loc);
    413             CACHE.put(loc, result);
    414         }
    415         return result;
    416     }
    417 
    418     /**
    419      * {@icu} Constructs a ULocale from a RFC 3066 locale ID. The locale ID consists
    420      * of optional language, script, country, and variant fields in that order,
    421      * separated by underscores, followed by an optional keyword list.  The
    422      * script, if present, is four characters long-- this distinguishes it
    423      * from a country code, which is two characters long.  Other fields
    424      * are distinguished by position as indicated by the underscores.  The
    425      * start of the keyword list is indicated by '@', and consists of two
    426      * or more keyword/value pairs separated by semicolons(';').
    427      *
    428      * <p>This constructor does not canonicalize the localeID.  So, for
    429      * example, "zh__pinyin" remains unchanged instead of converting
    430      * to "zh@collation=pinyin".  By default ICU only recognizes the
    431      * latter as specifying pinyin collation.  Use {@link #createCanonical}
    432      * or {@link #canonicalize} if you need to canonicalize the localeID.
    433      *
    434      * @param localeID string representation of the locale, e.g:
    435      * "en_US", "sy_Cyrl_YU", "zh__pinyin", "es_ES@currency=EUR;collation=traditional"
    436      * @stable ICU 2.8
    437      */
    438     public ULocale(String localeID) {
    439         this.localeID = getName(localeID);
    440     }
    441 
    442     /**
    443      * Convenience overload of ULocale(String, String, String) for
    444      * compatibility with java.util.Locale.
    445      * @see #ULocale(String, String, String)
    446      * @stable ICU 3.4
    447      */
    448     public ULocale(String a, String b) {
    449         this(a, b, null);
    450     }
    451 
    452     /**
    453      * Constructs a ULocale from a localeID constructed from the three 'fields' a, b, and
    454      * c.  These fields are concatenated using underscores to form a localeID of the form
    455      * a_b_c, which is then handled like the localeID passed to <code>ULocale(String
    456      * localeID)</code>.
    457      *
    458      * <p>Java locale strings consisting of language, country, and
    459      * variant will be handled by this form, since the country code
    460      * (being shorter than four letters long) will not be interpreted
    461      * as a script code.  If a script code is present, the final
    462      * argument ('c') will be interpreted as the country code.  It is
    463      * recommended that this constructor only be used to ease porting,
    464      * and that clients instead use the single-argument constructor
    465      * when constructing a ULocale from a localeID.
    466      * @param a first component of the locale id
    467      * @param b second component of the locale id
    468      * @param c third component of the locale id
    469      * @see #ULocale(String)
    470      * @stable ICU 3.0
    471      */
    472     public ULocale(String a, String b, String c) {
    473         localeID = getName(lscvToID(a, b, c, EMPTY_STRING));
    474     }
    475 
    476     /**
    477      * {@icu} Creates a ULocale from the id by first canonicalizing the id.
    478      * @param nonCanonicalID the locale id to canonicalize
    479      * @return the locale created from the canonical version of the ID.
    480      * @stable ICU 3.0
    481      */
    482     public static ULocale createCanonical(String nonCanonicalID) {
    483         return new ULocale(canonicalize(nonCanonicalID), (Locale)null);
    484     }
    485 
    486     private static String lscvToID(String lang, String script, String country, String variant) {
    487         StringBuilder buf = new StringBuilder();
    488 
    489         if (lang != null && lang.length() > 0) {
    490             buf.append(lang);
    491         }
    492         if (script != null && script.length() > 0) {
    493             buf.append(UNDERSCORE);
    494             buf.append(script);
    495         }
    496         if (country != null && country.length() > 0) {
    497             buf.append(UNDERSCORE);
    498             buf.append(country);
    499         }
    500         if (variant != null && variant.length() > 0) {
    501             if (country == null || country.length() == 0) {
    502                 buf.append(UNDERSCORE);
    503             }
    504             buf.append(UNDERSCORE);
    505             buf.append(variant);
    506         }
    507         return buf.toString();
    508     }
    509 
    510     /**
    511      * {@icu} Converts this ULocale object to a {@link java.util.Locale}.
    512      * @return a JDK locale that either exactly represents this object
    513      * or is the closest approximation.
    514      * @stable ICU 2.8
    515      */
    516     public Locale toLocale() {
    517         if (locale == null) {
    518             locale = JDKLocaleHelper.toLocale(this);
    519         }
    520         return locale;
    521     }
    522 
    523     private static ICUCache<String, String> nameCache = new SimpleCache<String, String>();
    524 
    525     /**
    526      * Keep our own default ULocale.
    527      */
    528     private static Locale defaultLocale = Locale.getDefault();
    529     private static ULocale defaultULocale = forLocale(defaultLocale);
    530 
    531     private static Locale[] defaultCategoryLocales = new Locale[Category.values().length];
    532     private static ULocale[] defaultCategoryULocales = new ULocale[Category.values().length];
    533 
    534     static {
    535         for (Category cat: Category.values()) {
    536             int idx = cat.ordinal();
    537             defaultCategoryLocales[idx] = JDKLocaleHelper.getDefault(cat);
    538             defaultCategoryULocales[idx] = forLocale(defaultCategoryLocales[idx]);
    539         }
    540     }
    541 
    542     /**
    543      * Returns the current default ULocale.
    544      * @return the default ULocale.
    545      * @stable ICU 2.8
    546      */
    547     public static ULocale getDefault() {
    548         synchronized (ULocale.class) {
    549             if (defaultULocale == null) {
    550                 // When Java's default locale has extensions (such as ja-JP-u-ca-japanese),
    551                 // Locale -> ULocale mapping requires BCP47 keyword mapping data that is currently
    552                 // stored in a resource bundle. However, UResourceBundle currently requires
    553                 // non-null default ULocale. For now, this implementation returns ULocale.ROOT
    554                 // to avoid the problem.
    555 
    556                 // TODO: Consider moving BCP47 mapping data out of resource bundle later.
    557 
    558                 return ULocale.ROOT;
    559             }
    560             Locale currentDefault = Locale.getDefault();
    561             if (!defaultLocale.equals(currentDefault)) {
    562                 defaultLocale = currentDefault;
    563                 defaultULocale = forLocale(currentDefault);
    564 
    565                 if (!JDKLocaleHelper.isJava7orNewer()) {
    566                     // Detected Java default Locale change.
    567                     // We need to update category defaults to match the
    568                     // Java 7's behavior on Java 6 or older environment.
    569                     for (Category cat : Category.values()) {
    570                         int idx = cat.ordinal();
    571                         defaultCategoryLocales[idx] = currentDefault;
    572                         defaultCategoryULocales[idx] = forLocale(currentDefault);
    573                     }
    574                 }
    575             }
    576             return defaultULocale;
    577         }
    578     }
    579 
    580     /**
    581      * {@icu} Sets the default ULocale.  This also sets the default Locale.
    582      * If the caller does not have write permission to the
    583      * user.language property, a security exception will be thrown,
    584      * and the default ULocale will remain unchanged.
    585      * <p>
    586      * By setting the default ULocale with this method, all of the default categoy locales
    587      * are also set to the specified default ULocale.
    588      * @param newLocale the new default locale
    589      * @throws SecurityException if a security manager exists and its
    590      *        <code>checkPermission</code> method doesn't allow the operation.
    591      * @throws NullPointerException if <code>newLocale</code> is null
    592      * @see SecurityManager#checkPermission(java.security.Permission)
    593      * @see java.util.PropertyPermission
    594      * @see ULocale#setDefault(Category, ULocale)
    595      * @stable ICU 3.0
    596      */
    597     public static synchronized void setDefault(ULocale newLocale){
    598         defaultLocale = newLocale.toLocale();
    599         Locale.setDefault(defaultLocale);
    600         defaultULocale = newLocale;
    601         // This method also updates all category default locales
    602         for (Category cat : Category.values()) {
    603             setDefault(cat, newLocale);
    604         }
    605     }
    606 
    607     /**
    608      * Returns the current default ULocale for the specified category.
    609      *
    610      * @param category the category
    611      * @return the default ULocale for the specified category.
    612      * @stable ICU 49
    613      */
    614     public static ULocale getDefault(Category category) {
    615         synchronized (ULocale.class) {
    616             int idx = category.ordinal();
    617             if (defaultCategoryULocales[idx] == null) {
    618                 // Just in case this method is called during ULocale class
    619                 // initialization. Unlike getDefault(), we do not have
    620                 // cyclic dependency for category default.
    621                 return ULocale.ROOT;
    622             }
    623             if (JDKLocaleHelper.isJava7orNewer()) {
    624                 Locale currentCategoryDefault = JDKLocaleHelper.getDefault(category);
    625                 if (!defaultCategoryLocales[idx].equals(currentCategoryDefault)) {
    626                     defaultCategoryLocales[idx] = currentCategoryDefault;
    627                     defaultCategoryULocales[idx] = forLocale(currentCategoryDefault);
    628                 }
    629             } else {
    630                 // java.util.Locale.setDefault(Locale) in Java 7 updates
    631                 // category locale defaults. On Java 6 or older environment,
    632                 // ICU4J checks if the default locale has changed and update
    633                 // category ULocales here if necessary.
    634 
    635                 // Note: When java.util.Locale.setDefault(Locale) is called
    636                 // with a Locale same with the previous one, Java 7 still
    637                 // updates category locale defaults. On Java 6 or older env,
    638                 // there is no good way to detect the event, ICU4J simply
    639                 // check if the default Java Locale has changed since last
    640                 // time.
    641 
    642                 Locale currentDefault = Locale.getDefault();
    643                 if (!defaultLocale.equals(currentDefault)) {
    644                     defaultLocale = currentDefault;
    645                     defaultULocale = forLocale(currentDefault);
    646 
    647                     for (Category cat : Category.values()) {
    648                         int tmpIdx = cat.ordinal();
    649                         defaultCategoryLocales[tmpIdx] = currentDefault;
    650                         defaultCategoryULocales[tmpIdx] = forLocale(currentDefault);
    651                     }
    652                 }
    653 
    654                 // No synchronization with JDK Locale, because category default
    655                 // is not supported in Java 6 or older versions
    656             }
    657             return defaultCategoryULocales[idx];
    658         }
    659     }
    660 
    661     /**
    662      * Sets the default <code>ULocale</code> for the specified <code>Category</code>.
    663      * This also sets the default <code>Locale</code> for the specified <code>Category</code>
    664      * of the JVM. If the caller does not have write permission to the
    665      * user.language property, a security exception will be thrown,
    666      * and the default ULocale for the specified Category will remain unchanged.
    667      *
    668      * @param category the specified category to set the default locale
    669      * @param newLocale the new default locale
    670      * @see SecurityManager#checkPermission(java.security.Permission)
    671      * @see java.util.PropertyPermission
    672      * @stable ICU 49
    673      */
    674     public static synchronized void setDefault(Category category, ULocale newLocale) {
    675         Locale newJavaDefault = newLocale.toLocale();
    676         int idx = category.ordinal();
    677         defaultCategoryULocales[idx] = newLocale;
    678         defaultCategoryLocales[idx] = newJavaDefault;
    679         JDKLocaleHelper.setDefault(category, newJavaDefault);
    680     }
    681 
    682     /**
    683      * This is for compatibility with Locale-- in actuality, since ULocale is
    684      * immutable, there is no reason to clone it, so this API returns 'this'.
    685      * @stable ICU 3.0
    686      */
    687     public Object clone() {
    688         return this;
    689     }
    690 
    691     /**
    692      * Returns the hashCode.
    693      * @stable ICU 3.0
    694      */
    695     public int hashCode() {
    696         return localeID.hashCode();
    697     }
    698 
    699     /**
    700      * Returns true if the other object is another ULocale with the
    701      * same full name, or is a String localeID that matches the full name.
    702      * Note that since names are not canonicalized, two ULocales that
    703      * function identically might not compare equal.
    704      *
    705      * @return true if this Locale is equal to the specified object.
    706      * @stable ICU 3.0
    707      */
    708     public boolean equals(Object obj) {
    709         if (this == obj) {
    710             return true;
    711         }
    712         if (obj instanceof String) {
    713             return localeID.equals((String)obj);
    714         }
    715         if (obj instanceof ULocale) {
    716             return localeID.equals(((ULocale)obj).localeID);
    717         }
    718         return false;
    719     }
    720 
    721     /**
    722      * {@icunote} Unlike the Locale API, this returns an array of <code>ULocale</code>,
    723      * not <code>Locale</code>.  Returns a list of all installed locales.
    724      * @stable ICU 3.0
    725      */
    726     public static ULocale[] getAvailableLocales() {
    727         //#com.ibm.icu.base
    728         if (availableLocales == null) {
    729             synchronized (ULocale.class) {
    730                 if (availableLocales == null) {
    731                     Locale[] locales = Locale.getAvailableLocales();
    732                     availableLocales = new ULocale[locales.length];
    733                     for (int i = 0; i < locales.length; i++) {
    734                         availableLocales[i] = ULocale.forLocale(locales[i]);
    735                     }
    736                 }
    737             }
    738         }
    739         return availableLocales.clone();
    740     }
    741     private static volatile ULocale[] availableLocales = null;
    742 
    743     /**
    744      * Returns a list of all 2-letter country codes defined in ISO 3166.
    745      * Can be used to create Locales.
    746      * @stable ICU 3.0
    747      */
    748     public static String[] getISOCountries() {
    749         return LocaleIDs.getISOCountries();
    750     }
    751 
    752     /**
    753      * Returns a list of all 2-letter language codes defined in ISO 639.
    754      * Can be used to create Locales.
    755      * [NOTE:  ISO 639 is not a stable standard-- some languages' codes have changed.
    756      * The list this function returns includes both the new and the old codes for the
    757      * languages whose codes have changed.]
    758      * @stable ICU 3.0
    759      */
    760     public static String[] getISOLanguages() {
    761         return LocaleIDs.getISOLanguages();
    762     }
    763 
    764     /**
    765      * Returns the language code for this locale, which will either be the empty string
    766      * or a lowercase ISO 639 code.
    767      * @see #getDisplayLanguage()
    768      * @see #getDisplayLanguage(ULocale)
    769      * @stable ICU 3.0
    770      */
    771     public String getLanguage() {
    772         return getLanguage(localeID);
    773     }
    774 
    775     /**
    776      * Returns the language code for the locale ID,
    777      * which will either be the empty string
    778      * or a lowercase ISO 639 code.
    779      * @see #getDisplayLanguage()
    780      * @see #getDisplayLanguage(ULocale)
    781      * @stable ICU 3.0
    782      */
    783     public static String getLanguage(String localeID) {
    784         return new LocaleIDParser(localeID).getLanguage();
    785     }
    786 
    787     /**
    788      * {@icu} Returns the script code for this locale, which might be the empty string.
    789      * @see #getDisplayScript()
    790      * @see #getDisplayScript(ULocale)
    791      * @stable ICU 3.0
    792      */
    793     public String getScript() {
    794         return getScript(localeID);
    795     }
    796 
    797     /**
    798      * {@icu} Returns the script code for the specified locale, which might be the empty
    799      * string.
    800      * @see #getDisplayScript()
    801      * @see #getDisplayScript(ULocale)
    802      * @stable ICU 3.0
    803      */
    804     public static String getScript(String localeID) {
    805         return new LocaleIDParser(localeID).getScript();
    806     }
    807 
    808     /**
    809      * Returns the country/region code for this locale, which will either be the empty string
    810      * or an uppercase ISO 3166 2-letter code.
    811      * @see #getDisplayCountry()
    812      * @see #getDisplayCountry(ULocale)
    813      * @stable ICU 3.0
    814      */
    815     public String getCountry() {
    816         return getCountry(localeID);
    817     }
    818 
    819     /**
    820      * Returns the country/region code for this locale, which will either be the empty string
    821      * or an uppercase ISO 3166 2-letter code.
    822      * @param localeID The locale identification string.
    823      * @see #getDisplayCountry()
    824      * @see #getDisplayCountry(ULocale)
    825      * @stable ICU 3.0
    826      */
    827     public static String getCountry(String localeID) {
    828         return new LocaleIDParser(localeID).getCountry();
    829     }
    830 
    831     /**
    832      * Returns the variant code for this locale, which might be the empty string.
    833      * @see #getDisplayVariant()
    834      * @see #getDisplayVariant(ULocale)
    835      * @stable ICU 3.0
    836      */
    837     public String getVariant() {
    838         return getVariant(localeID);
    839     }
    840 
    841     /**
    842      * Returns the variant code for the specified locale, which might be the empty string.
    843      * @see #getDisplayVariant()
    844      * @see #getDisplayVariant(ULocale)
    845      * @stable ICU 3.0
    846      */
    847     public static String getVariant(String localeID) {
    848         return new LocaleIDParser(localeID).getVariant();
    849     }
    850 
    851     /**
    852      * {@icu} Returns the fallback locale for the specified locale, which might be the
    853      * empty string.
    854      * @stable ICU 3.2
    855      */
    856     public static String getFallback(String localeID) {
    857         return getFallbackString(getName(localeID));
    858     }
    859 
    860     /**
    861      * {@icu} Returns the fallback locale for this locale.  If this locale is root,
    862      * returns null.
    863      * @stable ICU 3.2
    864      */
    865     public ULocale getFallback() {
    866         if (localeID.length() == 0 || localeID.charAt(0) == '@') {
    867             return null;
    868         }
    869         return new ULocale(getFallbackString(localeID), (Locale)null);
    870     }
    871 
    872     /**
    873      * Returns the given (canonical) locale id minus the last part before the tags.
    874      */
    875     private static String getFallbackString(String fallback) {
    876         int extStart = fallback.indexOf('@');
    877         if (extStart == -1) {
    878             extStart = fallback.length();
    879         }
    880         int last = fallback.lastIndexOf('_', extStart);
    881         if (last == -1) {
    882             last = 0;
    883         } else {
    884             // truncate empty segment
    885             while (last > 0) {
    886                 if (fallback.charAt(last - 1) != '_') {
    887                     break;
    888                 }
    889                 last--;
    890             }
    891         }
    892         return fallback.substring(0, last) + fallback.substring(extStart);
    893     }
    894 
    895     /**
    896      * {@icu} Returns the (normalized) base name for this locale.
    897      * @return the base name as a String.
    898      * @stable ICU 3.0
    899      */
    900     public String getBaseName() {
    901         return getBaseName(localeID);
    902     }
    903 
    904     /**
    905      * {@icu} Returns the (normalized) base name for the specified locale.
    906      * @param localeID the locale ID as a string
    907      * @return the base name as a String.
    908      * @stable ICU 3.0
    909      */
    910     public static String getBaseName(String localeID){
    911         if (localeID.indexOf('@') == -1) {
    912             return localeID;
    913         }
    914         return new LocaleIDParser(localeID).getBaseName();
    915     }
    916 
    917     /**
    918      * {@icu} Returns the (normalized) full name for this locale.
    919      *
    920      * @return String the full name of the localeID
    921      * @stable ICU 3.0
    922      */
    923     public String getName() {
    924         return localeID; // always normalized
    925     }
    926 
    927     /**
    928      * Gets the shortest length subtag's size.
    929      *
    930      * @param localeID
    931      * @return The size of the shortest length subtag
    932      **/
    933     private static int getShortestSubtagLength(String localeID) {
    934         int localeIDLength = localeID.length();
    935         int length = localeIDLength;
    936         boolean reset = true;
    937         int tmpLength = 0;
    938 
    939         for (int i = 0; i < localeIDLength; i++) {
    940             if (localeID.charAt(i) != '_' && localeID.charAt(i) != '-') {
    941                 if (reset) {
    942                     reset = false;
    943                     tmpLength = 0;
    944                 }
    945                 tmpLength++;
    946             } else {
    947                 if (tmpLength != 0 && tmpLength < length) {
    948                     length = tmpLength;
    949                 }
    950                 reset = true;
    951             }
    952         }
    953 
    954         return length;
    955     }
    956 
    957     /**
    958      * {@icu} Returns the (normalized) full name for the specified locale.
    959      *
    960      * @param localeID the localeID as a string
    961      * @return String the full name of the localeID
    962      * @stable ICU 3.0
    963      */
    964     public static String getName(String localeID){
    965         String tmpLocaleID;
    966         // Convert BCP47 id if necessary
    967         if (localeID != null && !localeID.contains("@") && getShortestSubtagLength(localeID) == 1) {
    968             tmpLocaleID = forLanguageTag(localeID).getName();
    969             if (tmpLocaleID.length() == 0) {
    970                 tmpLocaleID = localeID;
    971             }
    972         } else {
    973             tmpLocaleID = localeID;
    974         }
    975         String name = nameCache.get(tmpLocaleID);
    976         if (name == null) {
    977             name = new LocaleIDParser(tmpLocaleID).getName();
    978             nameCache.put(tmpLocaleID, name);
    979         }
    980         return name;
    981     }
    982 
    983     /**
    984      * Returns a string representation of this object.
    985      * @stable ICU 3.0
    986      */
    987     public String toString() {
    988         return localeID;
    989     }
    990 
    991     /**
    992      * {@icu} Returns an iterator over keywords for this locale.  If there
    993      * are no keywords, returns null.
    994      * @return iterator over keywords, or null if there are no keywords.
    995      * @stable ICU 3.0
    996      */
    997     public Iterator<String> getKeywords() {
    998         return getKeywords(localeID);
    999     }
   1000 
   1001     /**
   1002      * {@icu} Returns an iterator over keywords for the specified locale.  If there
   1003      * are no keywords, returns null.
   1004      * @return an iterator over the keywords in the specified locale, or null
   1005      * if there are no keywords.
   1006      * @stable ICU 3.0
   1007      */
   1008     public static Iterator<String> getKeywords(String localeID){
   1009         return new LocaleIDParser(localeID).getKeywords();
   1010     }
   1011 
   1012     /**
   1013      * {@icu} Returns the value for a keyword in this locale. If the keyword is not
   1014      * defined, returns null.
   1015      * @param keywordName name of the keyword whose value is desired. Case insensitive.
   1016      * @return the value of the keyword, or null.
   1017      * @stable ICU 3.0
   1018      */
   1019     public String getKeywordValue(String keywordName){
   1020         return getKeywordValue(localeID, keywordName);
   1021     }
   1022 
   1023     /**
   1024      * {@icu} Returns the value for a keyword in the specified locale. If the keyword is
   1025      * not defined, returns null.  The locale name does not need to be normalized.
   1026      * @param keywordName name of the keyword whose value is desired. Case insensitive.
   1027      * @return String the value of the keyword as a string
   1028      * @stable ICU 3.0
   1029      */
   1030     public static String getKeywordValue(String localeID, String keywordName) {
   1031         return new LocaleIDParser(localeID).getKeywordValue(keywordName);
   1032     }
   1033 
   1034     /**
   1035      * {@icu} Returns the canonical name for the specified locale ID.  This is used to
   1036      * convert POSIX and other grandfathered IDs to standard ICU form.
   1037      * @param localeID the locale id
   1038      * @return the canonicalized id
   1039      * @stable ICU 3.0
   1040      */
   1041     public static String canonicalize(String localeID){
   1042         LocaleIDParser parser = new LocaleIDParser(localeID, true);
   1043         String baseName = parser.getBaseName();
   1044         boolean foundVariant = false;
   1045 
   1046         // formerly, we always set to en_US_POSIX if the basename was empty, but
   1047         // now we require that the entire id be empty, so that "@foo=bar"
   1048         // will pass through unchanged.
   1049         // {dlf} I'd rather keep "" unchanged.
   1050         if (localeID.equals("")) {
   1051             return "";
   1052 //              return "en_US_POSIX";
   1053         }
   1054 
   1055         // we have an ID in the form xx_Yyyy_ZZ_KKKKK
   1056 
   1057         initCANONICALIZE_MAP();
   1058 
   1059         /* convert the variants to appropriate ID */
   1060         for (int i = 0; i < variantsToKeywords.length; i++) {
   1061             String[] vals = variantsToKeywords[i];
   1062             int idx = baseName.lastIndexOf("_" + vals[0]);
   1063             if (idx > -1) {
   1064                 foundVariant = true;
   1065 
   1066                 baseName = baseName.substring(0, idx);
   1067                 if (baseName.endsWith("_")) {
   1068                     baseName = baseName.substring(0, --idx);
   1069                 }
   1070                 parser.setBaseName(baseName);
   1071                 parser.defaultKeywordValue(vals[1], vals[2]);
   1072                 break;
   1073             }
   1074         }
   1075 
   1076         /* See if this is an already known locale */
   1077         for (int i = 0; i < CANONICALIZE_MAP.length; i++) {
   1078             if (CANONICALIZE_MAP[i][0].equals(baseName)) {
   1079                 foundVariant = true;
   1080 
   1081                 String[] vals = CANONICALIZE_MAP[i];
   1082                 parser.setBaseName(vals[1]);
   1083                 if (vals[2] != null) {
   1084                     parser.defaultKeywordValue(vals[2], vals[3]);
   1085                 }
   1086                 break;
   1087             }
   1088         }
   1089 
   1090         /* total mondo hack for Norwegian, fortunately the main NY case is handled earlier */
   1091         if (!foundVariant) {
   1092             if (parser.getLanguage().equals("nb") && parser.getVariant().equals("NY")) {
   1093                 parser.setBaseName(lscvToID("nn", parser.getScript(), parser.getCountry(), null));
   1094             }
   1095         }
   1096 
   1097         return parser.getName();
   1098     }
   1099 
   1100     /**
   1101      * Given a keyword and a value, return a new locale with an updated
   1102      * keyword and value.  If keyword is null, this removes all keywords from the locale id.
   1103      * Otherwise, if the value is null, this removes the value for this keyword from the
   1104      * locale id.  Otherwise, this adds/replaces the value for this keyword in the locale id.
   1105      * The keyword and value must not be empty.
   1106      * @param keyword the keyword to add/remove, or null to remove all keywords.
   1107      * @param value the value to add/set, or null to remove this particular keyword.
   1108      * @return the updated locale
   1109      * @stable ICU 3.2
   1110      */
   1111     public ULocale setKeywordValue(String keyword, String value) {
   1112         return new ULocale(setKeywordValue(localeID, keyword, value), (Locale)null);
   1113     }
   1114 
   1115     /**
   1116      * Given a locale id, a keyword, and a value, return a new locale id with an updated
   1117      * keyword and value.  If keyword is null, this removes all keywords from the locale id.
   1118      * Otherwise, if the value is null, this removes the value for this keyword from the
   1119      * locale id.  Otherwise, this adds/replaces the value for this keyword in the locale id.
   1120      * The keyword and value must not be empty.
   1121      * @param localeID the locale id to modify
   1122      * @param keyword the keyword to add/remove, or null to remove all keywords.
   1123      * @param value the value to add/set, or null to remove this particular keyword.
   1124      * @return the updated locale id
   1125      * @stable ICU 3.2
   1126      */
   1127     public static String setKeywordValue(String localeID, String keyword, String value) {
   1128         LocaleIDParser parser = new LocaleIDParser(localeID);
   1129         parser.setKeywordValue(keyword, value);
   1130         return parser.getName();
   1131     }
   1132 
   1133     /*
   1134      * Given a locale id, a keyword, and a value, return a new locale id with an updated
   1135      * keyword and value, if the keyword does not already have a value.  The keyword and
   1136      * value must not be null or empty.
   1137      * @param localeID the locale id to modify
   1138      * @param keyword the keyword to add, if not already present
   1139      * @param value the value to add, if not already present
   1140      * @return the updated locale id
   1141      */
   1142 /*    private static String defaultKeywordValue(String localeID, String keyword, String value) {
   1143         LocaleIDParser parser = new LocaleIDParser(localeID);
   1144         parser.defaultKeywordValue(keyword, value);
   1145         return parser.getName();
   1146     }*/
   1147 
   1148     /**
   1149      * Returns a three-letter abbreviation for this locale's language.  If the locale
   1150      * doesn't specify a language, returns the empty string.  Otherwise, returns
   1151      * a lowercase ISO 639-2/T language code.
   1152      * The ISO 639-2 language codes can be found on-line at
   1153      *   <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
   1154      * @exception MissingResourceException Throws MissingResourceException if the
   1155      * three-letter language abbreviation is not available for this locale.
   1156      * @stable ICU 3.0
   1157      */
   1158     public String getISO3Language(){
   1159         return getISO3Language(localeID);
   1160     }
   1161 
   1162     /**
   1163      * Returns a three-letter abbreviation for this locale's language.  If the locale
   1164      * doesn't specify a language, returns the empty string.  Otherwise, returns
   1165      * a lowercase ISO 639-2/T language code.
   1166      * The ISO 639-2 language codes can be found on-line at
   1167      *   <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
   1168      * @exception MissingResourceException Throws MissingResourceException if the
   1169      * three-letter language abbreviation is not available for this locale.
   1170      * @stable ICU 3.0
   1171      */
   1172     public static String getISO3Language(String localeID) {
   1173         return LocaleIDs.getISO3Language(getLanguage(localeID));
   1174     }
   1175 
   1176     /**
   1177      * Returns a three-letter abbreviation for this locale's country/region.  If the locale
   1178      * doesn't specify a country, returns the empty string.  Otherwise, returns
   1179      * an uppercase ISO 3166 3-letter country code.
   1180      * @exception MissingResourceException Throws MissingResourceException if the
   1181      * three-letter country abbreviation is not available for this locale.
   1182      * @stable ICU 3.0
   1183      */
   1184     public String getISO3Country() {
   1185         return getISO3Country(localeID);
   1186     }
   1187 
   1188     /**
   1189      * Returns a three-letter abbreviation for this locale's country/region.  If the locale
   1190      * doesn't specify a country, returns the empty string.  Otherwise, returns
   1191      * an uppercase ISO 3166 3-letter country code.
   1192      * @exception MissingResourceException Throws MissingResourceException if the
   1193      * three-letter country abbreviation is not available for this locale.
   1194      * @stable ICU 3.0
   1195      */
   1196     public static String getISO3Country(String localeID) {
   1197         return LocaleIDs.getISO3Country(getCountry(localeID));
   1198     }
   1199 
   1200     // display names
   1201 
   1202     /**
   1203      * Returns this locale's language localized for display in the default <code>DISPLAY</code> locale.
   1204      * @return the localized language name.
   1205      * @see Category#DISPLAY
   1206      * @stable ICU 3.0
   1207      */
   1208     public String getDisplayLanguage() {
   1209         return getDisplayLanguageInternal(this, getDefault(Category.DISPLAY), false);
   1210     }
   1211 
   1212     /**
   1213      * {@icu} Returns this locale's language localized for display in the provided locale.
   1214      * @param displayLocale the locale in which to display the name.
   1215      * @return the localized language name.
   1216      * @stable ICU 3.0
   1217      */
   1218     public String getDisplayLanguage(ULocale displayLocale) {
   1219         return getDisplayLanguageInternal(this, displayLocale, false);
   1220     }
   1221 
   1222     /**
   1223      * Returns a locale's language localized for display in the provided locale.
   1224      * This is a cover for the ICU4C API.
   1225      * @param localeID the id of the locale whose language will be displayed
   1226      * @param displayLocaleID the id of the locale in which to display the name.
   1227      * @return the localized language name.
   1228      * @stable ICU 3.0
   1229      */
   1230     public static String getDisplayLanguage(String localeID, String displayLocaleID) {
   1231         return getDisplayLanguageInternal(new ULocale(localeID), new ULocale(displayLocaleID),
   1232                 false);
   1233     }
   1234 
   1235     /**
   1236      * Returns a locale's language localized for display in the provided locale.
   1237      * This is a cover for the ICU4C API.
   1238      * @param localeID the id of the locale whose language will be displayed.
   1239      * @param displayLocale the locale in which to display the name.
   1240      * @return the localized language name.
   1241      * @stable ICU 3.0
   1242      */
   1243     public static String getDisplayLanguage(String localeID, ULocale displayLocale) {
   1244         return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, false);
   1245     }
   1246     /**
   1247      * {@icu} Returns this locale's language localized for display in the default <code>DISPLAY</code> locale.
   1248      * If a dialect name is present in the data, then it is returned.
   1249      * @return the localized language name.
   1250      * @see Category#DISPLAY
   1251      * @stable ICU 4.4
   1252      */
   1253     public String getDisplayLanguageWithDialect() {
   1254         return getDisplayLanguageInternal(this, getDefault(Category.DISPLAY), true);
   1255     }
   1256 
   1257     /**
   1258      * {@icu} Returns this locale's language localized for display in the provided locale.
   1259      * If a dialect name is present in the data, then it is returned.
   1260      * @param displayLocale the locale in which to display the name.
   1261      * @return the localized language name.
   1262      * @stable ICU 4.4
   1263      */
   1264     public String getDisplayLanguageWithDialect(ULocale displayLocale) {
   1265         return getDisplayLanguageInternal(this, displayLocale, true);
   1266     }
   1267 
   1268     /**
   1269      * {@icu} Returns a locale's language localized for display in the provided locale.
   1270      * If a dialect name is present in the data, then it is returned.
   1271      * This is a cover for the ICU4C API.
   1272      * @param localeID the id of the locale whose language will be displayed
   1273      * @param displayLocaleID the id of the locale in which to display the name.
   1274      * @return the localized language name.
   1275      * @stable ICU 4.4
   1276      */
   1277     public static String getDisplayLanguageWithDialect(String localeID, String displayLocaleID) {
   1278         return getDisplayLanguageInternal(new ULocale(localeID), new ULocale(displayLocaleID),
   1279                 true);
   1280     }
   1281 
   1282     /**
   1283      * {@icu} Returns a locale's language localized for display in the provided locale.
   1284      * If a dialect name is present in the data, then it is returned.
   1285      * This is a cover for the ICU4C API.
   1286      * @param localeID the id of the locale whose language will be displayed.
   1287      * @param displayLocale the locale in which to display the name.
   1288      * @return the localized language name.
   1289      * @stable ICU 4.4
   1290      */
   1291     public static String getDisplayLanguageWithDialect(String localeID, ULocale displayLocale) {
   1292         return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, true);
   1293     }
   1294 
   1295     private static String getDisplayLanguageInternal(ULocale locale, ULocale displayLocale,
   1296             boolean useDialect) {
   1297         //#com.ibm.icu.base
   1298         // No dialect support
   1299         return locale.toLocale().getDisplayLanguage(displayLocale.toLocale());
   1300     }
   1301 
   1302     /**
   1303      * {@icu} Returns this locale's script localized for display in the default <code>DISPLAY</code> locale.
   1304      * @return the localized script name.
   1305      * @see Category#DISPLAY
   1306      * @stable ICU 3.0
   1307      */
   1308     public String getDisplayScript() {
   1309         return getDisplayScriptInternal(this, getDefault(Category.DISPLAY));
   1310     }
   1311 
   1312     /**
   1313      * {@icu} Returns this locale's script localized for display in the provided locale.
   1314      * @param displayLocale the locale in which to display the name.
   1315      * @return the localized script name.
   1316      * @stable ICU 3.0
   1317      */
   1318     public String getDisplayScript(ULocale displayLocale) {
   1319         return getDisplayScriptInternal(this, displayLocale);
   1320     }
   1321 
   1322     /**
   1323      * {@icu} Returns a locale's script localized for display in the provided locale.
   1324      * This is a cover for the ICU4C API.
   1325      * @param localeID the id of the locale whose script will be displayed
   1326      * @param displayLocaleID the id of the locale in which to display the name.
   1327      * @return the localized script name.
   1328      * @stable ICU 3.0
   1329      */
   1330     public static String getDisplayScript(String localeID, String displayLocaleID) {
   1331         return getDisplayScriptInternal(new ULocale(localeID), new ULocale(displayLocaleID));
   1332     }
   1333 
   1334     /**
   1335      * {@icu} Returns a locale's script localized for display in the provided locale.
   1336      * @param localeID the id of the locale whose script will be displayed.
   1337      * @param displayLocale the locale in which to display the name.
   1338      * @return the localized script name.
   1339      * @stable ICU 3.0
   1340      */
   1341     public static String getDisplayScript(String localeID, ULocale displayLocale) {
   1342         return getDisplayScriptInternal(new ULocale(localeID), displayLocale);
   1343     }
   1344 
   1345     // displayLocaleID is canonical, localeID need not be since parsing will fix this.
   1346     private static String getDisplayScriptInternal(ULocale locale, ULocale displayLocale) {
   1347         //#com.ibm.icu.base
   1348         String dispScript = null;
   1349         try {
   1350             // Calling Locale#getDisplayScript on Java 7 or later
   1351             Method mGetDisplayScript = Locale.class.getMethod("getDisplayScript", Locale.class);
   1352             dispScript = (String) mGetDisplayScript.invoke(locale.toLocale(), displayLocale.toLocale());
   1353 
   1354         } catch (NoSuchMethodException e) {
   1355         } catch (InvocationTargetException e) {
   1356         } catch (IllegalAccessException e) {
   1357         }
   1358 
   1359         if (dispScript == null) {
   1360             dispScript = locale.getScript();
   1361         }
   1362         return dispScript;
   1363     }
   1364 
   1365     /**
   1366      * Returns this locale's country localized for display in the default <code>DISPLAY</code> locale.
   1367      * @return the localized country name.
   1368      * @see Category#DISPLAY
   1369      * @stable ICU 3.0
   1370      */
   1371     public String getDisplayCountry() {
   1372         return getDisplayCountryInternal(this, getDefault(Category.DISPLAY));
   1373     }
   1374 
   1375     /**
   1376      * Returns this locale's country localized for display in the provided locale.
   1377      * @param displayLocale the locale in which to display the name.
   1378      * @return the localized country name.
   1379      * @stable ICU 3.0
   1380      */
   1381     public String getDisplayCountry(ULocale displayLocale){
   1382         return getDisplayCountryInternal(this, displayLocale);
   1383     }
   1384 
   1385     /**
   1386      * Returns a locale's country localized for display in the provided locale.
   1387      * This is a cover for the ICU4C API.
   1388      * @param localeID the id of the locale whose country will be displayed
   1389      * @param displayLocaleID the id of the locale in which to display the name.
   1390      * @return the localized country name.
   1391      * @stable ICU 3.0
   1392      */
   1393     public static String getDisplayCountry(String localeID, String displayLocaleID) {
   1394         return getDisplayCountryInternal(new ULocale(localeID), new ULocale(displayLocaleID));
   1395     }
   1396 
   1397     /**
   1398      * Returns a locale's country localized for display in the provided locale.
   1399      * This is a cover for the ICU4C API.
   1400      * @param localeID the id of the locale whose country will be displayed.
   1401      * @param displayLocale the locale in which to display the name.
   1402      * @return the localized country name.
   1403      * @stable ICU 3.0
   1404      */
   1405     public static String getDisplayCountry(String localeID, ULocale displayLocale) {
   1406         return getDisplayCountryInternal(new ULocale(localeID), displayLocale);
   1407     }
   1408 
   1409     // displayLocaleID is canonical, localeID need not be since parsing will fix this.
   1410     private static String getDisplayCountryInternal(ULocale locale, ULocale displayLocale) {
   1411         //#com.ibm.icu.base
   1412         return locale.toLocale().getDisplayCountry(displayLocale.toLocale());
   1413     }
   1414 
   1415     /**
   1416      * Returns this locale's variant localized for display in the default <code>DISPLAY</code> locale.
   1417      * @return the localized variant name.
   1418      * @see Category#DISPLAY
   1419      * @stable ICU 3.0
   1420      */
   1421     public String getDisplayVariant() {
   1422         return getDisplayVariantInternal(this, getDefault(Category.DISPLAY));
   1423     }
   1424 
   1425     /**
   1426      * Returns this locale's variant localized for display in the provided locale.
   1427      * @param displayLocale the locale in which to display the name.
   1428      * @return the localized variant name.
   1429      * @stable ICU 3.0
   1430      */
   1431     public String getDisplayVariant(ULocale displayLocale) {
   1432         return getDisplayVariantInternal(this, displayLocale);
   1433     }
   1434 
   1435     /**
   1436      * Returns a locale's variant localized for display in the provided locale.
   1437      * This is a cover for the ICU4C API.
   1438      * @param localeID the id of the locale whose variant will be displayed
   1439      * @param displayLocaleID the id of the locale in which to display the name.
   1440      * @return the localized variant name.
   1441      * @stable ICU 3.0
   1442      */
   1443     public static String getDisplayVariant(String localeID, String displayLocaleID){
   1444         return getDisplayVariantInternal(new ULocale(localeID), new ULocale(displayLocaleID));
   1445     }
   1446 
   1447     /**
   1448      * Returns a locale's variant localized for display in the provided locale.
   1449      * This is a cover for the ICU4C API.
   1450      * @param localeID the id of the locale whose variant will be displayed.
   1451      * @param displayLocale the locale in which to display the name.
   1452      * @return the localized variant name.
   1453      * @stable ICU 3.0
   1454      */
   1455     public static String getDisplayVariant(String localeID, ULocale displayLocale) {
   1456         return getDisplayVariantInternal(new ULocale(localeID), displayLocale);
   1457     }
   1458 
   1459     private static String getDisplayVariantInternal(ULocale locale, ULocale displayLocale) {
   1460         //#com.ibm.icu.base
   1461         return locale.toLocale().getDisplayVariant(displayLocale.toLocale());
   1462     }
   1463 
   1464     /**
   1465      * {@icu} Returns a keyword localized for display in the default <code>DISPLAY</code> locale.
   1466      * @param keyword the keyword to be displayed.
   1467      * @return the localized keyword name.
   1468      * @see #getKeywords()
   1469      * @see Category#DISPLAY
   1470      * @stable ICU 3.0
   1471      */
   1472     public static String getDisplayKeyword(String keyword) {
   1473         return getDisplayKeywordInternal(keyword, getDefault(Category.DISPLAY));
   1474     }
   1475 
   1476     /**
   1477      * {@icu} Returns a keyword localized for display in the specified locale.
   1478      * @param keyword the keyword to be displayed.
   1479      * @param displayLocaleID the id of the locale in which to display the keyword.
   1480      * @return the localized keyword name.
   1481      * @see #getKeywords(String)
   1482      * @stable ICU 3.0
   1483      */
   1484     public static String getDisplayKeyword(String keyword, String displayLocaleID) {
   1485         return getDisplayKeywordInternal(keyword, new ULocale(displayLocaleID));
   1486     }
   1487 
   1488     /**
   1489      * {@icu} Returns a keyword localized for display in the specified locale.
   1490      * @param keyword the keyword to be displayed.
   1491      * @param displayLocale the locale in which to display the keyword.
   1492      * @return the localized keyword name.
   1493      * @see #getKeywords(String)
   1494      * @stable ICU 3.0
   1495      */
   1496     public static String getDisplayKeyword(String keyword, ULocale displayLocale) {
   1497         return getDisplayKeywordInternal(keyword, displayLocale);
   1498     }
   1499 
   1500     private static String getDisplayKeywordInternal(String keyword, ULocale displayLocale) {
   1501         //#com.ibm.icu.base
   1502         // No localization
   1503         return keyword;
   1504     }
   1505 
   1506     /**
   1507      * {@icu} Returns a keyword value localized for display in the default <code>DISPLAY</code> locale.
   1508      * @param keyword the keyword whose value is to be displayed.
   1509      * @return the localized value name.
   1510      * @see Category#DISPLAY
   1511      * @stable ICU 3.0
   1512      */
   1513     public String getDisplayKeywordValue(String keyword) {
   1514         return getDisplayKeywordValueInternal(this, keyword, getDefault(Category.DISPLAY));
   1515     }
   1516 
   1517     /**
   1518      * {@icu} Returns a keyword value localized for display in the specified locale.
   1519      * @param keyword the keyword whose value is to be displayed.
   1520      * @param displayLocale the locale in which to display the value.
   1521      * @return the localized value name.
   1522      * @stable ICU 3.0
   1523      */
   1524     public String getDisplayKeywordValue(String keyword, ULocale displayLocale) {
   1525         return getDisplayKeywordValueInternal(this, keyword, displayLocale);
   1526     }
   1527 
   1528     /**
   1529      * {@icu} Returns a keyword value localized for display in the specified locale.
   1530      * This is a cover for the ICU4C API.
   1531      * @param localeID the id of the locale whose keyword value is to be displayed.
   1532      * @param keyword the keyword whose value is to be displayed.
   1533      * @param displayLocaleID the id of the locale in which to display the value.
   1534      * @return the localized value name.
   1535      * @stable ICU 3.0
   1536      */
   1537     public static String getDisplayKeywordValue(String localeID, String keyword,
   1538             String displayLocaleID) {
   1539         return getDisplayKeywordValueInternal(new ULocale(localeID), keyword,
   1540                 new ULocale(displayLocaleID));
   1541     }
   1542 
   1543     /**
   1544      * {@icu} Returns a keyword value localized for display in the specified locale.
   1545      * This is a cover for the ICU4C API.
   1546      * @param localeID the id of the locale whose keyword value is to be displayed.
   1547      * @param keyword the keyword whose value is to be displayed.
   1548      * @param displayLocale the id of the locale in which to display the value.
   1549      * @return the localized value name.
   1550      * @stable ICU 3.0
   1551      */
   1552     public static String getDisplayKeywordValue(String localeID, String keyword,
   1553             ULocale displayLocale) {
   1554         return getDisplayKeywordValueInternal(new ULocale(localeID), keyword, displayLocale);
   1555     }
   1556 
   1557     // displayLocaleID is canonical, localeID need not be since parsing will fix this.
   1558     private static String getDisplayKeywordValueInternal(ULocale locale, String keyword,
   1559             ULocale displayLocale) {
   1560         //#com.ibm.icu.base
   1561         keyword = AsciiUtil.toLowerString(keyword.trim());
   1562         String value = locale.getKeywordValue(keyword);
   1563         // No localization
   1564         return value;
   1565     }
   1566 
   1567     /**
   1568      * Returns this locale name localized for display in the default <code>DISPLAY</code> locale.
   1569      * @return the localized locale name.
   1570      * @see Category#DISPLAY
   1571      * @stable ICU 3.0
   1572      */
   1573     public String getDisplayName() {
   1574         return getDisplayNameInternal(this, getDefault(Category.DISPLAY));
   1575     }
   1576 
   1577     /**
   1578      * Returns this locale name localized for display in the provided locale.
   1579      * @param displayLocale the locale in which to display the locale name.
   1580      * @return the localized locale name.
   1581      * @stable ICU 3.0
   1582      */
   1583     public String getDisplayName(ULocale displayLocale) {
   1584         return getDisplayNameInternal(this, displayLocale);
   1585     }
   1586 
   1587     /**
   1588      * Returns the locale ID localized for display in the provided locale.
   1589      * This is a cover for the ICU4C API.
   1590      * @param localeID the locale whose name is to be displayed.
   1591      * @param displayLocaleID the id of the locale in which to display the locale name.
   1592      * @return the localized locale name.
   1593      * @stable ICU 3.0
   1594      */
   1595     public static String getDisplayName(String localeID, String displayLocaleID) {
   1596         return getDisplayNameInternal(new ULocale(localeID), new ULocale(displayLocaleID));
   1597     }
   1598 
   1599     /**
   1600      * Returns the locale ID localized for display in the provided locale.
   1601      * This is a cover for the ICU4C API.
   1602      * @param localeID the locale whose name is to be displayed.
   1603      * @param displayLocale the locale in which to display the locale name.
   1604      * @return the localized locale name.
   1605      * @stable ICU 3.0
   1606      */
   1607     public static String getDisplayName(String localeID, ULocale displayLocale) {
   1608         return getDisplayNameInternal(new ULocale(localeID), displayLocale);
   1609     }
   1610 
   1611     private static String getDisplayNameInternal(ULocale locale, ULocale displayLocale) {
   1612         //#com.ibm.icu.base
   1613         return locale.toLocale().getDisplayName(displayLocale.toLocale());
   1614     }
   1615 
   1616     /**
   1617      * {@icu} Returns this locale name localized for display in the default <code>DISPLAY</code> locale.
   1618      * If a dialect name is present in the locale data, then it is returned.
   1619      * @return the localized locale name.
   1620      * @see Category#DISPLAY
   1621      * @stable ICU 4.4
   1622      */
   1623     public String getDisplayNameWithDialect() {
   1624         return getDisplayNameWithDialectInternal(this, getDefault(Category.DISPLAY));
   1625     }
   1626 
   1627     /**
   1628      * {@icu} Returns this locale name localized for display in the provided locale.
   1629      * If a dialect name is present in the locale data, then it is returned.
   1630      * @param displayLocale the locale in which to display the locale name.
   1631      * @return the localized locale name.
   1632      * @stable ICU 4.4
   1633      */
   1634     public String getDisplayNameWithDialect(ULocale displayLocale) {
   1635         return getDisplayNameWithDialectInternal(this, displayLocale);
   1636     }
   1637 
   1638     /**
   1639      * {@icu} Returns the locale ID localized for display in the provided locale.
   1640      * If a dialect name is present in the locale data, then it is returned.
   1641      * This is a cover for the ICU4C API.
   1642      * @param localeID the locale whose name is to be displayed.
   1643      * @param displayLocaleID the id of the locale in which to display the locale name.
   1644      * @return the localized locale name.
   1645      * @stable ICU 4.4
   1646      */
   1647     public static String getDisplayNameWithDialect(String localeID, String displayLocaleID) {
   1648         return getDisplayNameWithDialectInternal(new ULocale(localeID),
   1649                 new ULocale(displayLocaleID));
   1650     }
   1651 
   1652     /**
   1653      * {@icu} Returns the locale ID localized for display in the provided locale.
   1654      * If a dialect name is present in the locale data, then it is returned.
   1655      * This is a cover for the ICU4C API.
   1656      * @param localeID the locale whose name is to be displayed.
   1657      * @param displayLocale the locale in which to display the locale name.
   1658      * @return the localized locale name.
   1659      * @stable ICU 4.4
   1660      */
   1661     public static String getDisplayNameWithDialect(String localeID, ULocale displayLocale) {
   1662         return getDisplayNameWithDialectInternal(new ULocale(localeID), displayLocale);
   1663     }
   1664 
   1665     private static String getDisplayNameWithDialectInternal(ULocale locale, ULocale displayLocale) {
   1666         //#com.ibm.icu.base
   1667         // No dialect handling
   1668         return locale.toLocale().getDisplayName(displayLocale.toLocale());
   1669     }
   1670 
   1671     /**
   1672      * {@icu} Returns this locale's layout orientation for characters.  The possible
   1673      * values are "left-to-right", "right-to-left", "top-to-bottom" or
   1674      * "bottom-to-top".
   1675      * @return The locale's layout orientation for characters.
   1676      * @stable ICU 4.0
   1677      */
   1678     public String getCharacterOrientation() {
   1679         //#com.ibm.icu.base
   1680         // Hardcoded
   1681         String lang = getLanguage();
   1682         if (lang.equals("ar") || lang.equals("fa") || lang.equals("he") || lang.equals("ps") || lang.equals("ur")) {
   1683             return "right-to-left";
   1684         }
   1685         String script = getScript();
   1686         if (script.equals("Arab")) {
   1687             return "right-to-left";
   1688         }
   1689         return "left-to-right";
   1690     }
   1691 
   1692     /**
   1693      * {@icu} Returns this locale's layout orientation for lines.  The possible
   1694      * values are "left-to-right", "right-to-left", "top-to-bottom" or
   1695      * "bottom-to-top".
   1696      * @return The locale's layout orientation for lines.
   1697      * @stable ICU 4.0
   1698      */
   1699     public String getLineOrientation() {
   1700         //#com.ibm.icu.base
   1701         return "top-to-bottom";
   1702     }
   1703 
   1704     /**
   1705      * {@icu} Selector for <tt>getLocale()</tt> indicating the locale of the
   1706      * resource containing the data.  This is always at or above the
   1707      * valid locale.  If the valid locale does not contain the
   1708      * specific data being requested, then the actual locale will be
   1709      * above the valid locale.  If the object was not constructed from
   1710      * locale data, then the valid locale is <i>null</i>.
   1711      *
   1712      * @draft ICU 2.8 (retain)
   1713      * @provisional This API might change or be removed in a future release.
   1714      */
   1715     public static Type ACTUAL_LOCALE = new Type();
   1716 
   1717     /**
   1718      * {@icu} Selector for <tt>getLocale()</tt> indicating the most specific
   1719      * locale for which any data exists.  This is always at or above
   1720      * the requested locale, and at or below the actual locale.  If
   1721      * the requested locale does not correspond to any resource data,
   1722      * then the valid locale will be above the requested locale.  If
   1723      * the object was not constructed from locale data, then the
   1724      * actual locale is <i>null</i>.
   1725      *
   1726      * <p>Note: The valid locale will be returned correctly in ICU
   1727      * 3.0 or later.  In ICU 2.8, it is not returned correctly.
   1728      * @draft ICU 2.8 (retain)
   1729      * @provisional This API might change or be removed in a future release.
   1730      */
   1731     public static Type VALID_LOCALE = new Type();
   1732 
   1733     /**
   1734      * Opaque selector enum for <tt>getLocale()</tt>.
   1735      * @see com.ibm.icu.util.ULocale
   1736      * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
   1737      * @see com.ibm.icu.util.ULocale#VALID_LOCALE
   1738      * @draft ICU 2.8 (retainAll)
   1739      * @provisional This API might change or be removed in a future release.
   1740      */
   1741     public static final class Type {
   1742         private Type() {}
   1743     }
   1744 
   1745   /**
   1746     * {@icu} Based on a HTTP formatted list of acceptable locales, determine an available
   1747     * locale for the user.  NullPointerException is thrown if acceptLanguageList or
   1748     * availableLocales is null.  If fallback is non-null, it will contain true if a
   1749     * fallback locale (one not in the acceptLanguageList) was returned.  The value on
   1750     * entry is ignored.  ULocale will be one of the locales in availableLocales, or the
   1751     * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
   1752     * availableLocales matched).  No ULocale array element should be null; behavior is
   1753     * undefined if this is the case.
   1754     * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
   1755     * @param availableLocales list of available locales. One of these will be returned.
   1756     * @param fallback if non-null, a 1-element array containing a boolean to be set with
   1757     * the fallback status
   1758     * @return one of the locales from the availableLocales list, or null if none match
   1759     * @stable ICU 3.4
   1760     */
   1761     public static ULocale acceptLanguage(String acceptLanguageList, ULocale[] availableLocales,
   1762                                          boolean[] fallback) {
   1763         if (acceptLanguageList == null) {
   1764             throw new NullPointerException();
   1765         }
   1766         ULocale acceptList[] = null;
   1767         try {
   1768             acceptList = parseAcceptLanguage(acceptLanguageList, true);
   1769         } catch (ParseException pe) {
   1770             acceptList = null;
   1771         }
   1772         if (acceptList == null) {
   1773             return null;
   1774         }
   1775         return acceptLanguage(acceptList, availableLocales, fallback);
   1776     }
   1777 
   1778     /**
   1779     * {@icu} Based on a list of acceptable locales, determine an available locale for the
   1780     * user.  NullPointerException is thrown if acceptLanguageList or availableLocales is
   1781     * null.  If fallback is non-null, it will contain true if a fallback locale (one not
   1782     * in the acceptLanguageList) was returned.  The value on entry is ignored.  ULocale
   1783     * will be one of the locales in availableLocales, or the ROOT ULocale if if a ROOT
   1784     * locale was used as a fallback (because nothing else in availableLocales matched).
   1785     * No ULocale array element should be null; behavior is undefined if this is the case.
   1786     * @param acceptLanguageList list of acceptable locales
   1787     * @param availableLocales list of available locales. One of these will be returned.
   1788     * @param fallback if non-null, a 1-element array containing a boolean to be set with
   1789     * the fallback status
   1790     * @return one of the locales from the availableLocales list, or null if none match
   1791     * @stable ICU 3.4
   1792     */
   1793 
   1794     public static ULocale acceptLanguage(ULocale[] acceptLanguageList, ULocale[]
   1795     availableLocales, boolean[] fallback) {
   1796         // fallbacklist
   1797         int i,j;
   1798         if(fallback != null) {
   1799             fallback[0]=true;
   1800         }
   1801         for(i=0;i<acceptLanguageList.length;i++) {
   1802             ULocale aLocale = acceptLanguageList[i];
   1803             boolean[] setFallback = fallback;
   1804             do {
   1805                 for(j=0;j<availableLocales.length;j++) {
   1806                     if(availableLocales[j].equals(aLocale)) {
   1807                         if(setFallback != null) {
   1808                             setFallback[0]=false; // first time with this locale - not a fallback.
   1809                         }
   1810                         return availableLocales[j];
   1811                     }
   1812                     // compare to scriptless alias, so locales such as
   1813                     // zh_TW, zh_CN are considered as available locales - see #7190
   1814                     if (aLocale.getScript().length() == 0
   1815                             && availableLocales[j].getScript().length() > 0
   1816                             && availableLocales[j].getLanguage().equals(aLocale.getLanguage())
   1817                             && availableLocales[j].getCountry().equals(aLocale.getCountry())
   1818                             && availableLocales[j].getVariant().equals(aLocale.getVariant())) {
   1819                         ULocale minAvail = ULocale.minimizeSubtags(availableLocales[j]);
   1820                         if (minAvail.getScript().length() == 0) {
   1821                             if(setFallback != null) {
   1822                                 setFallback[0] = false; // not a fallback.
   1823                             }
   1824                             return aLocale;
   1825                         }
   1826                     }
   1827                 }
   1828                 Locale loc = aLocale.toLocale();
   1829                 Locale parent = LocaleUtility.fallback(loc);
   1830                 if(parent != null) {
   1831                     aLocale = new ULocale(parent);
   1832                 } else {
   1833                     aLocale = null;
   1834                 }
   1835                 setFallback = null; // Do not set fallback in later iterations
   1836             } while (aLocale != null);
   1837         }
   1838         return null;
   1839     }
   1840 
   1841    /**
   1842     * {@icu} Based on a HTTP formatted list of acceptable locales, determine an available
   1843     * locale for the user.  NullPointerException is thrown if acceptLanguageList or
   1844     * availableLocales is null.  If fallback is non-null, it will contain true if a
   1845     * fallback locale (one not in the acceptLanguageList) was returned.  The value on
   1846     * entry is ignored.  ULocale will be one of the locales in availableLocales, or the
   1847     * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
   1848     * availableLocales matched).  No ULocale array element should be null; behavior is
   1849     * undefined if this is the case.  This function will choose a locale from the
   1850     * ULocale.getAvailableLocales() list as available.
   1851     * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
   1852     * @param fallback if non-null, a 1-element array containing a boolean to be set with
   1853     * the fallback status
   1854     * @return one of the locales from the ULocale.getAvailableLocales() list, or null if
   1855     * none match
   1856     * @stable ICU 3.4
   1857     */
   1858     public static ULocale acceptLanguage(String acceptLanguageList, boolean[] fallback) {
   1859         return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
   1860                                 fallback);
   1861     }
   1862 
   1863    /**
   1864     * {@icu} Based on an ordered array of acceptable locales, determine an available
   1865     * locale for the user.  NullPointerException is thrown if acceptLanguageList or
   1866     * availableLocales is null.  If fallback is non-null, it will contain true if a
   1867     * fallback locale (one not in the acceptLanguageList) was returned.  The value on
   1868     * entry is ignored.  ULocale will be one of the locales in availableLocales, or the
   1869     * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
   1870     * availableLocales matched).  No ULocale array element should be null; behavior is
   1871     * undefined if this is the case.  This function will choose a locale from the
   1872     * ULocale.getAvailableLocales() list as available.
   1873     * @param acceptLanguageList ordered array of acceptable locales (preferred are listed first)
   1874     * @param fallback if non-null, a 1-element array containing a boolean to be set with
   1875     * the fallback status
   1876     * @return one of the locales from the ULocale.getAvailableLocales() list, or null if none match
   1877     * @stable ICU 3.4
   1878     */
   1879     public static ULocale acceptLanguage(ULocale[] acceptLanguageList, boolean[] fallback) {
   1880         return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
   1881                 fallback);
   1882     }
   1883 
   1884     /**
   1885      * Package local method used for parsing Accept-Language string
   1886      */
   1887     static ULocale[] parseAcceptLanguage(String acceptLanguage, boolean isLenient)
   1888         throws ParseException {
   1889         class ULocaleAcceptLanguageQ implements Comparable<ULocaleAcceptLanguageQ> {
   1890             private double q;
   1891             private double serial;
   1892             public ULocaleAcceptLanguageQ(double theq, int theserial) {
   1893                 q = theq;
   1894                 serial = theserial;
   1895             }
   1896             public int compareTo(ULocaleAcceptLanguageQ other) {
   1897                 if (q > other.q) { // reverse - to sort in descending order
   1898                     return -1;
   1899                 } else if (q < other.q) {
   1900                     return 1;
   1901                 }
   1902                 if (serial < other.serial) {
   1903                     return -1;
   1904                 } else if (serial > other.serial) {
   1905                     return 1;
   1906                 } else {
   1907                     return 0; // same object
   1908                 }
   1909             }
   1910         }
   1911 
   1912         // parse out the acceptLanguage into an array
   1913         TreeMap<ULocaleAcceptLanguageQ, ULocale> map =
   1914             new TreeMap<ULocaleAcceptLanguageQ, ULocale>();
   1915         StringBuilder languageRangeBuf = new StringBuilder();
   1916         StringBuilder qvalBuf = new StringBuilder();
   1917         int state = 0;
   1918         acceptLanguage += ","; // append comma to simplify the parsing code
   1919         int n;
   1920         boolean subTag = false;
   1921         boolean q1 = false;
   1922         for (n = 0; n < acceptLanguage.length(); n++) {
   1923             boolean gotLanguageQ = false;
   1924             char c = acceptLanguage.charAt(n);
   1925             switch (state) {
   1926             case 0: // before language-range start
   1927                 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
   1928                     // in language-range
   1929                     languageRangeBuf.append(c);
   1930                     state = 1;
   1931                     subTag = false;
   1932                 } else if (c == '*') {
   1933                     languageRangeBuf.append(c);
   1934                     state = 2;
   1935                 } else if (c != ' ' && c != '\t') {
   1936                     // invalid character
   1937                     state = -1;
   1938                 }
   1939                 break;
   1940             case 1: // in language-range
   1941                 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
   1942                     languageRangeBuf.append(c);
   1943                 } else if (c == '-') {
   1944                     subTag = true;
   1945                     languageRangeBuf.append(c);
   1946                 } else if (c == '_') {
   1947                     if (isLenient) {
   1948                         subTag = true;
   1949                         languageRangeBuf.append(c);
   1950                     } else {
   1951                         state = -1;
   1952                     }
   1953                 } else if ('0' <= c && c <= '9') {
   1954                     if (subTag) {
   1955                         languageRangeBuf.append(c);
   1956                     } else {
   1957                         // DIGIT is allowed only in language sub tag
   1958                         state = -1;
   1959                     }
   1960                 } else if (c == ',') {
   1961                     // language-q end
   1962                     gotLanguageQ = true;
   1963                 } else if (c == ' ' || c == '\t') {
   1964                     // language-range end
   1965                     state = 3;
   1966                 } else if (c == ';') {
   1967                     // before q
   1968                     state = 4;
   1969                 } else {
   1970                     // invalid character for language-range
   1971                     state = -1;
   1972                 }
   1973                 break;
   1974             case 2: // saw wild card range
   1975                 if (c == ',') {
   1976                     // language-q end
   1977                     gotLanguageQ = true;
   1978                 } else if (c == ' ' || c == '\t') {
   1979                     // language-range end
   1980                     state = 3;
   1981                 } else if (c == ';') {
   1982                     // before q
   1983                     state = 4;
   1984                 } else {
   1985                     // invalid
   1986                     state = -1;
   1987                 }
   1988                 break;
   1989             case 3: // language-range end
   1990                 if (c == ',') {
   1991                     // language-q end
   1992                     gotLanguageQ = true;
   1993                 } else if (c == ';') {
   1994                     // before q
   1995                     state =4;
   1996                 } else if (c != ' ' && c != '\t') {
   1997                     // invalid
   1998                     state = -1;
   1999                 }
   2000                 break;
   2001             case 4: // before q
   2002                 if (c == 'q') {
   2003                     // before equal
   2004                     state = 5;
   2005                 } else if (c != ' ' && c != '\t') {
   2006                     // invalid
   2007                     state = -1;
   2008                 }
   2009                 break;
   2010             case 5: // before equal
   2011                 if (c == '=') {
   2012                     // before q value
   2013                     state = 6;
   2014                 } else if (c != ' ' && c != '\t') {
   2015                     // invalid
   2016                     state = -1;
   2017                 }
   2018                 break;
   2019             case 6: // before q value
   2020                 if (c == '0') {
   2021                     // q value start with 0
   2022                     q1 = false;
   2023                     qvalBuf.append(c);
   2024                     state = 7;
   2025                 } else if (c == '1') {
   2026                     // q value start with 1
   2027                     qvalBuf.append(c);
   2028                     state = 7;
   2029                 } else if (c == '.') {
   2030                     if (isLenient) {
   2031                         qvalBuf.append(c);
   2032                         state = 8;
   2033                     } else {
   2034                         state = -1;
   2035                     }
   2036                 } else if (c != ' ' && c != '\t') {
   2037                     // invalid
   2038                     state = -1;
   2039                 }
   2040                 break;
   2041             case 7: // q value start
   2042                 if (c == '.') {
   2043                     // before q value fraction part
   2044                     qvalBuf.append(c);
   2045                     state = 8;
   2046                 } else if (c == ',') {
   2047                     // language-q end
   2048                     gotLanguageQ = true;
   2049                 } else if (c == ' ' || c == '\t') {
   2050                     // after q value
   2051                     state = 10;
   2052                 } else {
   2053                     // invalid
   2054                     state = -1;
   2055                 }
   2056                 break;
   2057             case 8: // before q value fraction part
   2058                 if ('0' <= c || c <= '9') {
   2059                     if (q1 && c != '0' && !isLenient) {
   2060                         // if q value starts with 1, the fraction part must be 0
   2061                         state = -1;
   2062                     } else {
   2063                         // in q value fraction part
   2064                         qvalBuf.append(c);
   2065                         state = 9;
   2066                     }
   2067                 } else {
   2068                     // invalid
   2069                     state = -1;
   2070                 }
   2071                 break;
   2072             case 9: // in q value fraction part
   2073                 if ('0' <= c && c <= '9') {
   2074                     if (q1 && c != '0') {
   2075                         // if q value starts with 1, the fraction part must be 0
   2076                         state = -1;
   2077                     } else {
   2078                         qvalBuf.append(c);
   2079                     }
   2080                 } else if (c == ',') {
   2081                     // language-q end
   2082                     gotLanguageQ = true;
   2083                 } else if (c == ' ' || c == '\t') {
   2084                     // after q value
   2085                     state = 10;
   2086                 } else {
   2087                     // invalid
   2088                     state = -1;
   2089                 }
   2090                 break;
   2091             case 10: // after q value
   2092                 if (c == ',') {
   2093                     // language-q end
   2094                     gotLanguageQ = true;
   2095                 } else if (c != ' ' && c != '\t') {
   2096                     // invalid
   2097                     state = -1;
   2098                 }
   2099                 break;
   2100             }
   2101             if (state == -1) {
   2102                 // error state
   2103                 throw new ParseException("Invalid Accept-Language", n);
   2104             }
   2105             if (gotLanguageQ) {
   2106                 double q = 1.0;
   2107                 if (qvalBuf.length() != 0) {
   2108                     try {
   2109                         q = Double.parseDouble(qvalBuf.toString());
   2110                     } catch (NumberFormatException nfe) {
   2111                         // Already validated, so it should never happen
   2112                         q = 1.0;
   2113                     }
   2114                     if (q > 1.0) {
   2115                         q = 1.0;
   2116                     }
   2117                 }
   2118                 if (languageRangeBuf.charAt(0) != '*') {
   2119                     int serial = map.size();
   2120                     ULocaleAcceptLanguageQ entry = new ULocaleAcceptLanguageQ(q, serial);
   2121                     // sort in reverse order..   1.0, 0.9, 0.8 .. etc
   2122                     map.put(entry, new ULocale(canonicalize(languageRangeBuf.toString())));
   2123                 }
   2124 
   2125                 // reset buffer and parse state
   2126                 languageRangeBuf.setLength(0);
   2127                 qvalBuf.setLength(0);
   2128                 state = 0;
   2129             }
   2130         }
   2131         if (state != 0) {
   2132             // Well, the parser should handle all cases.  So just in case.
   2133             throw new ParseException("Invalid AcceptlLanguage", n);
   2134         }
   2135 
   2136         // pull out the map
   2137         ULocale acceptList[] = map.values().toArray(new ULocale[map.size()]);
   2138         return acceptList;
   2139     }
   2140 
   2141     private static final String UNDEFINED_LANGUAGE = "und";
   2142 
   2143     /**
   2144      * {@icu} Adds the likely subtags for a provided locale ID, per the algorithm
   2145      * described in the following CLDR technical report:
   2146      *
   2147      *   http://www.unicode.org/reports/tr35/#Likely_Subtags
   2148      *
   2149      * If the provided ULocale instance is already in the maximal form, or there is no
   2150      * data available available for maximization, it will be returned.  For example,
   2151      * "und-Zzzz" cannot be maximized, since there is no reasonable maximization.
   2152      * Otherwise, a new ULocale instance with the maximal form is returned.
   2153      *
   2154      * Examples:
   2155      *
   2156      * "en" maximizes to "en_Latn_US"
   2157      *
   2158      * "de" maximizes to "de_Latn_US"
   2159      *
   2160      * "sr" maximizes to "sr_Cyrl_RS"
   2161      *
   2162      * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.)
   2163      *
   2164      * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.)
   2165      *
   2166      * @param loc The ULocale to maximize
   2167      * @return The maximized ULocale instance.
   2168      * @stable ICU 4.0
   2169      */
   2170     public static ULocale addLikelySubtags(ULocale loc) {
   2171         //#com.ibm.icu.base
   2172         return loc;
   2173     }
   2174 
   2175     /**
   2176      * {@icu} Minimizes the subtags for a provided locale ID, per the algorithm described
   2177      * in the following CLDR technical report:<blockquote>
   2178      *
   2179      *   <a href="http://www.unicode.org/reports/tr35/#Likely_Subtags"
   2180      *>http://www.unicode.org/reports/tr35/#Likely_Subtags</a></blockquote>
   2181      *
   2182      * If the provided ULocale instance is already in the minimal form, or there
   2183      * is no data available for minimization, it will be returned.  Since the
   2184      * minimization algorithm relies on proper maximization, see the comments
   2185      * for addLikelySubtags for reasons why there might not be any data.
   2186      *
   2187      * Examples:<pre>
   2188      *
   2189      * "en_Latn_US" minimizes to "en"
   2190      *
   2191      * "de_Latn_US" minimizes to "de"
   2192      *
   2193      * "sr_Cyrl_RS" minimizes to "sr"
   2194      *
   2195      * "zh_Hant_TW" minimizes to "zh_TW" (The region is preferred to the
   2196      * script, and minimizing to "zh" would imply "zh_Hans_CN".) </pre>
   2197      *
   2198      * @param loc The ULocale to minimize
   2199      * @return The minimized ULocale instance.
   2200      * @stable ICU 4.0
   2201      */
   2202     public static ULocale minimizeSubtags(ULocale loc) {
   2203         //#com.ibm.icu.base
   2204         return loc;
   2205     }
   2206 
   2207     /**
   2208      * A trivial utility function that checks for a null
   2209      * reference or checks the length of the supplied String.
   2210      *
   2211      *   @param string The string to check
   2212      *
   2213      *   @return true if the String is empty, or if the reference is null.
   2214      */
   2215     private static boolean isEmptyString(String string) {
   2216       return string == null || string.length() == 0;
   2217     }
   2218 
   2219     /**
   2220      * Append a tag to a StringBuilder, adding the separator if necessary.The tag must
   2221      * not be a zero-length string.
   2222      *
   2223      * @param tag The tag to add.
   2224      * @param buffer The output buffer.
   2225      **/
   2226     private static void appendTag(String tag, StringBuilder buffer) {
   2227         if (buffer.length() != 0) {
   2228             buffer.append(UNDERSCORE);
   2229         }
   2230 
   2231         buffer.append(tag);
   2232     }
   2233 
   2234     /**
   2235      * Create a tag string from the supplied parameters.  The lang, script and region
   2236      * parameters may be null references.
   2237      *
   2238      * If any of the language, script or region parameters are empty, and the alternateTags
   2239      * parameter is not null, it will be parsed for potential language, script and region tags
   2240      * to be used when constructing the new tag.  If the alternateTags parameter is null, or
   2241      * it contains no language tag, the default tag for the unknown language is used.
   2242      *
   2243      * @param lang The language tag to use.
   2244      * @param script The script tag to use.
   2245      * @param region The region tag to use.
   2246      * @param trailing Any trailing data to append to the new tag.
   2247      * @param alternateTags A string containing any alternate tags.
   2248      * @return The new tag string.
   2249      **/
   2250     private static String createTagString(String lang, String script, String region,
   2251         String trailing, String alternateTags) {
   2252 
   2253         LocaleIDParser parser = null;
   2254         boolean regionAppended = false;
   2255 
   2256         StringBuilder tag = new StringBuilder();
   2257 
   2258         if (!isEmptyString(lang)) {
   2259             appendTag(
   2260                 lang,
   2261                 tag);
   2262         }
   2263         else if (isEmptyString(alternateTags)) {
   2264             /*
   2265              * Append the value for an unknown language, if
   2266              * we found no language.
   2267              */
   2268             appendTag(
   2269                 UNDEFINED_LANGUAGE,
   2270                 tag);
   2271         }
   2272         else {
   2273             parser = new LocaleIDParser(alternateTags);
   2274 
   2275             String alternateLang = parser.getLanguage();
   2276 
   2277             /*
   2278              * Append the value for an unknown language, if
   2279              * we found no language.
   2280              */
   2281             appendTag(
   2282                 !isEmptyString(alternateLang) ? alternateLang : UNDEFINED_LANGUAGE,
   2283                 tag);
   2284         }
   2285 
   2286         if (!isEmptyString(script)) {
   2287             appendTag(
   2288                 script,
   2289                 tag);
   2290         }
   2291         else if (!isEmptyString(alternateTags)) {
   2292             /*
   2293              * Parse the alternateTags string for the script.
   2294              */
   2295             if (parser == null) {
   2296                 parser = new LocaleIDParser(alternateTags);
   2297             }
   2298 
   2299             String alternateScript = parser.getScript();
   2300 
   2301             if (!isEmptyString(alternateScript)) {
   2302                 appendTag(
   2303                     alternateScript,
   2304                     tag);
   2305             }
   2306         }
   2307 
   2308         if (!isEmptyString(region)) {
   2309             appendTag(
   2310                 region,
   2311                 tag);
   2312 
   2313             regionAppended = true;
   2314         }
   2315         else if (!isEmptyString(alternateTags)) {
   2316             /*
   2317              * Parse the alternateTags string for the region.
   2318              */
   2319             if (parser == null) {
   2320                 parser = new LocaleIDParser(alternateTags);
   2321             }
   2322 
   2323             String alternateRegion = parser.getCountry();
   2324 
   2325             if (!isEmptyString(alternateRegion)) {
   2326                 appendTag(
   2327                     alternateRegion,
   2328                     tag);
   2329 
   2330                 regionAppended = true;
   2331             }
   2332         }
   2333 
   2334         if (trailing != null && trailing.length() > 1) {
   2335             /*
   2336              * The current ICU format expects two underscores
   2337              * will separate the variant from the preceeding
   2338              * parts of the tag, if there is no region.
   2339              */
   2340             int separators = 0;
   2341 
   2342             if (trailing.charAt(0) == UNDERSCORE) {
   2343                 if (trailing.charAt(1) == UNDERSCORE) {
   2344                     separators = 2;
   2345                 }
   2346                 }
   2347                 else {
   2348                     separators = 1;
   2349                 }
   2350 
   2351             if (regionAppended) {
   2352                 /*
   2353                  * If we appended a region, we may need to strip
   2354                  * the extra separator from the variant portion.
   2355                  */
   2356                 if (separators == 2) {
   2357                     tag.append(trailing.substring(1));
   2358                 }
   2359                 else {
   2360                     tag.append(trailing);
   2361                 }
   2362             }
   2363             else {
   2364                 /*
   2365                  * If we did not append a region, we may need to add
   2366                  * an extra separator to the variant portion.
   2367                  */
   2368                 if (separators == 1) {
   2369                     tag.append(UNDERSCORE);
   2370                 }
   2371                 tag.append(trailing);
   2372             }
   2373         }
   2374 
   2375         return tag.toString();
   2376     }
   2377 
   2378     /**
   2379      * Create a tag string from the supplied parameters.  The lang, script and region
   2380      * parameters may be null references.If the lang parameter is an empty string, the
   2381      * default value for an unknown language is written to the output buffer.
   2382      *
   2383      * @param lang The language tag to use.
   2384      * @param script The script tag to use.
   2385      * @param region The region tag to use.
   2386      * @param trailing Any trailing data to append to the new tag.
   2387      * @return The new String.
   2388      **/
   2389     static String createTagString(String lang, String script, String region, String trailing) {
   2390         return createTagString(lang, script, region, trailing, null);
   2391     }
   2392 
   2393     // --------------------------------
   2394     //      BCP47/OpenJDK APIs
   2395     // --------------------------------
   2396 
   2397     /**
   2398      * {@icu} The key for the private use locale extension ('x').
   2399      *
   2400      * @see #getExtension(char)
   2401      * @see Builder#setExtension(char, String)
   2402      *
   2403      * @draft ICU 4.2
   2404      * @provisional This API might change or be removed in a future release.
   2405      */
   2406     public static final char PRIVATE_USE_EXTENSION = 'x';
   2407 
   2408     /**
   2409      * {@icu} The key for Unicode locale extension ('u').
   2410      *
   2411      * @see #getExtension(char)
   2412      * @see Builder#setExtension(char, String)
   2413      *
   2414      * @draft ICU 4.2
   2415      * @provisional This API might change or be removed in a future release.
   2416      */
   2417     public static final char UNICODE_LOCALE_EXTENSION = 'u';
   2418 
   2419     /**
   2420      * {@icu} Returns the extension (or private use) value associated with
   2421      * the specified key, or null if there is no extension
   2422      * associated with the key. To be well-formed, the key must be one
   2423      * of <code>[0-9A-Za-z]</code>. Keys are case-insensitive, so
   2424      * for example 'z' and 'Z' represent the same extension.
   2425      *
   2426      * @param key the extension key
   2427      * @return The extension, or null if this locale defines no
   2428      * extension for the specified key.
   2429      * @throws IllegalArgumentException if key is not well-formed
   2430      * @see #PRIVATE_USE_EXTENSION
   2431      * @see #UNICODE_LOCALE_EXTENSION
   2432      *
   2433      * @draft ICU 4.2
   2434      * @provisional This API might change or be removed in a future release.
   2435      */
   2436     public String getExtension(char key) {
   2437         if (!LocaleExtensions.isValidKey(key)) {
   2438             throw new IllegalArgumentException("Invalid extension key: " + key);
   2439         }
   2440         return extensions().getExtensionValue(key);
   2441     }
   2442 
   2443     /**
   2444      * {@icu} Returns the set of extension keys associated with this locale, or the
   2445      * empty set if it has no extensions. The returned set is unmodifiable.
   2446      * The keys will all be lower-case.
   2447      *
   2448      * @return the set of extension keys, or the empty set if this locale has
   2449      * no extensions
   2450      * @draft ICU 4.2
   2451      * @provisional This API might change or be removed in a future release.
   2452      */
   2453     public Set<Character> getExtensionKeys() {
   2454         return extensions().getKeys();
   2455     }
   2456 
   2457     /**
   2458      * {@icu} Returns the set of unicode locale attributes associated with
   2459      * this locale, or the empty set if it has no attributes. The
   2460      * returned set is unmodifiable.
   2461      *
   2462      * @return The set of attributes.
   2463      * @draft ICU 4.6
   2464      * @provisional This API might change or be removed in a future release.
   2465      */
   2466     public Set<String> getUnicodeLocaleAttributes() {
   2467         return extensions().getUnicodeLocaleAttributes();
   2468     }
   2469 
   2470     /**
   2471      * {@icu} Returns the Unicode locale type associated with the specified Unicode locale key
   2472      * for this locale. Returns the empty string for keys that are defined with no type.
   2473      * Returns null if the key is not defined. Keys are case-insensitive. The key must
   2474      * be two alphanumeric characters ([0-9a-zA-Z]), or an IllegalArgumentException is
   2475      * thrown.
   2476      *
   2477      * @param key the Unicode locale key
   2478      * @return The Unicode locale type associated with the key, or null if the
   2479      * locale does not define the key.
   2480      * @throws IllegalArgumentException if the key is not well-formed
   2481      * @throws NullPointerException if <code>key</code> is null
   2482      *
   2483      * @draft ICU 4.4
   2484      * @provisional This API might change or be removed in a future release.
   2485      */
   2486     public String getUnicodeLocaleType(String key) {
   2487         if (!LocaleExtensions.isValidUnicodeLocaleKey(key)) {
   2488             throw new IllegalArgumentException("Invalid Unicode locale key: " + key);
   2489         }
   2490         return extensions().getUnicodeLocaleType(key);
   2491     }
   2492 
   2493     /**
   2494      * {@icu} Returns the set of Unicode locale keys defined by this locale, or the empty set if
   2495      * this locale has none.  The returned set is immutable.  Keys are all lower case.
   2496      *
   2497      * @return The set of Unicode locale keys, or the empty set if this locale has
   2498      * no Unicode locale keywords.
   2499      *
   2500      * @draft ICU 4.4
   2501      * @provisional This API might change or be removed in a future release.
   2502      */
   2503     public Set<String> getUnicodeLocaleKeys() {
   2504         return extensions().getUnicodeLocaleKeys();
   2505     }
   2506 
   2507     /**
   2508      * {@icu} Returns a well-formed IETF BCP 47 language tag representing
   2509      * this locale.
   2510      *
   2511      * <p>If this <code>ULocale</code> has a language, script, country, or
   2512      * variant that does not satisfy the IETF BCP 47 language tag
   2513      * syntax requirements, this method handles these fields as
   2514      * described below:
   2515      *
   2516      * <p><b>Language:</b> If language is empty, or not well-formed
   2517      * (for example "a" or "e2"), it will be emitted as "und" (Undetermined).
   2518      *
   2519      * <p><b>Script:</b> If script is not well-formed (for example "12"
   2520      * or "Latin"), it will be omitted.
   2521      *
   2522      * <p><b>Country:</b> If country is not well-formed (for example "12"
   2523      * or "USA"), it will be omitted.
   2524      *
   2525      * <p><b>Variant:</b> If variant <b>is</b> well-formed, each sub-segment
   2526      * (delimited by '-' or '_') is emitted as a subtag.  Otherwise:
   2527      * <ul>
   2528      *
   2529      * <li>if all sub-segments match <code>[0-9a-zA-Z]{1,8}</code>
   2530      * (for example "WIN" or "Oracle_JDK_Standard_Edition"), the first
   2531      * ill-formed sub-segment and all following will be appended to
   2532      * the private use subtag.  The first appended subtag will be
   2533      * "lvariant", followed by the sub-segments in order, separated by
   2534      * hyphen. For example, "x-lvariant-WIN",
   2535      * "Oracle-x-lvariant-JDK-Standard-Edition".
   2536      *
   2537      * <li>if any sub-segment does not match
   2538      * <code>[0-9a-zA-Z]{1,8}</code>, the variant will be truncated
   2539      * and the problematic sub-segment and all following sub-segments
   2540      * will be omitted.  If the remainder is non-empty, it will be
   2541      * emitted as a private use subtag as above (even if the remainder
   2542      * turns out to be well-formed).  For example,
   2543      * "Solaris_isjustthecoolestthing" is emitted as
   2544      * "x-lvariant-Solaris", not as "solaris".</li></ul>
   2545      *
   2546      * <p><b>Note:</b> Although the language tag created by this
   2547      * method is well-formed (satisfies the syntax requirements
   2548      * defined by the IETF BCP 47 specification), it is not
   2549      * necessarily a valid BCP 47 language tag.  For example,
   2550      * <pre>
   2551      *   new Locale("xx", "YY").toLanguageTag();</pre>
   2552      *
   2553      * will return "xx-YY", but the language subtag "xx" and the
   2554      * region subtag "YY" are invalid because they are not registered
   2555      * in the IANA Language Subtag Registry.
   2556      *
   2557      * @return a BCP47 language tag representing the locale
   2558      * @see #forLanguageTag(String)
   2559      *
   2560      * @draft ICU 4.2
   2561      * @provisional This API might change or be removed in a future release.
   2562      */
   2563     public String toLanguageTag() {
   2564         BaseLocale base = base();
   2565         LocaleExtensions exts = extensions();
   2566 
   2567         if (base.getVariant().equalsIgnoreCase("POSIX")) {
   2568             // special handling for variant POSIX
   2569             base = BaseLocale.getInstance(base.getLanguage(), base.getScript(), base.getRegion(), "");
   2570             if (exts.getUnicodeLocaleType("va") == null) {
   2571                 // add va-posix
   2572                 InternalLocaleBuilder ilocbld = new InternalLocaleBuilder();
   2573                 try {
   2574                     ilocbld.setLocale(BaseLocale.ROOT, exts);
   2575                     ilocbld.setUnicodeLocaleKeyword("va", "posix");
   2576                     exts = ilocbld.getLocaleExtensions();
   2577                 } catch (LocaleSyntaxException e) {
   2578                     // this should not happen
   2579                     throw new RuntimeException(e);
   2580                 }
   2581             }
   2582         }
   2583 
   2584         LanguageTag tag = LanguageTag.parseLocale(base, exts);
   2585 
   2586         StringBuilder buf = new StringBuilder();
   2587         String subtag = tag.getLanguage();
   2588         if (subtag.length() > 0) {
   2589             buf.append(LanguageTag.canonicalizeLanguage(subtag));
   2590         }
   2591 
   2592         subtag = tag.getScript();
   2593         if (subtag.length() > 0) {
   2594             buf.append(LanguageTag.SEP);
   2595             buf.append(LanguageTag.canonicalizeScript(subtag));
   2596         }
   2597 
   2598         subtag = tag.getRegion();
   2599         if (subtag.length() > 0) {
   2600             buf.append(LanguageTag.SEP);
   2601             buf.append(LanguageTag.canonicalizeRegion(subtag));
   2602         }
   2603 
   2604         List<String>subtags = tag.getVariants();
   2605         for (String s : subtags) {
   2606             buf.append(LanguageTag.SEP);
   2607             buf.append(LanguageTag.canonicalizeVariant(s));
   2608         }
   2609 
   2610         subtags = tag.getExtensions();
   2611         for (String s : subtags) {
   2612             buf.append(LanguageTag.SEP);
   2613             buf.append(LanguageTag.canonicalizeExtension(s));
   2614         }
   2615 
   2616         subtag = tag.getPrivateuse();
   2617         if (subtag.length() > 0) {
   2618             if (buf.length() > 0) {
   2619                 buf.append(LanguageTag.SEP);
   2620             }
   2621             buf.append(LanguageTag.PRIVATEUSE).append(LanguageTag.SEP);
   2622             buf.append(LanguageTag.canonicalizePrivateuse(subtag));
   2623         }
   2624 
   2625         return buf.toString();
   2626     }
   2627 
   2628     /**
   2629      * {@icu} Returns a locale for the specified IETF BCP 47 language tag string.
   2630      *
   2631      * <p>If the specified language tag contains any ill-formed subtags,
   2632      * the first such subtag and all following subtags are ignored.  Compare
   2633      * to {@link ULocale.Builder#setLanguageTag} which throws an exception
   2634      * in this case.
   2635      *
   2636      * <p>The following <b>conversions</b> are performed:<ul>
   2637      *
   2638      * <li>The language code "und" is mapped to language "".
   2639      *
   2640      * <li>The portion of a private use subtag prefixed by "lvariant",
   2641      * if any, is removed and appended to the variant field in the
   2642      * result locale (without case normalization).  If it is then
   2643      * empty, the private use subtag is discarded:
   2644      *
   2645      * <pre>
   2646      *     ULocale loc;
   2647      *     loc = ULocale.forLanguageTag("en-US-x-lvariant-icu4j);
   2648      *     loc.getVariant(); // returns "ICU4J"
   2649      *     loc.getExtension('x'); // returns null
   2650      *
   2651      *     loc = Locale.forLanguageTag("de-icu4j-x-URP-lvariant-Abc-Def");
   2652      *     loc.getVariant(); // returns "ICU4J_ABC_DEF"
   2653      *     loc.getExtension('x'); // returns "urp"
   2654      * </pre>
   2655      *
   2656      * <li>When the languageTag argument contains an extlang subtag,
   2657      * the first such subtag is used as the language, and the primary
   2658      * language subtag and other extlang subtags are ignored:
   2659      *
   2660      * <pre>
   2661      *     ULocale.forLanguageTag("ar-aao").getLanguage(); // returns "aao"
   2662      *     ULocale.forLanguageTag("en-abc-def-us").toString(); // returns "abc_US"
   2663      * </pre>
   2664      *
   2665      * <li>Case is normalized. Language is normalized to lower case,
   2666      * script to title case, country to upper case, variant to upper case,
   2667      * and extensions to lower case.
   2668      *
   2669      * <p>This implements the 'Language-Tag' production of BCP47, and
   2670      * so supports grandfathered (regular and irregular) as well as
   2671      * private use language tags.  Stand alone private use tags are
   2672      * represented as empty language and extension 'x-whatever',
   2673      * and grandfathered tags are converted to their canonical replacements
   2674      * where they exist.
   2675      *
   2676      * <p>Grandfathered tags with canonical replacements are as follows:
   2677      *
   2678      * <table>
   2679      * <tbody align="center">
   2680      * <tr><th>grandfathered tag</th><th>&nbsp;</th><th>modern replacement</th></tr>
   2681      * <tr><td>art-lojban</td><td>&nbsp;</td><td>jbo</td></tr>
   2682      * <tr><td>i-ami</td><td>&nbsp;</td><td>ami</td></tr>
   2683      * <tr><td>i-bnn</td><td>&nbsp;</td><td>bnn</td></tr>
   2684      * <tr><td>i-hak</td><td>&nbsp;</td><td>hak</td></tr>
   2685      * <tr><td>i-klingon</td><td>&nbsp;</td><td>tlh</td></tr>
   2686      * <tr><td>i-lux</td><td>&nbsp;</td><td>lb</td></tr>
   2687      * <tr><td>i-navajo</td><td>&nbsp;</td><td>nv</td></tr>
   2688      * <tr><td>i-pwn</td><td>&nbsp;</td><td>pwn</td></tr>
   2689      * <tr><td>i-tao</td><td>&nbsp;</td><td>tao</td></tr>
   2690      * <tr><td>i-tay</td><td>&nbsp;</td><td>tay</td></tr>
   2691      * <tr><td>i-tsu</td><td>&nbsp;</td><td>tsu</td></tr>
   2692      * <tr><td>no-bok</td><td>&nbsp;</td><td>nb</td></tr>
   2693      * <tr><td>no-nyn</td><td>&nbsp;</td><td>nn</td></tr>
   2694      * <tr><td>sgn-BE-FR</td><td>&nbsp;</td><td>sfb</td></tr>
   2695      * <tr><td>sgn-BE-NL</td><td>&nbsp;</td><td>vgt</td></tr>
   2696      * <tr><td>sgn-CH-DE</td><td>&nbsp;</td><td>sgg</td></tr>
   2697      * <tr><td>zh-guoyu</td><td>&nbsp;</td><td>cmn</td></tr>
   2698      * <tr><td>zh-hakka</td><td>&nbsp;</td><td>hak</td></tr>
   2699      * <tr><td>zh-min-nan</td><td>&nbsp;</td><td>nan</td></tr>
   2700      * <tr><td>zh-xiang</td><td>&nbsp;</td><td>hsn</td></tr>
   2701      * </tbody>
   2702      * </table>
   2703      *
   2704      * <p>Grandfathered tags with no modern replacement will be
   2705      * converted as follows:
   2706      *
   2707      * <table>
   2708      * <tbody align="center">
   2709      * <tr><th>grandfathered tag</th><th>&nbsp;</th><th>converts to</th></tr>
   2710      * <tr><td>cel-gaulish</td><td>&nbsp;</td><td>xtg-x-cel-gaulish</td></tr>
   2711      * <tr><td>en-GB-oed</td><td>&nbsp;</td><td>en-GB-x-oed</td></tr>
   2712      * <tr><td>i-default</td><td>&nbsp;</td><td>en-x-i-default</td></tr>
   2713      * <tr><td>i-enochian</td><td>&nbsp;</td><td>und-x-i-enochian</td></tr>
   2714      * <tr><td>i-mingo</td><td>&nbsp;</td><td>see-x-i-mingo</td></tr>
   2715      * <tr><td>zh-min</td><td>&nbsp;</td><td>nan-x-zh-min</td></tr>
   2716      * </tbody>
   2717      * </table>
   2718      *
   2719      * <p>For a list of all grandfathered tags, see the
   2720      * IANA Language Subtag Registry (search for "Type: grandfathered").
   2721      *
   2722      * <p><b>Note</b>: there is no guarantee that <code>toLanguageTag</code>
   2723      * and <code>forLanguageTag</code> will round-trip.
   2724      *
   2725      * @param languageTag the language tag
   2726      * @return The locale that best represents the language tag.
   2727      * @throws NullPointerException if <code>languageTag</code> is <code>null</code>
   2728      * @see #toLanguageTag()
   2729      * @see ULocale.Builder#setLanguageTag(String)
   2730      *
   2731      * @draft ICU 4.2
   2732      * @provisional This API might change or be removed in a future release.
   2733      */
   2734     public static ULocale forLanguageTag(String languageTag) {
   2735         LanguageTag tag = LanguageTag.parse(languageTag, null);
   2736         InternalLocaleBuilder bldr = new InternalLocaleBuilder();
   2737         bldr.setLanguageTag(tag);
   2738         return getInstance(bldr.getBaseLocale(), bldr.getLocaleExtensions());
   2739     }
   2740 
   2741 
   2742     /**
   2743      * <code>Builder</code> is used to build instances of <code>ULocale</code>
   2744      * from values configured by the setters.  Unlike the <code>ULocale</code>
   2745      * constructors, the <code>Builder</code> checks if a value configured by a
   2746      * setter satisfies the syntax requirements defined by the <code>ULocale</code>
   2747      * class.  A <code>ULocale</code> object created by a <code>Builder</code> is
   2748      * well-formed and can be transformed to a well-formed IETF BCP 47 language tag
   2749      * without losing information.
   2750      *
   2751      * <p><b>Note:</b> The <code>ULocale</code> class does not provide any
   2752      * syntactic restrictions on variant, while BCP 47 requires each variant
   2753      * subtag to be 5 to 8 alphanumerics or a single numeric followed by 3
   2754      * alphanumerics.  The method <code>setVariant</code> throws
   2755      * <code>IllformedLocaleException</code> for a variant that does not satisfy
   2756      * this restriction. If it is necessary to support such a variant, use a
   2757      * ULocale constructor.  However, keep in mind that a <code>ULocale</code>
   2758      * object created this way might lose the variant information when
   2759      * transformed to a BCP 47 language tag.
   2760      *
   2761      * <p>The following example shows how to create a <code>Locale</code> object
   2762      * with the <code>Builder</code>.
   2763      * <blockquote>
   2764      * <pre>
   2765      *     ULocale aLocale = new Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build();
   2766      * </pre>
   2767      * </blockquote>
   2768      *
   2769      * <p>Builders can be reused; <code>clear()</code> resets all
   2770      * fields to their default values.
   2771      *
   2772      * @see ULocale#toLanguageTag()
   2773      *
   2774      * @draft ICU 4.2
   2775      * @provisional This API might change or be removed in a future release.
   2776      */
   2777     public static final class Builder {
   2778 
   2779         private final InternalLocaleBuilder _locbld;
   2780 
   2781         /**
   2782          * Constructs an empty Builder. The default value of all
   2783          * fields, extensions, and private use information is the
   2784          * empty string.
   2785          *
   2786          * @draft ICU 4.2
   2787          * @provisional This API might change or be removed in a future release.
   2788          */
   2789         public Builder() {
   2790             _locbld = new InternalLocaleBuilder();
   2791         }
   2792 
   2793         /**
   2794          * Resets the <code>Builder</code> to match the provided
   2795          * <code>locale</code>.  Existing state is discarded.
   2796          *
   2797          * <p>All fields of the locale must be well-formed, see {@link Locale}.
   2798          *
   2799          * <p>Locales with any ill-formed fields cause
   2800          * <code>IllformedLocaleException</code> to be thrown.
   2801          *
   2802          * @param locale the locale
   2803          * @return This builder.
   2804          * @throws IllformedLocaleException if <code>locale</code> has
   2805          * any ill-formed fields.
   2806          * @throws NullPointerException if <code>locale</code> is null.
   2807          *
   2808          * @draft ICU 4.2
   2809          * @provisional This API might change or be removed in a future release.
   2810          */
   2811         public Builder setLocale(ULocale locale) {
   2812             try {
   2813                 _locbld.setLocale(locale.base(), locale.extensions());
   2814             } catch (LocaleSyntaxException e) {
   2815                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   2816             }
   2817             return this;
   2818         }
   2819 
   2820         /**
   2821          * Resets the Builder to match the provided IETF BCP 47
   2822          * language tag.  Discards the existing state.  Null and the
   2823          * empty string cause the builder to be reset, like {@link
   2824          * #clear}.  Grandfathered tags (see {@link
   2825          * ULocale#forLanguageTag}) are converted to their canonical
   2826          * form before being processed.  Otherwise, the language tag
   2827          * must be well-formed (see {@link ULocale}) or an exception is
   2828          * thrown (unlike <code>ULocale.forLanguageTag</code>, which
   2829          * just discards ill-formed and following portions of the
   2830          * tag).
   2831          *
   2832          * @param languageTag the language tag
   2833          * @return This builder.
   2834          * @throws IllformedLocaleException if <code>languageTag</code> is ill-formed
   2835          * @see ULocale#forLanguageTag(String)
   2836          *
   2837          * @draft ICU 4.2
   2838          * @provisional This API might change or be removed in a future release.
   2839          */
   2840         public Builder setLanguageTag(String languageTag) {
   2841             ParseStatus sts = new ParseStatus();
   2842             LanguageTag tag = LanguageTag.parse(languageTag, sts);
   2843             if (sts.isError()) {
   2844                 throw new IllformedLocaleException(sts.getErrorMessage(), sts.getErrorIndex());
   2845             }
   2846             _locbld.setLanguageTag(tag);
   2847 
   2848             return this;
   2849         }
   2850 
   2851         /**
   2852          * Sets the language.  If <code>language</code> is the empty string or
   2853          * null, the language in this <code>Builder</code> is removed.  Otherwise,
   2854          * the language must be <a href="./Locale.html#def_language">well-formed</a>
   2855          * or an exception is thrown.
   2856          *
   2857          * <p>The typical language value is a two or three-letter language
   2858          * code as defined in ISO639.
   2859          *
   2860          * @param language the language
   2861          * @return This builder.
   2862          * @throws IllformedLocaleException if <code>language</code> is ill-formed
   2863          *
   2864          * @draft ICU 4.2
   2865          * @provisional This API might change or be removed in a future release.
   2866          */
   2867         public Builder setLanguage(String language) {
   2868             try {
   2869                 _locbld.setLanguage(language);
   2870             } catch (LocaleSyntaxException e) {
   2871                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   2872             }
   2873             return this;
   2874         }
   2875 
   2876         /**
   2877          * Sets the script. If <code>script</code> is null or the empty string,
   2878          * the script in this <code>Builder</code> is removed.
   2879          * Otherwise, the script must be well-formed or an exception is thrown.
   2880          *
   2881          * <p>The typical script value is a four-letter script code as defined by ISO 15924.
   2882          *
   2883          * @param script the script
   2884          * @return This builder.
   2885          * @throws IllformedLocaleException if <code>script</code> is ill-formed
   2886          *
   2887          * @draft ICU 4.2
   2888          * @provisional This API might change or be removed in a future release.
   2889          */
   2890         public Builder setScript(String script) {
   2891             try {
   2892                 _locbld.setScript(script);
   2893             } catch (LocaleSyntaxException e) {
   2894                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   2895             }
   2896             return this;
   2897         }
   2898 
   2899         /**
   2900          * Sets the region.  If region is null or the empty string, the region
   2901          * in this <code>Builder</code> is removed.  Otherwise,
   2902          * the region must be well-formed or an exception is thrown.
   2903          *
   2904          * <p>The typical region value is a two-letter ISO 3166 code or a
   2905          * three-digit UN M.49 area code.
   2906          *
   2907          * <p>The country value in the <code>Locale</code> created by the
   2908          * <code>Builder</code> is always normalized to upper case.
   2909          *
   2910          * @param region the region
   2911          * @return This builder.
   2912          * @throws IllformedLocaleException if <code>region</code> is ill-formed
   2913          *
   2914          * @draft ICU 4.2
   2915          * @provisional This API might change or be removed in a future release.
   2916          */
   2917         public Builder setRegion(String region) {
   2918             try {
   2919                 _locbld.setRegion(region);
   2920             } catch (LocaleSyntaxException e) {
   2921                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   2922             }
   2923             return this;
   2924         }
   2925 
   2926         /**
   2927          * Sets the variant.  If variant is null or the empty string, the
   2928          * variant in this <code>Builder</code> is removed.  Otherwise, it
   2929          * must consist of one or more well-formed subtags, or an exception is thrown.
   2930          *
   2931          * <p><b>Note:</b> This method checks if <code>variant</code>
   2932          * satisfies the IETF BCP 47 variant subtag's syntax requirements,
   2933          * and normalizes the value to lowercase letters.  However,
   2934          * the <code>ULocale</code> class does not impose any syntactic
   2935          * restriction on variant.  To set such a variant,
   2936          * use a ULocale constructor.
   2937          *
   2938          * @param variant the variant
   2939          * @return This builder.
   2940          * @throws IllformedLocaleException if <code>variant</code> is ill-formed
   2941          *
   2942          * @draft ICU 4.2
   2943          * @provisional This API might change or be removed in a future release.
   2944          */
   2945         public Builder setVariant(String variant) {
   2946             try {
   2947                 _locbld.setVariant(variant);
   2948             } catch (LocaleSyntaxException e) {
   2949                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   2950             }
   2951             return this;
   2952         }
   2953 
   2954         /**
   2955          * Sets the extension for the given key. If the value is null or the
   2956          * empty string, the extension is removed.  Otherwise, the extension
   2957          * must be well-formed or an exception is thrown.
   2958          *
   2959          * <p><b>Note:</b> The key {@link ULocale#UNICODE_LOCALE_EXTENSION
   2960          * UNICODE_LOCALE_EXTENSION} ('u') is used for the Unicode locale extension.
   2961          * Setting a value for this key replaces any existing Unicode locale key/type
   2962          * pairs with those defined in the extension.
   2963          *
   2964          * <p><b>Note:</b> The key {@link ULocale#PRIVATE_USE_EXTENSION
   2965          * PRIVATE_USE_EXTENSION} ('x') is used for the private use code. To be
   2966          * well-formed, the value for this key needs only to have subtags of one to
   2967          * eight alphanumeric characters, not two to eight as in the general case.
   2968          *
   2969          * @param key the extension key
   2970          * @param value the extension value
   2971          * @return This builder.
   2972          * @throws IllformedLocaleException if <code>key</code> is illegal
   2973          * or <code>value</code> is ill-formed
   2974          * @see #setUnicodeLocaleKeyword(String, String)
   2975          *
   2976          * @draft ICU 4.2
   2977          * @provisional This API might change or be removed in a future release.
   2978          */
   2979         public Builder setExtension(char key, String value) {
   2980             try {
   2981                 _locbld.setExtension(key, value);
   2982             } catch (LocaleSyntaxException e) {
   2983                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   2984             }
   2985             return this;
   2986         }
   2987 
   2988         /**
   2989          * Sets the Unicode locale keyword type for the given key.  If the type
   2990          * is null, the Unicode keyword is removed.  Otherwise, the key must be
   2991          * non-null and both key and type must be well-formed or an exception
   2992          * is thrown.
   2993          *
   2994          * <p>Keys and types are converted to lower case.
   2995          *
   2996          * <p><b>Note</b>:Setting the 'u' extension via {@link #setExtension}
   2997          * replaces all Unicode locale keywords with those defined in the
   2998          * extension.
   2999          *
   3000          * @param key the Unicode locale key
   3001          * @param type the Unicode locale type
   3002          * @return This builder.
   3003          * @throws IllformedLocaleException if <code>key</code> or <code>type</code>
   3004          * is ill-formed
   3005          * @throws NullPointerException if <code>key</code> is null
   3006          * @see #setExtension(char, String)
   3007          *
   3008          * @draft ICU 4.4
   3009          * @provisional This API might change or be removed in a future release.
   3010          */
   3011         public Builder setUnicodeLocaleKeyword(String key, String type) {
   3012             try {
   3013                 _locbld.setUnicodeLocaleKeyword(key, type);
   3014             } catch (LocaleSyntaxException e) {
   3015                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   3016             }
   3017             return this;
   3018         }
   3019 
   3020         /**
   3021          * Adds a unicode locale attribute, if not already present, otherwise
   3022          * has no effect.  The attribute must not be null and must be well-formed
   3023          * or an exception is thrown.
   3024          *
   3025          * @param attribute the attribute
   3026          * @return This builder.
   3027          * @throws NullPointerException if <code>attribute</code> is null
   3028          * @throws IllformedLocaleException if <code>attribute</code> is ill-formed
   3029          * @see #setExtension(char, String)
   3030          *
   3031          * @draft ICU 4.6
   3032          * @provisional This API might change or be removed in a future release.
   3033          */
   3034         public Builder addUnicodeLocaleAttribute(String attribute) {
   3035             try {
   3036                 _locbld.addUnicodeLocaleAttribute(attribute);
   3037             } catch (LocaleSyntaxException e) {
   3038                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   3039             }
   3040             return this;
   3041         }
   3042 
   3043         /**
   3044          * Removes a unicode locale attribute, if present, otherwise has no
   3045          * effect.  The attribute must not be null and must be well-formed
   3046          * or an exception is thrown.
   3047          *
   3048          * <p>Attribute comparision for removal is case-insensitive.
   3049          *
   3050          * @param attribute the attribute
   3051          * @return This builder.
   3052          * @throws NullPointerException if <code>attribute</code> is null
   3053          * @throws IllformedLocaleException if <code>attribute</code> is ill-formed
   3054          * @see #setExtension(char, String)
   3055          *
   3056          * @draft ICU 4.6
   3057          * @provisional This API might change or be removed in a future release.
   3058          */
   3059         public Builder removeUnicodeLocaleAttribute(String attribute) {
   3060             try {
   3061                 _locbld.removeUnicodeLocaleAttribute(attribute);
   3062             } catch (LocaleSyntaxException e) {
   3063                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
   3064             }
   3065             return this;
   3066         }
   3067 
   3068         /**
   3069          * Resets the builder to its initial, empty state.
   3070          *
   3071          * @return this builder
   3072          *
   3073          * @draft ICU 4.2
   3074          * @provisional This API might change or be removed in a future release.
   3075          */
   3076         public Builder clear() {
   3077             _locbld.clear();
   3078             return this;
   3079         }
   3080 
   3081         /**
   3082          * Resets the extensions to their initial, empty state.
   3083          * Language, script, region and variant are unchanged.
   3084          *
   3085          * @return this builder
   3086          * @see #setExtension(char, String)
   3087          *
   3088          * @draft ICU 4.2
   3089          * @provisional This API might change or be removed in a future release.
   3090          */
   3091         public Builder clearExtensions() {
   3092             _locbld.clearExtensions();
   3093             return this;
   3094         }
   3095 
   3096         /**
   3097          * Returns an instance of <code>ULocale</code> created from the fields set
   3098          * on this builder.
   3099          *
   3100          * @return a new Locale
   3101          *
   3102          * @draft ICU 4.4
   3103          * @provisional This API might change or be removed in a future release.
   3104          */
   3105         public ULocale build() {
   3106             return getInstance(_locbld.getBaseLocale(), _locbld.getLocaleExtensions());
   3107         }
   3108     }
   3109 
   3110     private static ULocale getInstance(BaseLocale base, LocaleExtensions exts) {
   3111         String id = lscvToID(base.getLanguage(), base.getScript(), base.getRegion(),
   3112                 base.getVariant());
   3113 
   3114         Set<Character> extKeys = exts.getKeys();
   3115         if (!extKeys.isEmpty()) {
   3116             // legacy locale ID assume Unicode locale keywords and
   3117             // other extensions are at the same level.
   3118             // e.g. @a=ext-for-aa;calendar=japanese;m=ext-for-mm;x=priv-use
   3119 
   3120             TreeMap<String, String> kwds = new TreeMap<String, String>();
   3121             for (Character key : extKeys) {
   3122                 Extension ext = exts.getExtension(key);
   3123                 if (ext instanceof UnicodeLocaleExtension) {
   3124                     UnicodeLocaleExtension uext = (UnicodeLocaleExtension)ext;
   3125                     Set<String> ukeys = uext.getUnicodeLocaleKeys();
   3126                     for (String bcpKey : ukeys) {
   3127                         String bcpType = uext.getUnicodeLocaleType(bcpKey);
   3128                         // convert to legacy key/type
   3129                         String lkey = bcp47ToLDMLKey(bcpKey);
   3130                         String ltype = bcp47ToLDMLType(lkey, ((bcpType.length() == 0) ? "yes" : bcpType)); // use "yes" as the value of typeless keywords
   3131                         // special handling for u-va-posix, since this is a variant, not a keyword
   3132                         if (lkey.equals("va") && ltype.equals("posix") && base.getVariant().length() == 0) {
   3133                             id = id + "_POSIX";
   3134                         } else {
   3135                             kwds.put(lkey, ltype);
   3136                         }
   3137                     }
   3138                     // Mapping Unicode locale attribute to the special keyword, attribute=xxx-yyy
   3139                     Set<String> uattributes = uext.getUnicodeLocaleAttributes();
   3140                     if (uattributes.size() > 0) {
   3141                         StringBuilder attrbuf = new StringBuilder();
   3142                         for (String attr : uattributes) {
   3143                             if (attrbuf.length() > 0) {
   3144                                 attrbuf.append('-');
   3145                             }
   3146                             attrbuf.append(attr);
   3147                         }
   3148                         kwds.put(LOCALE_ATTRIBUTE_KEY, attrbuf.toString());
   3149                     }
   3150                 } else {
   3151                     kwds.put(String.valueOf(key), ext.getValue());
   3152                 }
   3153             }
   3154 
   3155             if (!kwds.isEmpty()) {
   3156                 StringBuilder buf = new StringBuilder(id);
   3157                 buf.append("@");
   3158                 Set<Map.Entry<String, String>> kset = kwds.entrySet();
   3159                 boolean insertSep = false;
   3160                 for (Map.Entry<String, String> kwd : kset) {
   3161                     if (insertSep) {
   3162                         buf.append(";");
   3163                     } else {
   3164                         insertSep = true;
   3165                     }
   3166                     buf.append(kwd.getKey());
   3167                     buf.append("=");
   3168                     buf.append(kwd.getValue());
   3169                 }
   3170 
   3171                 id = buf.toString();
   3172             }
   3173         }
   3174         return new ULocale(id);
   3175     }
   3176 
   3177     private BaseLocale base() {
   3178         if (baseLocale == null) {
   3179             String language = getLanguage();
   3180             if (equals(ULocale.ROOT)) {
   3181                 language = "";
   3182             }
   3183             baseLocale = BaseLocale.getInstance(language, getScript(), getCountry(), getVariant());
   3184         }
   3185         return baseLocale;
   3186     }
   3187 
   3188     private LocaleExtensions extensions() {
   3189         if (extensions == null) {
   3190             Iterator<String> kwitr = getKeywords();
   3191             if (kwitr == null) {
   3192                 extensions = LocaleExtensions.EMPTY_EXTENSIONS;
   3193             } else {
   3194                 InternalLocaleBuilder intbld = new InternalLocaleBuilder();
   3195                 while (kwitr.hasNext()) {
   3196                     String key = kwitr.next();
   3197                     if (key.equals(LOCALE_ATTRIBUTE_KEY)) {
   3198                         // special keyword used for representing Unicode locale attributes
   3199                         String[] uattributes = getKeywordValue(key).split("[-_]");
   3200                         for (String uattr : uattributes) {
   3201                             try {
   3202                                 intbld.addUnicodeLocaleAttribute(uattr);
   3203                             } catch (LocaleSyntaxException e) {
   3204                                 // ignore and fall through
   3205                             }
   3206                         }
   3207                     } else if (key.length() >= 2) {
   3208                         String bcpKey = ldmlKeyToBCP47(key);
   3209                         String bcpType = ldmlTypeToBCP47(key, getKeywordValue(key));
   3210                         if (bcpKey != null && bcpType != null) {
   3211                             try {
   3212                                 intbld.setUnicodeLocaleKeyword(bcpKey, bcpType);
   3213                             } catch (LocaleSyntaxException e) {
   3214                                 // ignore and fall through
   3215                             }
   3216                         }
   3217                     } else if (key.length() == 1 && (key.charAt(0) != UNICODE_LOCALE_EXTENSION)) {
   3218                         try  {
   3219                             intbld.setExtension(key.charAt(0), getKeywordValue(key).replace("_",
   3220                                     LanguageTag.SEP));
   3221                         } catch (LocaleSyntaxException e) {
   3222                             // ignore and fall through
   3223                         }
   3224                     }
   3225                 }
   3226                 extensions = intbld.getLocaleExtensions();
   3227             }
   3228         }
   3229         return extensions;
   3230     }
   3231 
   3232     //
   3233     // LDML legacy/BCP47 key and type mapping functions
   3234     //
   3235     private static String ldmlKeyToBCP47(String key) {
   3236         //#com.ibm.icu.base
   3237         // normalize key to lowercase
   3238         key = AsciiUtil.toLowerString(key);
   3239         String bcpKey = null;
   3240 
   3241         for (int i = 0; i < KEYMAP.length; i += 2) {
   3242             if (key.equals(KEYMAP[i])) {
   3243                 bcpKey = KEYMAP[i + 1];
   3244                 break;
   3245             }
   3246         }
   3247 
   3248         if (bcpKey == null) {
   3249             if (key.length() == 2 && LanguageTag.isExtensionSubtag(key)) {
   3250                 return key;
   3251             }
   3252             return null;
   3253         }
   3254 
   3255         return bcpKey;
   3256     }
   3257 
   3258     private static String bcp47ToLDMLKey(String bcpKey) {
   3259         //#com.ibm.icu.base
   3260         // normalize bcp key to lowercase
   3261         bcpKey = AsciiUtil.toLowerString(bcpKey);
   3262         String key = null;
   3263 
   3264         for (int i = 0; i < KEYMAP.length; i += 2) {
   3265             if (bcpKey.equals(KEYMAP[i + 1])) {
   3266                 key = KEYMAP[i];
   3267                 break;
   3268             }
   3269         }
   3270 
   3271         if (key == null) {
   3272             return bcpKey;
   3273         }
   3274 
   3275         return key;
   3276     }
   3277 
   3278     private static String ldmlTypeToBCP47(String key, String type) {
   3279         //#com.ibm.icu.base
   3280 
   3281         // keys are case-insensitive, while types are case-sensitive
   3282         key = AsciiUtil.toLowerString(key);
   3283         String bcpType = null;
   3284         String[] map = null;
   3285         String[] aliasMap = null;
   3286 
   3287         if (key.equals("calendar")) {
   3288             map = TYPEMAP_CALENDAR;
   3289         } else if (key.equals("colalternate")) {
   3290             map = TYPEMAP_COLALTERNATE;
   3291         } else if (key.equals("colbackwards")) {
   3292             map = TYPEMAP_COLBACKWARDS;
   3293         } else if (key.equals("colcasefirst")) {
   3294             map = TYPEMAP_COLCASEFIRST;
   3295         } else if (key.equals("colcaselevel")) {
   3296             map = TYPEMAP_COLCASELEVEL;
   3297         } else if (key.equals("colhiraganaquaternary")) {
   3298             map = TYPEMAP_COLHIRAGANAQUATERNARY;
   3299         } else if (key.equals("collation")) {
   3300             map = TYPEMAP_COLLATION;
   3301         } else if (key.equals("colnormalization")) {
   3302             map = TYPEMAP_COLNORMALIZATION;
   3303         } else if (key.equals("colnumeric")) {
   3304             map = TYPEMAP_COLNUMERIC;
   3305         } else if (key.equals("colstrength")) {
   3306             map = TYPEMAP_COLSTRENGTH;
   3307             aliasMap = TYPEALIAS_COLSTRENGTH;
   3308         } else if (key.equals("numbers")) {
   3309             map = TYPEMAP_NUMBERS;
   3310         } else if (key.equals("timezone")) {
   3311             map = TYPEMAP_TIMEZONE;
   3312             aliasMap = TYPEALIAS_TIMEZONE;
   3313         }
   3314 
   3315         // LDML alias -> LDML canonical
   3316         if (aliasMap != null) {
   3317             for (int i = 0; i < aliasMap.length; i += 2) {
   3318                 if (type.equals(aliasMap[i])) {
   3319                     type = aliasMap[i + 1];
   3320                     break;
   3321                 }
   3322             }
   3323         }
   3324 
   3325         // LDML type -> BCP47 type
   3326         if (map != null) {
   3327             for (int i = 0; i < map.length; i += 2) {
   3328                 if (type.equals(map[i])) {
   3329                     bcpType = map[i + 1];
   3330                     break;
   3331                 }
   3332             }
   3333         }
   3334 
   3335         if (bcpType == null) {
   3336             int typeLen = type.length();
   3337             if (typeLen >= 3 && typeLen <= 8 && LanguageTag.isExtensionSubtag(type)) {
   3338                 return type;
   3339             }
   3340             return null;
   3341         }
   3342         return bcpType;
   3343     }
   3344 
   3345     private static String bcp47ToLDMLType(String key, String bcpType) {
   3346         //#com.ibm.icu.base
   3347 
   3348         // normalize key/bcpType to lowercase
   3349         key = AsciiUtil.toLowerString(key);
   3350         bcpType = AsciiUtil.toLowerString(bcpType);
   3351         String type = null;
   3352         String[] map = null;
   3353 
   3354         if (key.equals("calendar")) {
   3355             map = TYPEMAP_CALENDAR;
   3356         } else if (key.equals("colalternate")) {
   3357             map = TYPEMAP_COLALTERNATE;
   3358         } else if (key.equals("colbackwards")) {
   3359             map = TYPEMAP_COLBACKWARDS;
   3360         } else if (key.equals("colcasefirst")) {
   3361             map = TYPEMAP_COLCASEFIRST;
   3362         } else if (key.equals("colcaselevel")) {
   3363             map = TYPEMAP_COLCASELEVEL;
   3364         } else if (key.equals("colhiraganaquaternary")) {
   3365             map = TYPEMAP_COLHIRAGANAQUATERNARY;
   3366         } else if (key.equals("collation")) {
   3367             map = TYPEMAP_COLLATION;
   3368         } else if (key.equals("colnormalization")) {
   3369             map = TYPEMAP_COLNORMALIZATION;
   3370         } else if (key.equals("colnumeric")) {
   3371             map = TYPEMAP_COLNUMERIC;
   3372         } else if (key.equals("colstrength")) {
   3373             map = TYPEMAP_COLSTRENGTH;
   3374         } else if (key.equals("timezone")) {
   3375             map = TYPEMAP_TIMEZONE;
   3376         }
   3377 
   3378         if (map != null) {
   3379             for (int i = 0; i < map.length; i += 2) {
   3380                 if (bcpType.equals(map[i + 1])) {
   3381                     type = map[i];
   3382                     break;
   3383                 }
   3384             }
   3385         }
   3386 
   3387         return (type != null) ? type : bcpType;
   3388     }
   3389 
   3390     /*
   3391      * JDK Locale Helper
   3392      */
   3393     private static final class JDKLocaleHelper {
   3394         private static boolean isJava7orNewer = false;
   3395 
   3396         /*
   3397          * New methods in Java 7 Locale class
   3398          */
   3399         private static Method mGetScript;
   3400         private static Method mGetExtensionKeys;
   3401         private static Method mGetExtension;
   3402         private static Method mGetUnicodeLocaleKeys;
   3403         private static Method mGetUnicodeLocaleAttributes;
   3404         private static Method mGetUnicodeLocaleType;
   3405         private static Method mForLanguageTag;
   3406 
   3407         private static Method mGetDefault;
   3408         private static Method mSetDefault;
   3409         private static Object eDISPLAY;
   3410         private static Object eFORMAT;
   3411 
   3412         /*
   3413          * This table is used for mapping between ICU and special Java
   3414          * 6 locales.  When an ICU locale matches <minumum base> with
   3415          * <keyword>/<value>, the ICU locale is mapped to <Java> locale.
   3416          * For example, both ja_JP@calendar=japanese and ja@calendar=japanese
   3417          * are mapped to Java locale "ja_JP_JP".  ICU locale "nn" is mapped
   3418          * to Java locale "no_NO_NY".
   3419          */
   3420         private static final String[][] JAVA6_MAPDATA = {
   3421         //  { <Java>,       <ICU base>, <keyword>,  <value>,    <minimum base>
   3422             { "ja_JP_JP",   "ja_JP",    "calendar", "japanese", "ja"},
   3423             { "no_NO_NY",   "nn_NO",    null,       null,       "nn"},
   3424             { "th_TH_TH",   "th_TH",    "numbers",  "thai",     "th"},
   3425         };
   3426 
   3427         static {
   3428             do {
   3429                 try {
   3430                     mGetScript = Locale.class.getMethod("getScript", (Class[]) null);
   3431                     mGetExtensionKeys = Locale.class.getMethod("getExtensionKeys", (Class[]) null);
   3432                     mGetExtension = Locale.class.getMethod("getExtension", char.class);
   3433                     mGetUnicodeLocaleKeys = Locale.class.getMethod("getUnicodeLocaleKeys", (Class[]) null);
   3434                     mGetUnicodeLocaleAttributes = Locale.class.getMethod("getUnicodeLocaleAttributes", (Class[]) null);
   3435                     mGetUnicodeLocaleType = Locale.class.getMethod("getUnicodeLocaleType", String.class);
   3436                     mForLanguageTag = Locale.class.getMethod("forLanguageTag", String.class);
   3437 
   3438                     Class<?> cCategory = null;
   3439                     Class<?>[] classes = Locale.class.getDeclaredClasses();
   3440                     for (Class<?> c : classes) {
   3441                         if (c.getName().equals("java.util.Locale$Category")) {
   3442                             cCategory = c;
   3443                             break;
   3444                         }
   3445                     }
   3446                     if (cCategory == null) {
   3447                         break;
   3448                     }
   3449                     mGetDefault = Locale.class.getDeclaredMethod("getDefault", cCategory);
   3450                     mSetDefault = Locale.class.getDeclaredMethod("setDefault", cCategory, Locale.class);
   3451 
   3452                     Method mName = cCategory.getMethod("name", (Class[]) null);
   3453                     Object[] enumConstants = cCategory.getEnumConstants();
   3454                     for (Object e : enumConstants) {
   3455                         String catVal = (String)mName.invoke(e, (Object[])null);
   3456                         if (catVal.equals("DISPLAY")) {
   3457                             eDISPLAY = e;
   3458                         } else if (catVal.equals("FORMAT")) {
   3459                             eFORMAT = e;
   3460                         }
   3461                     }
   3462                     if (eDISPLAY == null || eFORMAT == null) {
   3463                         break;
   3464                     }
   3465                     isJava7orNewer = true;
   3466                 } catch (NoSuchMethodException e) {
   3467                 } catch (IllegalArgumentException e) {
   3468                 } catch (IllegalAccessException e) {
   3469                 } catch (InvocationTargetException e) {
   3470                 } catch (SecurityException e) {
   3471                     // TODO : report?
   3472                 }
   3473             } while (false);
   3474         }
   3475 
   3476         private JDKLocaleHelper() {
   3477         }
   3478 
   3479         public static boolean isJava7orNewer() {
   3480             return isJava7orNewer;
   3481         }
   3482 
   3483         public static ULocale toULocale(Locale loc) {
   3484             return isJava7orNewer ? toULocale7(loc) : toULocale6(loc);
   3485         }
   3486 
   3487         public static Locale toLocale(ULocale uloc) {
   3488             return isJava7orNewer ? toLocale7(uloc) : toLocale6(uloc);
   3489         }
   3490 
   3491         private static ULocale toULocale7(Locale loc) {
   3492             String language = loc.getLanguage();
   3493             String script = "";
   3494             String country = loc.getCountry();
   3495             String variant = loc.getVariant();
   3496 
   3497             Set<String> attributes = null;
   3498             Map<String, String> keywords = null;
   3499 
   3500             try {
   3501                 script = (String) mGetScript.invoke(loc, (Object[]) null);
   3502                 @SuppressWarnings("unchecked")
   3503                 Set<Character> extKeys = (Set<Character>) mGetExtensionKeys.invoke(loc, (Object[]) null);
   3504                 if (!extKeys.isEmpty()) {
   3505                     for (Character extKey : extKeys) {
   3506                         if (extKey.charValue() == 'u') {
   3507                             // Found Unicode locale extension
   3508 
   3509                             // attributes
   3510                             @SuppressWarnings("unchecked")
   3511                             Set<String> uAttributes = (Set<String>) mGetUnicodeLocaleAttributes.invoke(loc, (Object[]) null);
   3512                             if (!uAttributes.isEmpty()) {
   3513                                 attributes = new TreeSet<String>();
   3514                                 for (String attr : uAttributes) {
   3515                                     attributes.add(attr);
   3516                                 }
   3517                             }
   3518 
   3519                             // keywords
   3520                             @SuppressWarnings("unchecked")
   3521                             Set<String> uKeys = (Set<String>) mGetUnicodeLocaleKeys.invoke(loc, (Object[]) null);
   3522                             for (String kwKey : uKeys) {
   3523                                 String kwVal = (String) mGetUnicodeLocaleType.invoke(loc, kwKey);
   3524                                 if (kwVal != null) {
   3525                                     if (kwKey.equals("va")) {
   3526                                         // va-* is interpreted as a variant
   3527                                         variant = (variant.length() == 0) ? kwVal : kwVal + "_" + variant;
   3528                                     } else {
   3529                                         if (keywords == null) {
   3530                                             keywords = new TreeMap<String, String>();
   3531                                         }
   3532                                         keywords.put(kwKey, kwVal);
   3533                                     }
   3534                                 }
   3535                             }
   3536                         } else {
   3537                             String extVal = (String) mGetExtension.invoke(loc, extKey);
   3538                             if (extVal != null) {
   3539                                 if (keywords == null) {
   3540                                     keywords = new TreeMap<String, String>();
   3541                                 }
   3542                                 keywords.put(String.valueOf(extKey), extVal);
   3543                             }
   3544                         }
   3545                     }
   3546                 }
   3547             } catch (IllegalAccessException e) {
   3548                 throw new RuntimeException(e);
   3549             } catch (InvocationTargetException e) {
   3550                 throw new RuntimeException(e);
   3551             }
   3552 
   3553             // JDK locale no_NO_NY is not interpreted as Nynorsk by ICU,
   3554             // and it should be transformed to nn_NO.
   3555 
   3556             // Note: JDK7+ unerstand both no_NO_NY and nn_NO. When convert
   3557             // ICU locale to JDK, we do not need to map nn_NO back to no_NO_NY.
   3558 
   3559             if (language.equals("no") && country.equals("NO") && variant.equals("NY")) {
   3560                 language = "nn";
   3561                 variant = "";
   3562             }
   3563 
   3564             // Constructing ID
   3565             StringBuilder buf = new StringBuilder(language);
   3566 
   3567             if (script.length() > 0) {
   3568                 buf.append('_');
   3569                 buf.append(script);
   3570             }
   3571 
   3572             if (country.length() > 0) {
   3573                 buf.append('_');
   3574                 buf.append(country);
   3575             }
   3576 
   3577             if (variant.length() > 0) {
   3578                 if (country.length() == 0) {
   3579                     buf.append('_');
   3580                 }
   3581                 buf.append('_');
   3582                 buf.append(variant);
   3583             }
   3584 
   3585             if (attributes != null) {
   3586                 // transform Unicode attributes into a keyword
   3587                 StringBuilder attrBuf = new StringBuilder();
   3588                 for (String attr : attributes) {
   3589                     if (attrBuf.length() != 0) {
   3590                         attrBuf.append('-');
   3591                     }
   3592                     attrBuf.append(attr);
   3593                 }
   3594                 if (keywords == null) {
   3595                     keywords = new TreeMap<String, String>();
   3596                 }
   3597                 keywords.put(LOCALE_ATTRIBUTE_KEY, attrBuf.toString());
   3598             }
   3599 
   3600             if (keywords != null) {
   3601                 buf.append('@');
   3602                 boolean addSep = false;
   3603                 for (Entry<String, String> kwEntry : keywords.entrySet()) {
   3604                     String kwKey = kwEntry.getKey();
   3605                     String kwVal = kwEntry.getValue();
   3606 
   3607                     if (kwKey.length() != 1) {
   3608                         // Unicode locale key
   3609                         kwKey = bcp47ToLDMLKey(kwKey);
   3610                         // use "yes" as the value of typeless keywords
   3611                         kwVal = bcp47ToLDMLType(kwKey, ((kwVal.length() == 0) ? "yes" : kwVal));
   3612                     }
   3613 
   3614                     if (addSep) {
   3615                         buf.append(';');
   3616                     } else {
   3617                         addSep = true;
   3618                     }
   3619                     buf.append(kwKey);
   3620                     buf.append('=');
   3621                     buf.append(kwVal);
   3622                 }
   3623             }
   3624 
   3625             return new ULocale(getName(buf.toString()), loc);
   3626         }
   3627 
   3628         private static ULocale toULocale6(Locale loc) {
   3629             ULocale uloc = null;
   3630             String locStr = loc.toString();
   3631             if (locStr.length() == 0) {
   3632                 uloc = ULocale.ROOT;
   3633             } else {
   3634                 for (int i = 0; i < JAVA6_MAPDATA.length; i++) {
   3635                     if (JAVA6_MAPDATA[i][0].equals(locStr)) {
   3636                         LocaleIDParser p = new LocaleIDParser(JAVA6_MAPDATA[i][1]);
   3637                         p.setKeywordValue(JAVA6_MAPDATA[i][2], JAVA6_MAPDATA[i][3]);
   3638                         locStr = p.getName();
   3639                         break;
   3640                     }
   3641                 }
   3642                 uloc = new ULocale(getName(locStr), loc);
   3643             }
   3644             return uloc;
   3645         }
   3646 
   3647         private static Locale toLocale7(ULocale uloc) {
   3648             Locale loc = null;
   3649             String ulocStr = uloc.getName();
   3650             if (uloc.getScript().length() > 0 || ulocStr.contains("@")) {
   3651                 // With script or keywords available, the best way
   3652                 // to get a mapped Locale is to go through a language tag.
   3653                 // A Locale with script or keywords can only have variants
   3654                 // that is 1 to 8 alphanum. If this ULocale has a variant
   3655                 // subtag not satisfying the criteria, the variant subtag
   3656                 // will be lost.
   3657                 String tag = uloc.toLanguageTag();
   3658 
   3659                 // Workaround for variant casing problem:
   3660                 //
   3661                 // The variant field in ICU is case insensitive and normalized
   3662                 // to upper case letters by getVariant(), while
   3663                 // the variant field in JDK Locale is case sensitive.
   3664                 // ULocale#toLanguageTag use lower case characters for
   3665                 // BCP 47 variant and private use x-lvariant.
   3666                 //
   3667                 // Locale#forLanguageTag in JDK preserves character casing
   3668                 // for variant. Because ICU always normalizes variant to
   3669                 // upper case, we convert language tag to upper case here.
   3670                 tag = AsciiUtil.toUpperString(tag);
   3671 
   3672                 try {
   3673                     loc = (Locale)mForLanguageTag.invoke(null, tag);
   3674                 } catch (IllegalAccessException e) {
   3675                     throw new RuntimeException(e);
   3676                 } catch (InvocationTargetException e) {
   3677                     throw new RuntimeException(e);
   3678                 }
   3679             }
   3680             if (loc == null) {
   3681                 // Without script or keywords, use a Locale constructor,
   3682                 // so we can preserve any ill-formed variants.
   3683                 loc = new Locale(uloc.getLanguage(), uloc.getCountry(), uloc.getVariant());
   3684             }
   3685             return loc;
   3686         }
   3687 
   3688         private static Locale toLocale6(ULocale uloc) {
   3689             String locstr = uloc.getBaseName();
   3690             for (int i = 0; i < JAVA6_MAPDATA.length; i++) {
   3691                 if (locstr.equals(JAVA6_MAPDATA[i][1]) || locstr.equals(JAVA6_MAPDATA[i][4])) {
   3692                     if (JAVA6_MAPDATA[i][2] != null) {
   3693                         String val = uloc.getKeywordValue(JAVA6_MAPDATA[i][2]);
   3694                         if (val != null && val.equals(JAVA6_MAPDATA[i][3])) {
   3695                             locstr = JAVA6_MAPDATA[i][0];
   3696                             break;
   3697                         }
   3698                     } else {
   3699                         locstr = JAVA6_MAPDATA[i][0];
   3700                         break;
   3701                     }
   3702                 }
   3703             }
   3704             LocaleIDParser p = new LocaleIDParser(locstr);
   3705             String[] names = p.getLanguageScriptCountryVariant();
   3706             return new Locale(names[0], names[2], names[3]);
   3707         }
   3708 
   3709         public static Locale getDefault(Category category) {
   3710             Locale loc = Locale.getDefault();
   3711             if (isJava7orNewer) {
   3712                 Object cat = null;
   3713                 switch (category) {
   3714                 case DISPLAY:
   3715                     cat = eDISPLAY;
   3716                     break;
   3717                 case FORMAT:
   3718                     cat = eFORMAT;
   3719                     break;
   3720                 }
   3721                 if (cat != null) {
   3722                     try {
   3723                         loc = (Locale)mGetDefault.invoke(null, cat);
   3724                     } catch (InvocationTargetException e) {
   3725                         // fall through - use the base default
   3726                     } catch (IllegalArgumentException e) {
   3727                         // fall through - use the base default
   3728                     } catch (IllegalAccessException e) {
   3729                         // fall through - use the base default
   3730                     }
   3731                 }
   3732             }
   3733             return loc;
   3734         }
   3735 
   3736         public static void setDefault(Category category, Locale newLocale) {
   3737             if (isJava7orNewer) {
   3738                 Object cat = null;
   3739                 switch (category) {
   3740                 case DISPLAY:
   3741                     cat = eDISPLAY;
   3742                     break;
   3743                 case FORMAT:
   3744                     cat = eFORMAT;
   3745                     break;
   3746                 }
   3747                 if (cat != null) {
   3748                     try {
   3749                         mSetDefault.invoke(null, cat, newLocale);
   3750                     } catch (InvocationTargetException e) {
   3751                         // fall through - no effects
   3752                     } catch (IllegalArgumentException e) {
   3753                         // fall through - no effects
   3754                     } catch (IllegalAccessException e) {
   3755                         // fall through - no effects
   3756                     }
   3757                 }
   3758             }
   3759         }
   3760     }
   3761 
   3762     private static final String[] KEYMAP = {
   3763         "calendar", "ca",
   3764         "colalternate", "ka",
   3765         "colbackwards", "kb",
   3766         "colcasefirst", "kf",
   3767         "colcaselevel", "kc",
   3768         "colhiraganaquaternary", "kh",
   3769         "collation", "co",
   3770         "colnormalization", "kk",
   3771         "colnumeric", "kn",
   3772         "colstrength", "ks",
   3773         "currency", "cu",
   3774         "numbers", "nu",
   3775         "timezone", "tz",
   3776         "variabletop", "vt",
   3777     };
   3778 
   3779     private static final String[] TYPEMAP_CALENDAR = {
   3780         "ethiopic-amete-alem", "ethioaa",
   3781         "gregorian", "gregory",
   3782         "islamic-civil", "islamicc",
   3783     };
   3784 
   3785     private static final String[] TYPEMAP_COLALTERNATE = {
   3786         "non-ignorable", "noignore",
   3787     };
   3788 
   3789     private static final String[] TYPEMAP_COLBACKWARDS = {
   3790         "no", "false",
   3791         "yes", "true",
   3792     };
   3793 
   3794     private static final String[] TYPEMAP_COLCASEFIRST = {
   3795         "no", "false",
   3796     };
   3797 
   3798     private static final String[] TYPEMAP_COLCASELEVEL = {
   3799         "no", "false",
   3800         "yes", "true",
   3801     };
   3802 
   3803     private static final String[] TYPEMAP_COLHIRAGANAQUATERNARY = {
   3804         "no", "false",
   3805         "yes", "true",
   3806     };
   3807 
   3808     private static final String[] TYPEMAP_COLLATION = {
   3809         "dictionary", "dict",
   3810         "gb2312han", "gb2312",
   3811         "phonebook", "phonebk",
   3812         "traditional", "trad",
   3813     };
   3814 
   3815     private static final String[] TYPEMAP_COLNORMALIZATION = {
   3816         "no", "false",
   3817         "yes", "true",
   3818     };
   3819 
   3820     private static final String[] TYPEMAP_COLNUMERIC = {
   3821         "no", "false",
   3822         "yes", "true",
   3823     };
   3824 
   3825     private static final String[] TYPEMAP_COLSTRENGTH = {
   3826         "identical", "identic",
   3827         "primary", "level1",
   3828         "quaternary", "level4",
   3829         "secondary", "level2",
   3830         "tertiary", "level3",
   3831     };
   3832 
   3833     private static final String[] TYPEMAP_NUMBERS = {
   3834         "traditional", "traditio",
   3835     };
   3836 
   3837     private static final String[] TYPEMAP_TIMEZONE = {
   3838         "Africa/Abidjan", "ciabj",
   3839         "Africa/Accra", "ghacc",
   3840         "Africa/Addis_Ababa", "etadd",
   3841         "Africa/Algiers", "dzalg",
   3842         "Africa/Asmera", "erasm",
   3843         "Africa/Bamako", "mlbko",
   3844         "Africa/Bangui", "cfbgf",
   3845         "Africa/Banjul", "gmbjl",
   3846         "Africa/Bissau", "gwoxb",
   3847         "Africa/Blantyre", "mwblz",
   3848         "Africa/Brazzaville", "cgbzv",
   3849         "Africa/Bujumbura", "bibjm",
   3850         "Africa/Cairo", "egcai",
   3851         "Africa/Casablanca", "macas",
   3852         "Africa/Ceuta", "esceu",
   3853         "Africa/Conakry", "gncky",
   3854         "Africa/Dakar", "sndkr",
   3855         "Africa/Dar_es_Salaam", "tzdar",
   3856         "Africa/Djibouti", "djjib",
   3857         "Africa/Douala", "cmdla",
   3858         "Africa/El_Aaiun", "eheai",
   3859         "Africa/Freetown", "slfna",
   3860         "Africa/Gaborone", "bwgbe",
   3861         "Africa/Harare", "zwhre",
   3862         "Africa/Johannesburg", "zajnb",
   3863         "Africa/Juba", "ssjub",
   3864         "Africa/Kampala", "ugkla",
   3865         "Africa/Khartoum", "sdkrt",
   3866         "Africa/Kigali", "rwkgl",
   3867         "Africa/Kinshasa", "cdfih",
   3868         "Africa/Lagos", "nglos",
   3869         "Africa/Libreville", "galbv",
   3870         "Africa/Lome", "tglfw",
   3871         "Africa/Luanda", "aolad",
   3872         "Africa/Lubumbashi", "cdfbm",
   3873         "Africa/Lusaka", "zmlun",
   3874         "Africa/Malabo", "gqssg",
   3875         "Africa/Maputo", "mzmpm",
   3876         "Africa/Maseru", "lsmsu",
   3877         "Africa/Mbabane", "szqmn",
   3878         "Africa/Mogadishu", "somgq",
   3879         "Africa/Monrovia", "lrmlw",
   3880         "Africa/Nairobi", "kenbo",
   3881         "Africa/Ndjamena", "tdndj",
   3882         "Africa/Niamey", "nenim",
   3883         "Africa/Nouakchott", "mrnkc",
   3884         "Africa/Ouagadougou", "bfoua",
   3885         "Africa/Porto-Novo", "bjptn",
   3886         "Africa/Sao_Tome", "sttms",
   3887         "Africa/Tripoli", "lytip",
   3888         "Africa/Tunis", "tntun",
   3889         "Africa/Windhoek", "nawdh",
   3890         "America/Adak", "usadk",
   3891         "America/Anchorage", "usanc",
   3892         "America/Anguilla", "aiaxa",
   3893         "America/Antigua", "aganu",
   3894         "America/Araguaina", "braux",
   3895         "America/Argentina/La_Rioja", "arirj",
   3896         "America/Argentina/Rio_Gallegos", "arrgl",
   3897         "America/Argentina/Salta", "arsla",
   3898         "America/Argentina/San_Juan", "aruaq",
   3899         "America/Argentina/San_Luis", "arluq",
   3900         "America/Argentina/Tucuman", "artuc",
   3901         "America/Argentina/Ushuaia", "arush",
   3902         "America/Aruba", "awaua",
   3903         "America/Asuncion", "pyasu",
   3904         "America/Bahia", "brssa",
   3905         "America/Bahia_Banderas", "mxpvr",
   3906         "America/Barbados", "bbbgi",
   3907         "America/Belem", "brbel",
   3908         "America/Belize", "bzbze",
   3909         "America/Blanc-Sablon", "caybx",
   3910         "America/Boa_Vista", "brbvb",
   3911         "America/Bogota", "cobog",
   3912         "America/Boise", "usboi",
   3913         "America/Buenos_Aires", "arbue",
   3914         "America/Cambridge_Bay", "caycb",
   3915         "America/Campo_Grande", "brcgr",
   3916         "America/Cancun", "mxcun",
   3917         "America/Caracas", "veccs",
   3918         "America/Catamarca", "arctc",
   3919         "America/Cayenne", "gfcay",
   3920         "America/Cayman", "kygec",
   3921         "America/Chicago", "uschi",
   3922         "America/Chihuahua", "mxchi",
   3923         "America/Coral_Harbour", "cayzs",
   3924         "America/Cordoba", "arcor",
   3925         "America/Costa_Rica", "crsjo",
   3926         "America/Creston", "cacfq",
   3927         "America/Cuiaba", "brcgb",
   3928         "America/Curacao", "ancur",
   3929         "America/Danmarkshavn", "gldkshvn",
   3930         "America/Dawson", "cayda",
   3931         "America/Dawson_Creek", "caydq",
   3932         "America/Denver", "usden",
   3933         "America/Detroit", "usdet",
   3934         "America/Dominica", "dmdom",
   3935         "America/Edmonton", "caedm",
   3936         "America/Eirunepe", "brern",
   3937         "America/El_Salvador", "svsal",
   3938         "America/Fortaleza", "brfor",
   3939         "America/Glace_Bay", "caglb",
   3940         "America/Godthab", "glgoh",
   3941         "America/Goose_Bay", "cagoo",
   3942         "America/Grand_Turk", "tcgdt",
   3943         "America/Grenada", "gdgnd",
   3944         "America/Guadeloupe", "gpbbr",
   3945         "America/Guatemala", "gtgua",
   3946         "America/Guayaquil", "ecgye",
   3947         "America/Guyana", "gygeo",
   3948         "America/Halifax", "cahal",
   3949         "America/Havana", "cuhav",
   3950         "America/Hermosillo", "mxhmo",
   3951         "America/Indiana/Knox", "usknx",
   3952         "America/Indiana/Marengo", "usaeg",
   3953         "America/Indiana/Petersburg", "uswsq",
   3954         "America/Indiana/Tell_City", "ustel",
   3955         "America/Indiana/Vevay", "usinvev",
   3956         "America/Indiana/Vincennes", "usoea",
   3957         "America/Indiana/Winamac", "uswlz",
   3958         "America/Indianapolis", "usind",
   3959         "America/Inuvik", "cayev",
   3960         "America/Iqaluit", "caiql",
   3961         "America/Jamaica", "jmkin",
   3962         "America/Jujuy", "arjuj",
   3963         "America/Juneau", "usjnu",
   3964         "America/Kentucky/Monticello", "usmoc",
   3965         "America/Kralendijk", "bqkra",
   3966         "America/La_Paz", "bolpb",
   3967         "America/Lima", "pelim",
   3968         "America/Los_Angeles", "uslax",
   3969         "America/Louisville", "uslui",
   3970         "America/Lower_Princes", "sxphi",
   3971         "America/Maceio", "brmcz",
   3972         "America/Managua", "nimga",
   3973         "America/Manaus", "brmao",
   3974         "America/Marigot", "gpmsb",
   3975         "America/Martinique", "mqfdf",
   3976         "America/Matamoros", "mxmam",
   3977         "America/Mazatlan", "mxmzt",
   3978         "America/Mendoza", "armdz",
   3979         "America/Menominee", "usmnm",
   3980         "America/Merida", "mxmid",
   3981         "America/Metlakatla", "usmtm",
   3982         "America/Mexico_City", "mxmex",
   3983         "America/Miquelon", "pmmqc",
   3984         "America/Moncton", "camon",
   3985         "America/Monterrey", "mxmty",
   3986         "America/Montevideo", "uymvd",
   3987         "America/Montreal", "camtr",
   3988         "America/Montserrat", "msmni",
   3989         "America/Nassau", "bsnas",
   3990         "America/New_York", "usnyc",
   3991         "America/Nipigon", "canpg",
   3992         "America/Nome", "usome",
   3993         "America/Noronha", "brfen",
   3994         "America/North_Dakota/Beulah", "usxul",
   3995         "America/North_Dakota/Center", "usndcnt",
   3996         "America/North_Dakota/New_Salem", "usndnsl",
   3997         "America/Ojinaga", "mxoji",
   3998         "America/Panama", "papty",
   3999         "America/Pangnirtung", "capnt",
   4000         "America/Paramaribo", "srpbm",
   4001         "America/Phoenix", "usphx",
   4002         "America/Port_of_Spain", "ttpos",
   4003         "America/Port-au-Prince", "htpap",
   4004         "America/Porto_Velho", "brpvh",
   4005         "America/Puerto_Rico", "prsju",
   4006         "America/Rainy_River", "caffs",
   4007         "America/Rankin_Inlet", "cayek",
   4008         "America/Recife", "brrec",
   4009         "America/Regina", "careg",
   4010         "America/Resolute", "careb",
   4011         "America/Rio_Branco", "brrbr",
   4012         "America/Santa_Isabel", "mxstis",
   4013         "America/Santarem", "brstm",
   4014         "America/Santiago", "clscl",
   4015         "America/Santo_Domingo", "dosdq",
   4016         "America/Sao_Paulo", "brsao",
   4017         "America/Scoresbysund", "globy",
   4018         "America/Shiprock", "usnavajo",
   4019         "America/Sitka", "ussit",
   4020         "America/St_Barthelemy", "gpsbh",
   4021         "America/St_Johns", "casjf",
   4022         "America/St_Kitts", "knbas",
   4023         "America/St_Lucia", "lccas",
   4024         "America/St_Thomas", "vistt",
   4025         "America/St_Vincent", "vcsvd",
   4026         "America/Swift_Current", "cayyn",
   4027         "America/Tegucigalpa", "hntgu",
   4028         "America/Thule", "glthu",
   4029         "America/Thunder_Bay", "cathu",
   4030         "America/Tijuana", "mxtij",
   4031         "America/Toronto", "cator",
   4032         "America/Tortola", "vgtov",
   4033         "America/Vancouver", "cavan",
   4034         "America/Whitehorse", "cayxy",
   4035         "America/Winnipeg", "cawnp",
   4036         "America/Yakutat", "usyak",
   4037         "America/Yellowknife", "cayzf",
   4038         "Antarctica/Casey", "aqcas",
   4039         "Antarctica/Davis", "aqdav",
   4040         "Antarctica/DumontDUrville", "aqddu",
   4041         "Antarctica/Macquarie", "aumqi",
   4042         "Antarctica/Mawson", "aqmaw",
   4043         "Antarctica/McMurdo", "aqmcm",
   4044         "Antarctica/Palmer", "aqplm",
   4045         "Antarctica/Rothera", "aqrot",
   4046         "Antarctica/South_Pole", "aqams",
   4047         "Antarctica/Syowa", "aqsyw",
   4048         "Antarctica/Vostok", "aqvos",
   4049         "Arctic/Longyearbyen", "sjlyr",
   4050         "Asia/Aden", "yeade",
   4051         "Asia/Almaty", "kzala",
   4052         "Asia/Amman", "joamm",
   4053         "Asia/Anadyr", "rudyr",
   4054         "Asia/Aqtau", "kzaau",
   4055         "Asia/Aqtobe", "kzakx",
   4056         "Asia/Ashgabat", "tmasb",
   4057         "Asia/Baghdad", "iqbgw",
   4058         "Asia/Bahrain", "bhbah",
   4059         "Asia/Baku", "azbak",
   4060         "Asia/Bangkok", "thbkk",
   4061         "Asia/Beirut", "lbbey",
   4062         "Asia/Bishkek", "kgfru",
   4063         "Asia/Brunei", "bnbwn",
   4064         "Asia/Calcutta", "inccu",
   4065         "Asia/Choibalsan", "mncoq",
   4066         "Asia/Chongqing", "cnckg",
   4067         "Asia/Colombo", "lkcmb",
   4068         "Asia/Damascus", "sydam",
   4069         "Asia/Dhaka", "bddac",
   4070         "Asia/Dili", "tldil",
   4071         "Asia/Dubai", "aedxb",
   4072         "Asia/Dushanbe", "tjdyu",
   4073         "Asia/Gaza", "gaza",
   4074         "Asia/Harbin", "cnhrb",
   4075         "Asia/Hebron", "hebron",
   4076         "Asia/Hong_Kong", "hkhkg",
   4077         "Asia/Hovd", "mnhvd",
   4078         "Asia/Irkutsk", "ruikt",
   4079         "Asia/Jakarta", "idjkt",
   4080         "Asia/Jayapura", "iddjj",
   4081         "Asia/Jerusalem", "jeruslm",
   4082         "Asia/Kabul", "afkbl",
   4083         "Asia/Kamchatka", "rupkc",
   4084         "Asia/Karachi", "pkkhi",
   4085         "Asia/Kashgar", "cnkhg",
   4086         "Asia/Katmandu", "npktm",
   4087         "Asia/Krasnoyarsk", "rukra",
   4088         "Asia/Kuala_Lumpur", "mykul",
   4089         "Asia/Kuching", "mykch",
   4090         "Asia/Kuwait", "kwkwi",
   4091         "Asia/Macau", "momfm",
   4092         "Asia/Magadan", "rugdx",
   4093         "Asia/Makassar", "idmak",
   4094         "Asia/Manila", "phmnl",
   4095         "Asia/Muscat", "ommct",
   4096         "Asia/Nicosia", "cynic",
   4097         "Asia/Novokuznetsk", "runoz",
   4098         "Asia/Novosibirsk", "ruovb",
   4099         "Asia/Omsk", "ruoms",
   4100         "Asia/Oral", "kzura",
   4101         "Asia/Phnom_Penh", "khpnh",
   4102         "Asia/Pontianak", "idpnk",
   4103         "Asia/Pyongyang", "kpfnj",
   4104         "Asia/Qatar", "qadoh",
   4105         "Asia/Qyzylorda", "kzkzo",
   4106         "Asia/Rangoon", "mmrgn",
   4107         "Asia/Riyadh", "saruh",
   4108         "Asia/Saigon", "vnsgn",
   4109         "Asia/Sakhalin", "ruuus",
   4110         "Asia/Samarkand", "uzskd",
   4111         "Asia/Seoul", "krsel",
   4112         "Asia/Shanghai", "cnsha",
   4113         "Asia/Singapore", "sgsin",
   4114         "Asia/Taipei", "twtpe",
   4115         "Asia/Tashkent", "uztas",
   4116         "Asia/Tbilisi", "getbs",
   4117         "Asia/Tehran", "irthr",
   4118         "Asia/Thimphu", "btthi",
   4119         "Asia/Tokyo", "jptyo",
   4120         "Asia/Ulaanbaatar", "mnuln",
   4121         "Asia/Urumqi", "cnurc",
   4122         "Asia/Vientiane", "lavte",
   4123         "Asia/Vladivostok", "ruvvo",
   4124         "Asia/Yakutsk", "ruyks",
   4125         "Asia/Yekaterinburg", "ruyek",
   4126         "Asia/Yerevan", "amevn",
   4127         "Atlantic/Azores", "ptpdl",
   4128         "Atlantic/Bermuda", "bmbda",
   4129         "Atlantic/Canary", "eslpa",
   4130         "Atlantic/Cape_Verde", "cvrai",
   4131         "Atlantic/Faeroe", "fotho",
   4132         "Atlantic/Madeira", "ptfnc",
   4133         "Atlantic/Reykjavik", "isrey",
   4134         "Atlantic/South_Georgia", "gsgrv",
   4135         "Atlantic/St_Helena", "shshn",
   4136         "Atlantic/Stanley", "fkpsy",
   4137         "Australia/Adelaide", "auadl",
   4138         "Australia/Brisbane", "aubne",
   4139         "Australia/Broken_Hill", "aubhq",
   4140         "Australia/Currie", "aukns",
   4141         "Australia/Darwin", "audrw",
   4142         "Australia/Eucla", "aueuc",
   4143         "Australia/Hobart", "auhba",
   4144         "Australia/Lindeman", "auldc",
   4145         "Australia/Lord_Howe", "auldh",
   4146         "Australia/Melbourne", "aumel",
   4147         "Australia/Perth", "auper",
   4148         "Australia/Sydney", "ausyd",
   4149         "CST6CDT", "cst6cdt",
   4150         "EST5EDT", "est5edt",
   4151         "Etc/GMT", "utc",
   4152         "Etc/GMT+1", "utcw01",
   4153         "Etc/GMT+10", "utcw10",
   4154         "Etc/GMT+11", "utcw11",
   4155         "Etc/GMT+12", "utcw12",
   4156         "Etc/GMT+2", "utcw02",
   4157         "Etc/GMT+3", "utcw03",
   4158         "Etc/GMT+4", "utcw04",
   4159         "Etc/GMT+5", "utcw05",
   4160         "Etc/GMT+6", "utcw06",
   4161         "Etc/GMT+7", "utcw07",
   4162         "Etc/GMT+8", "utcw08",
   4163         "Etc/GMT+9", "utcw09",
   4164         "Etc/GMT-1", "utce01",
   4165         "Etc/GMT-10", "utce10",
   4166         "Etc/GMT-11", "utce11",
   4167         "Etc/GMT-12", "utce12",
   4168         "Etc/GMT-13", "utce13",
   4169         "Etc/GMT-14", "utce14",
   4170         "Etc/GMT-2", "utce02",
   4171         "Etc/GMT-3", "utce03",
   4172         "Etc/GMT-4", "utce04",
   4173         "Etc/GMT-5", "utce05",
   4174         "Etc/GMT-6", "utce06",
   4175         "Etc/GMT-7", "utce07",
   4176         "Etc/GMT-8", "utce08",
   4177         "Etc/GMT-9", "utce09",
   4178         "Etc/Unknown", "unk",
   4179         "Europe/Amsterdam", "nlams",
   4180         "Europe/Andorra", "adalv",
   4181         "Europe/Athens", "grath",
   4182         "Europe/Belgrade", "rsbeg",
   4183         "Europe/Berlin", "deber",
   4184         "Europe/Bratislava", "skbts",
   4185         "Europe/Brussels", "bebru",
   4186         "Europe/Bucharest", "robuh",
   4187         "Europe/Budapest", "hubud",
   4188         "Europe/Chisinau", "mdkiv",
   4189         "Europe/Copenhagen", "dkcph",
   4190         "Europe/Dublin", "iedub",
   4191         "Europe/Gibraltar", "gigib",
   4192         "Europe/Guernsey", "gggci",
   4193         "Europe/Helsinki", "fihel",
   4194         "Europe/Isle_of_Man", "imdgs",
   4195         "Europe/Istanbul", "trist",
   4196         "Europe/Jersey", "jesth",
   4197         "Europe/Kaliningrad", "rukgd",
   4198         "Europe/Kiev", "uaiev",
   4199         "Europe/Lisbon", "ptlis",
   4200         "Europe/Ljubljana", "silju",
   4201         "Europe/London", "gblon",
   4202         "Europe/Luxembourg", "lulux",
   4203         "Europe/Madrid", "esmad",
   4204         "Europe/Malta", "mtmla",
   4205         "Europe/Mariehamn", "fimhq",
   4206         "Europe/Minsk", "bymsq",
   4207         "Europe/Monaco", "mcmon",
   4208         "Europe/Moscow", "rumow",
   4209         "Europe/Oslo", "noosl",
   4210         "Europe/Paris", "frpar",
   4211         "Europe/Podgorica", "metgd",
   4212         "Europe/Prague", "czprg",
   4213         "Europe/Riga", "lvrix",
   4214         "Europe/Rome", "itrom",
   4215         "Europe/Samara", "rukuf",
   4216         "Europe/San_Marino", "smsai",
   4217         "Europe/Sarajevo", "basjj",
   4218         "Europe/Simferopol", "uasip",
   4219         "Europe/Skopje", "mkskp",
   4220         "Europe/Sofia", "bgsof",
   4221         "Europe/Stockholm", "sesto",
   4222         "Europe/Tallinn", "eetll",
   4223         "Europe/Tirane", "altia",
   4224         "Europe/Uzhgorod", "uauzh",
   4225         "Europe/Vaduz", "livdz",
   4226         "Europe/Vatican", "vavat",
   4227         "Europe/Vienna", "atvie",
   4228         "Europe/Vilnius", "ltvno",
   4229         "Europe/Volgograd", "ruvog",
   4230         "Europe/Warsaw", "plwaw",
   4231         "Europe/Zagreb", "hrzag",
   4232         "Europe/Zaporozhye", "uaozh",
   4233         "Europe/Zurich", "chzrh",
   4234         "Indian/Antananarivo", "mgtnr",
   4235         "Indian/Chagos", "iodga",
   4236         "Indian/Christmas", "cxxch",
   4237         "Indian/Cocos", "cccck",
   4238         "Indian/Comoro", "kmyva",
   4239         "Indian/Kerguelen", "tfpfr",
   4240         "Indian/Mahe", "scmaw",
   4241         "Indian/Maldives", "mvmle",
   4242         "Indian/Mauritius", "muplu",
   4243         "Indian/Mayotte", "ytmam",
   4244         "Indian/Reunion", "rereu",
   4245         "MST7MDT", "mst7mdt",
   4246         "Pacific/Apia", "wsapw",
   4247         "Pacific/Auckland", "nzakl",
   4248         "Pacific/Chatham", "nzcht",
   4249         "Pacific/Easter", "clipc",
   4250         "Pacific/Efate", "vuvli",
   4251         "Pacific/Enderbury", "kipho",
   4252         "Pacific/Fakaofo", "tkfko",
   4253         "Pacific/Fiji", "fjsuv",
   4254         "Pacific/Funafuti", "tvfun",
   4255         "Pacific/Galapagos", "ecgps",
   4256         "Pacific/Gambier", "pfgmr",
   4257         "Pacific/Guadalcanal", "sbhir",
   4258         "Pacific/Guam", "gugum",
   4259         "Pacific/Honolulu", "ushnl",
   4260         "Pacific/Johnston", "umjon",
   4261         "Pacific/Kiritimati", "kicxi",
   4262         "Pacific/Kosrae", "fmksa",
   4263         "Pacific/Kwajalein", "mhkwa",
   4264         "Pacific/Majuro", "mhmaj",
   4265         "Pacific/Marquesas", "pfnhv",
   4266         "Pacific/Midway", "ummdy",
   4267         "Pacific/Nauru", "nrinu",
   4268         "Pacific/Niue", "nuiue",
   4269         "Pacific/Norfolk", "nfnlk",
   4270         "Pacific/Noumea", "ncnou",
   4271         "Pacific/Pago_Pago", "asppg",
   4272         "Pacific/Palau", "pwror",
   4273         "Pacific/Pitcairn", "pnpcn",
   4274         "Pacific/Ponape", "fmpni",
   4275         "Pacific/Port_Moresby", "pgpom",
   4276         "Pacific/Rarotonga", "ckrar",
   4277         "Pacific/Saipan", "mpspn",
   4278         "Pacific/Tahiti", "pfppt",
   4279         "Pacific/Tarawa", "kitrw",
   4280         "Pacific/Tongatapu", "totbu",
   4281         "Pacific/Truk", "fmtkk",
   4282         "Pacific/Wake", "umawk",
   4283         "Pacific/Wallis", "wfmau",
   4284         "PST8PDT", "pst8pdt",
   4285     };
   4286 
   4287     private static final String[] TYPEALIAS_COLSTRENGTH = {
   4288         "quarternary", "quaternary",
   4289     };
   4290 
   4291     private static final String[] TYPEALIAS_TIMEZONE = {
   4292         "Africa/Asmara", "Africa/Asmera",
   4293         "Africa/Timbuktu", "Africa/Bamako",
   4294         "America/Argentina/Buenos_Aires", "America/Buenos_Aires",
   4295         "America/Argentina/Catamarca", "America/Catamarca",
   4296         "America/Argentina/ComodRivadavia", "America/Catamarca",
   4297         "America/Argentina/Cordoba", "America/Cordoba",
   4298         "America/Argentina/Jujuy", "America/Jujuy",
   4299         "America/Argentina/Mendoza", "America/Mendoza",
   4300         "America/Atikokan", "America/Coral_Harbour",
   4301         "America/Atka", "America/Adak",
   4302         "America/Ensenada", "America/Tijuana",
   4303         "America/Fort_Wayne", "America/Indianapolis",
   4304         "America/Indiana/Indianapolis", "America/Indianapolis",
   4305         "America/Kentucky/Louisville", "America/Louisville",
   4306         "America/Knox_IN", "America/Indiana/Knox",
   4307         "America/Porto_Acre", "America/Rio_Branco",
   4308         "America/Rosario", "America/Cordoba",
   4309         "America/Virgin", "America/St_Thomas",
   4310         "Asia/Ashkhabad", "Asia/Ashgabat",
   4311         "Asia/Chungking", "Asia/Chongqing",
   4312         "Asia/Dacca", "Asia/Dhaka",
   4313         "Asia/Ho_Chi_Minh", "Asia/Saigon",
   4314         "Asia/Istanbul", "Europe/Istanbul",
   4315         "Asia/Kathmandu", "Asia/Katmandu",
   4316         "Asia/Kolkata", "Asia/Calcutta",
   4317         "Asia/Macao", "Asia/Macau",
   4318         "Asia/Tel_Aviv", "Asia/Jerusalem",
   4319         "Asia/Thimbu", "Asia/Thimphu",
   4320         "Asia/Ujung_Pandang", "Asia/Makassar",
   4321         "Asia/Ulan_Bator", "Asia/Ulaanbaatar",
   4322         "Atlantic/Faroe", "Atlantic/Faeroe",
   4323         "Atlantic/Jan_Mayen", "Arctic/Longyearbyen",
   4324         "Australia/ACT", "Australia/Sydney",
   4325         "Australia/Canberra", "Australia/Sydney",
   4326         "Australia/LHI", "Australia/Lord_Howe",
   4327         "Australia/North", "Australia/Darwin",
   4328         "Australia/NSW", "Australia/Sydney",
   4329         "Australia/Queensland", "Australia/Brisbane",
   4330         "Australia/South", "Australia/Adelaide",
   4331         "Australia/Tasmania", "Australia/Hobart",
   4332         "Australia/Victoria", "Australia/Melbourne",
   4333         "Australia/West", "Australia/Perth",
   4334         "Australia/Yancowinna", "Australia/Broken_Hill",
   4335         "Brazil/Acre", "America/Rio_Branco",
   4336         "Brazil/DeNoronha", "America/Noronha",
   4337         "Brazil/East", "America/Sao_Paulo",
   4338         "Brazil/West", "America/Manaus",
   4339         "Canada/Atlantic", "America/Halifax",
   4340         "Canada/Central", "America/Winnipeg",
   4341         "Canada/Eastern", "America/Toronto",
   4342         "Canada/East-Saskatchewan", "America/Regina",
   4343         "Canada/Mountain", "America/Edmonton",
   4344         "Canada/Newfoundland", "America/St_Johns",
   4345         "Canada/Pacific", "America/Vancouver",
   4346         "Canada/Saskatchewan", "America/Regina",
   4347         "Canada/Yukon", "America/Whitehorse",
   4348         "Chile/Continental", "America/Santiago",
   4349         "Chile/EasterIsland", "Pacific/Easter",
   4350         "Cuba", "America/Havana",
   4351         "Egypt", "Africa/Cairo",
   4352         "Eire", "Europe/Dublin",
   4353         "EST", "Etc/GMT+5",
   4354         "Etc/GMT+0", "Etc/GMT",
   4355         "Etc/GMT0", "Etc/GMT",
   4356         "Etc/GMT-0", "Etc/GMT",
   4357         "Etc/Greenwich", "Etc/GMT",
   4358         "Etc/UCT", "Etc/GMT",
   4359         "Etc/Universal", "Etc/GMT",
   4360         "Etc/UTC", "Etc/GMT",
   4361         "Etc/Zulu", "Etc/GMT",
   4362         "Europe/Belfast", "Europe/London",
   4363         "Europe/Nicosia", "Asia/Nicosia",
   4364         "Europe/Tiraspol", "Europe/Chisinau",
   4365         "GB", "Europe/London",
   4366         "GB-Eire", "Europe/London",
   4367         "GMT", "Etc/GMT",
   4368         "GMT+0", "Etc/GMT",
   4369         "GMT0", "Etc/GMT",
   4370         "GMT-0", "Etc/GMT",
   4371         "Greenwich", "Etc/GMT",
   4372         "Hongkong", "Asia/Hong_Kong",
   4373         "HST", "Etc/GMT+10",
   4374         "Iceland", "Atlantic/Reykjavik",
   4375         "Iran", "Asia/Tehran",
   4376         "Israel", "Asia/Jerusalem",
   4377         "Jamaica", "America/Jamaica",
   4378         "Japan", "Asia/Tokyo",
   4379         "Kwajalein", "Pacific/Kwajalein",
   4380         "Libya", "Africa/Tripoli",
   4381         "Mexico/BajaNorte", "America/Tijuana",
   4382         "Mexico/BajaSur", "America/Mazatlan",
   4383         "Mexico/General", "America/Mexico_City",
   4384         "MST", "Etc/GMT+7",
   4385         "Navajo", "America/Shiprock",
   4386         "NZ", "Pacific/Auckland",
   4387         "NZ-CHAT", "Pacific/Chatham",
   4388         "Pacific/Chuuk", "Pacific/Truk",
   4389         "Pacific/Pohnpei", "Pacific/Ponape",
   4390         "Pacific/Samoa", "Pacific/Pago_Pago",
   4391         "Pacific/Yap", "Pacific/Truk",
   4392         "Poland", "Europe/Warsaw",
   4393         "Portugal", "Europe/Lisbon",
   4394         "PRC", "Asia/Shanghai",
   4395         "ROC", "Asia/Taipei",
   4396         "ROK", "Asia/Seoul",
   4397         "Singapore", "Asia/Singapore",
   4398         "Turkey", "Europe/Istanbul",
   4399         "UCT", "Etc/GMT",
   4400         "Universal", "Etc/GMT",
   4401         "US/Alaska", "America/Anchorage",
   4402         "US/Aleutian", "America/Adak",
   4403         "US/Arizona", "America/Phoenix",
   4404         "US/Central", "America/Chicago",
   4405         "US/Eastern", "America/New_York",
   4406         "US/East-Indiana", "America/Indianapolis",
   4407         "US/Hawaii", "Pacific/Honolulu",
   4408         "US/Indiana-Starke", "America/Indiana/Knox",
   4409         "US/Michigan", "America/Detroit",
   4410         "US/Mountain", "America/Denver",
   4411         "US/Pacific", "America/Los_Angeles",
   4412         "US/Pacific-New", "America/Los_Angeles",
   4413         "US/Samoa", "Pacific/Pago_Pago",
   4414         "UTC", "Etc/GMT",
   4415         "W-SU", "Europe/Moscow",
   4416         "Zulu", "Etc/GMT",
   4417     };
   4418 }
   4419