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