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