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