12d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// © 2016 and later: Unicode, Inc. and others.
22d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License
37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
4f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert ******************************************************************************
587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Copyright (C) 2003-2016, International Business Machines Corporation and
6f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert * others. All Rights Reserved.
7f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert ******************************************************************************
8f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert */
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.util;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.Serializable;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.lang.reflect.InvocationTargetException;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.lang.reflect.Method;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.security.AccessControlException;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.security.AccessController;
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.security.PrivilegedAction;
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.ParseException;
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Iterator;
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.List;
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Locale;
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map;
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map.Entry;
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.MissingResourceException;
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Set;
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.TreeMap;
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.TreeSet;
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
292d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertimport com.ibm.icu.impl.CacheBase;
302d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertimport com.ibm.icu.impl.ICUData;
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.ICUResourceBundle;
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.ICUResourceTableAccess;
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.LocaleIDParser;
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.LocaleIDs;
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.LocaleUtility;
362d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertimport com.ibm.icu.impl.SoftCache;
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.locale.AsciiUtil;
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.locale.BaseLocale;
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.locale.Extension;
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.locale.InternalLocaleBuilder;
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.locale.KeyTypeData;
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.locale.LanguageTag;
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.locale.LocaleExtensions;
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.locale.LocaleSyntaxException;
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.locale.ParseStatus;
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.locale.UnicodeLocaleExtension;
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.lang.UScript;
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.LocaleDisplayNames;
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.LocaleDisplayNames.DialectHandling;
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@icuenhanced java.util.Locale}.{@icu _usage_}
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * A class analogous to {@link java.util.Locale} that provides additional
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * support for ICU protocol.  In ICU 3.0 this class is enhanced to support
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * RFC 3066 language identifiers.
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>Many classes and services in ICU follow a factory idiom, in
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * which a factory method or object responds to a client request with
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * an object.  The request includes a locale (the <i>requested</i>
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * locale), and the returned object is constructed using data for that
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * locale.  The system may lack data for the requested locale, in
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * which case the locale fallback mechanism will be invoked until a
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * populated locale is found (the <i>valid</i> locale).  Furthermore,
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * even when a populated locale is found (the <i>valid</i> locale),
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * further fallback may be required to reach a locale containing the
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * specific data required by the service (the <i>actual</i> locale).
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>ULocale performs <b>'normalization'</b> and <b>'canonicalization'</b> of locale ids.
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Normalization 'cleans up' ICU locale ids as follows:
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <ul>
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>language, script, country, variant, and keywords are properly cased<br>
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * (lower, title, upper, upper, and lower case respectively)</li>
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>hyphens used as separators are converted to underscores</li>
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>three-letter language and country ids are converted to two-letter
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * equivalents where available</li>
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>surrounding spaces are removed from keywords and values</li>
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>if there are multiple keywords, they are put in sorted order</li>
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </ul>
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Canonicalization additionally performs the following:
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <ul>
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>POSIX ids are converted to ICU format IDs</li>
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>'grandfathered' 3066 ids are converted to ICU standard form</li>
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>'PREEURO' and 'EURO' variants are converted to currency keyword form,
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * with the currency
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * id appropriate to the country of the locale (for PREEURO) or EUR (for EURO).
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </ul>
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * All ULocale constructors automatically normalize the locale id.  To handle
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * POSIX ids, <code>canonicalize</code> can be called to convert the id
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * to canonical form, or the <code>canonicalInstance</code> factory method
919e281ba4837cba4a1cf9523d6f8b0621b150063dScott Russell * can be called.
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
9387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * <p>This class provides selectors {@link #VALID_LOCALE} and {@link
9487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * #ACTUAL_LOCALE} intended for use in methods named
9587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * <tt>getLocale()</tt>.  These methods exist in several ICU classes,
9687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * including {@link com.ibm.icu.util.Calendar}, {@link
9787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * com.ibm.icu.util.Currency}, {@link com.ibm.icu.text.UFormat},
9887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * {@link com.ibm.icu.text.BreakIterator},
9987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * {@link com.ibm.icu.text.Collator},
10087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * {@link com.ibm.icu.text.DateFormatSymbols}, and {@link
10187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * com.ibm.icu.text.DecimalFormatSymbols} and their subclasses, if
10287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * any. Once an object of one of these classes has been created,
10387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * <tt>getLocale()</tt> may be called on it to determine the valid and
10487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * actual locale arrived at during the object's construction.
10587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert *
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>Note: The <i>actual</i> locale is returned correctly, but the <i>valid</i>
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * locale is not, in most cases.
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see java.util.Locale
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @author weiv
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @author Alan Liu
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @author Ram Viswanadha
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 2.8
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
11587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert@SuppressWarnings("javadoc")    // com.ibm.icu.text.Collator is in another project
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic final class ULocale implements Serializable, Comparable<ULocale> {
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // using serialver from jdk1.4.2_05
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final long serialVersionUID = 3715177670352309217L;
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1202d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    private static CacheBase<String, String, Void> nameCache = new SoftCache<String, String, Void>() {
1212d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
1222d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        protected String createInstance(String tmpLocaleID, Void unused) {
1232d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return new LocaleIDParser(tmpLocaleID).getName();
1242d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
1252d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    };
126f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for language.
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale ENGLISH = new ULocale("en", Locale.ENGLISH);
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for language.
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale FRENCH = new ULocale("fr", Locale.FRENCH);
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for language.
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale GERMAN = new ULocale("de", Locale.GERMAN);
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for language.
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale ITALIAN = new ULocale("it", Locale.ITALIAN);
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for language.
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale JAPANESE = new ULocale("ja", Locale.JAPANESE);
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for language.
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale KOREAN = new ULocale("ko", Locale.KOREAN);
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for language.
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale CHINESE = new ULocale("zh", Locale.CHINESE);
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
169f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
170f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // Special note about static initializer for
171f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    //   - SIMPLIFIED_CHINESE
172f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    //   - TRADTIONAL_CHINESE
173f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    //   - CHINA
174f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    //   - TAIWAN
175f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    //
176f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // Equivalent JDK Locale for ULocale.SIMPLIFIED_CHINESE is different
177f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // by JRE version. JRE 7 or later supports a script tag "Hans", while
178f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // JRE 6 or older does not. JDK's Locale.SIMPLIFIED_CHINESE is actually
179f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // zh_CN, not zh_Hans. This is same in Java 7 or later versions.
180f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    //
181f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // ULocale#toLocale() implementation uses Java reflection to create a Locale
182f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // with a script tag. When a new ULocale is constructed with the single arg
183f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // constructor, the volatile field 'Locale locale' is initialized by
184f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // #toLocale() method.
185f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    //
186f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // Because we cannot hardcode corresponding JDK Locale representation below,
187f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // SIMPLIFIED_CHINESE is constructed without JDK Locale argument, and
188f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // #toLocale() is used for resolving the best matching JDK Locale at runtime.
189f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    //
190f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    // The same thing applies to TRADITIONAL_CHINESE.
191f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for language.
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
196f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    public static final ULocale SIMPLIFIED_CHINESE = new ULocale("zh_Hans");
197f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for language.
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
203f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    public static final ULocale TRADITIONAL_CHINESE = new ULocale("zh_Hant");
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for country/region.
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale FRANCE = new ULocale("fr_FR", Locale.FRANCE);
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for country/region.
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale GERMANY = new ULocale("de_DE", Locale.GERMANY);
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for country/region.
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale ITALY = new ULocale("it_IT", Locale.ITALY);
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for country/region.
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale JAPAN = new ULocale("ja_JP", Locale.JAPAN);
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for country/region.
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale KOREA = new ULocale("ko_KR", Locale.KOREA);
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for country/region.
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
239f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    public static final ULocale CHINA = new ULocale("zh_Hans_CN");
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for country/region.
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale PRC = CHINA;
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for country/region.
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
251f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    public static final ULocale TAIWAN = new ULocale("zh_Hant_TW");
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for country/region.
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale UK = new ULocale("en_GB", Locale.UK);
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for country/region.
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale US = new ULocale("en_US", Locale.US);
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for country/region.
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale CANADA = new ULocale("en_CA", Locale.CANADA);
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Useful constant for country/region.
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale CANADA_FRENCH = new ULocale("fr_CA", Locale.CANADA_FRENCH);
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Handy constant.
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String EMPTY_STRING = "";
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Used in both ULocale and LocaleIDParser, so moved up here.
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final char UNDERSCORE            = '_';
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // default empty locale
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final Locale EMPTY_LOCALE = new Locale("", "");
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // special keyword key for Unicode locale attributes
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String LOCALE_ATTRIBUTE_KEY = "attribute";
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The root ULocale.
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 2.8
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final ULocale ROOT = new ULocale("", EMPTY_LOCALE);
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Enum for locale categories. These locale categories are used to get/set the default locale for
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the specific functionality represented by the category.
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 49
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public enum Category {
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Category used to represent the default locale for displaying user interfaces.
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 49
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DISPLAY,
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Category used to represent the default locale for formatting date, number and/or currency.
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 49
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        FORMAT
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3152d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    private static final SoftCache<Locale, ULocale, Void> CACHE = new SoftCache<Locale, ULocale, Void>() {
3162d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
3172d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        protected ULocale createInstance(Locale key, Void unused) {
3182d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return JDKLocaleHelper.toULocale(key);
3192d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
3202d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    };
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Cache the locale.
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient volatile Locale locale;
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The raw localeID that we were passed in.
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String localeID;
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Cache the locale data container fields.
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * In future, we want to use them as the primary locale identifier storage.
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient volatile BaseLocale baseLocale;
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient volatile LocaleExtensions extensions;
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String[][] CANONICALIZE_MAP;
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String[][] variantsToKeywords;
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static void initCANONICALIZE_MAP() {
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (CANONICALIZE_MAP == null) {
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /**
3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * This table lists pairs of locale ids for canonicalization.  The
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * The 1st item is the normalized id. The 2nd item is the
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * canonicalized id. The 3rd is the keyword. The 4th is the keyword value.
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String[][] tempCANONICALIZE_MAP = {
351f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    //              { EMPTY_STRING,     "en_US_POSIX", null, null }, /* .NET name */
352f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "C",              "en_US_POSIX", null, null }, /* POSIX name */
353f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "art_LOJBAN",     "jbo", null, null }, /* registered name */
354f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "az_AZ_CYRL",     "az_Cyrl_AZ", null, null }, /* .NET name */
355f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "az_AZ_LATN",     "az_Latn_AZ", null, null }, /* .NET name */
356f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "ca_ES_PREEURO",  "ca_ES", "currency", "ESP" },
357f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "cel_GAULISH",    "cel__GAULISH", null, null }, /* registered name */
358f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "de_1901",        "de__1901", null, null }, /* registered name */
359f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "de_1906",        "de__1906", null, null }, /* registered name */
360f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "de__PHONEBOOK",  "de", "collation", "phonebook" }, /* Old ICU name */
361f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "de_AT_PREEURO",  "de_AT", "currency", "ATS" },
362f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "de_DE_PREEURO",  "de_DE", "currency", "DEM" },
363f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "de_LU_PREEURO",  "de_LU", "currency", "EUR" },
364f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "el_GR_PREEURO",  "el_GR", "currency", "GRD" },
365f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "en_BOONT",       "en__BOONT", null, null }, /* registered name */
366f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "en_SCOUSE",      "en__SCOUSE", null, null }, /* registered name */
367f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "en_BE_PREEURO",  "en_BE", "currency", "BEF" },
368f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "en_IE_PREEURO",  "en_IE", "currency", "IEP" },
369f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "es__TRADITIONAL", "es", "collation", "traditional" }, /* Old ICU name */
370f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "es_ES_PREEURO",  "es_ES", "currency", "ESP" },
371f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "eu_ES_PREEURO",  "eu_ES", "currency", "ESP" },
372f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "fi_FI_PREEURO",  "fi_FI", "currency", "FIM" },
373f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "fr_BE_PREEURO",  "fr_BE", "currency", "BEF" },
374f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "fr_FR_PREEURO",  "fr_FR", "currency", "FRF" },
375f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "fr_LU_PREEURO",  "fr_LU", "currency", "LUF" },
376f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "ga_IE_PREEURO",  "ga_IE", "currency", "IEP" },
377f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "gl_ES_PREEURO",  "gl_ES", "currency", "ESP" },
378f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "hi__DIRECT",     "hi", "collation", "direct" }, /* Old ICU name */
379f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "it_IT_PREEURO",  "it_IT", "currency", "ITL" },
380f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "ja_JP_TRADITIONAL", "ja_JP", "calendar", "japanese" },
381f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    //              { "nb_NO_NY",       "nn_NO", null, null },
382f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "nl_BE_PREEURO",  "nl_BE", "currency", "BEF" },
383f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "nl_NL_PREEURO",  "nl_NL", "currency", "NLG" },
384f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "pt_PT_PREEURO",  "pt_PT", "currency", "PTE" },
385f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "sl_ROZAJ",       "sl__ROZAJ", null, null }, /* registered name */
386f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "sr_SP_CYRL",     "sr_Cyrl_RS", null, null }, /* .NET name */
387f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "sr_SP_LATN",     "sr_Latn_RS", null, null }, /* .NET name */
388f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "sr_YU_CYRILLIC", "sr_Cyrl_RS", null, null }, /* Linux name */
389f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "th_TH_TRADITIONAL", "th_TH", "calendar", "buddhist" }, /* Old ICU name */
390f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", null, null }, /* Linux name */
391f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "uz_UZ_CYRL",     "uz_Cyrl_UZ", null, null }, /* .NET name */
392f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "uz_UZ_LATN",     "uz_Latn_UZ", null, null }, /* .NET name */
393f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "zh_CHS",         "zh_Hans", null, null }, /* .NET name */
394f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "zh_CHT",         "zh_Hant", null, null }, /* .NET name */
395f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "zh_GAN",         "zh__GAN", null, null }, /* registered name */
396f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "zh_GUOYU",       "zh", null, null }, /* registered name */
397f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "zh_HAKKA",       "zh__HAKKA", null, null }, /* registered name */
398f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "zh_MIN",         "zh__MIN", null, null }, /* registered name */
399f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "zh_MIN_NAN",     "zh__MINNAN", null, null }, /* registered name */
400f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "zh_WUU",         "zh__WUU", null, null }, /* registered name */
401f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "zh_XIANG",       "zh__XIANG", null, null }, /* registered name */
402f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    { "zh_YUE",         "zh__YUE", null, null } /* registered name */
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            };
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            synchronized (ULocale.class) {
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (CANONICALIZE_MAP == null) {
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    CANONICALIZE_MAP = tempCANONICALIZE_MAP;
4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (variantsToKeywords == null) {
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /**
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * This table lists pairs of locale ids for canonicalization.  The
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * The first item is the normalized variant id.
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String[][] tempVariantsToKeywords = {
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    { "EURO",   "currency", "EUR" },
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    { "PINYIN", "collation", "pinyin" }, /* Solaris variant */
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    { "STROKE", "collation", "stroke" }  /* Solaris variant */
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            };
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            synchronized (ULocale.class) {
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (variantsToKeywords == null) {
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    variantsToKeywords = tempVariantsToKeywords;
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Private constructor used by static initializers.
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private ULocale(String localeID, Locale locale) {
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.localeID = localeID;
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.locale = locale;
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a ULocale object from a {@link java.util.Locale}.
4402a7db240a11150ab654325587d653754c62b71a7Yoshito Umaoka     * @param loc a {@link java.util.Locale}
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private ULocale(Locale loc) {
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.localeID = getName(forLocale(loc).toString());
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.locale = loc;
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a ULocale object for a {@link java.util.Locale}.
4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The ULocale is canonicalized.
4502a7db240a11150ab654325587d653754c62b71a7Yoshito Umaoka     * @param loc a {@link java.util.Locale}
4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.2
4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static ULocale forLocale(Locale loc) {
4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (loc == null) {
4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4572d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        return CACHE.getInstance(loc, null /* unused */);
4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Constructs a ULocale from a RFC 3066 locale ID. The locale ID consists
4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * of optional language, script, country, and variant fields in that order,
4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * separated by underscores, followed by an optional keyword list.  The
4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * script, if present, is four characters long-- this distinguishes it
4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * from a country code, which is two characters long.  Other fields
4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * are distinguished by position as indicated by the underscores.  The
4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * start of the keyword list is indicated by '@', and consists of two
4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or more keyword/value pairs separated by semicolons(';').
4692d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>This constructor does not canonicalize the localeID.  So, for
4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * example, "zh__pinyin" remains unchanged instead of converting
4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * to "zh@collation=pinyin".  By default ICU only recognizes the
4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * latter as specifying pinyin collation.  Use {@link #createCanonical}
4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or {@link #canonicalize} if you need to canonicalize the localeID.
4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID string representation of the locale, e.g:
4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "en_US", "sy_Cyrl_YU", "zh__pinyin", "es_ES@currency=EUR;collation=traditional"
4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 2.8
4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public ULocale(String localeID) {
4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.localeID = getName(localeID);
4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Convenience overload of ULocale(String, String, String) for
4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * compatibility with java.util.Locale.
4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #ULocale(String, String, String)
4887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.4
4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public ULocale(String a, String b) {
4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this(a, b, null);
4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Constructs a ULocale from a localeID constructed from the three 'fields' a, b, and
4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * c.  These fields are concatenated using underscores to form a localeID of the form
4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a_b_c, which is then handled like the localeID passed to <code>ULocale(String
4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * localeID)</code>.
4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Java locale strings consisting of language, country, and
5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * variant will be handled by this form, since the country code
5027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * (being shorter than four letters long) will not be interpreted
5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * as a script code.  If a script code is present, the final
5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * argument ('c') will be interpreted as the country code.  It is
5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * recommended that this constructor only be used to ease porting,
5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and that clients instead use the single-argument constructor
5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * when constructing a ULocale from a localeID.
5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param a first component of the locale id
5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param b second component of the locale id
5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param c third component of the locale id
5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #ULocale(String)
5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public ULocale(String a, String b, String c) {
5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        localeID = getName(lscvToID(a, b, c, EMPTY_STRING));
5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Creates a ULocale from the id by first canonicalizing the id.
5207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param nonCanonicalID the locale id to canonicalize
5217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the locale created from the canonical version of the ID.
5227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
5237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static ULocale createCanonical(String nonCanonicalID) {
5257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new ULocale(canonicalize(nonCanonicalID), (Locale)null);
5267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String lscvToID(String lang, String script, String country, String variant) {
5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder buf = new StringBuilder();
5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (lang != null && lang.length() > 0) {
5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(lang);
5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (script != null && script.length() > 0) {
5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(UNDERSCORE);
5367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(script);
5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (country != null && country.length() > 0) {
5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(UNDERSCORE);
5407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(country);
5417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (variant != null && variant.length() > 0) {
5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (country == null || country.length() == 0) {
5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(UNDERSCORE);
5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(UNDERSCORE);
5477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(variant);
5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return buf.toString();
5507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Converts this ULocale object to a {@link java.util.Locale}.
5542a7db240a11150ab654325587d653754c62b71a7Yoshito Umaoka     * @return a {@link java.util.Locale} that either exactly represents this object
5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or is the closest approximation.
5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 2.8
5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Locale toLocale() {
5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (locale == null) {
5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            locale = JDKLocaleHelper.toLocale(this);
5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return locale;
5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Keep our own default ULocale.
5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static Locale defaultLocale = Locale.getDefault();
5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static ULocale defaultULocale;
5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static Locale[] defaultCategoryLocales = new Locale[Category.values().length];
5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static ULocale[] defaultCategoryULocales = new ULocale[Category.values().length];
5737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static {
5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        defaultULocale = forLocale(defaultLocale);
5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // For Java 6 or older JRE, ICU initializes the default script from
5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // "user.script" system property. The system property was added
5797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // in Java 7. On JRE 7, Locale.getDefault() should reflect the
5807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // property value to the Locale's default. So ICU just relies on
5817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Locale.getDefault().
582f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Note: The "user.script" property is only used by initialization.
5842d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        //
5859713d6ad72cf9922638a2a56f61089a3db31adabroubert        if (JDKLocaleHelper.hasLocaleCategories()) {
5867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Category cat: Category.values()) {
5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int idx = cat.ordinal();
5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                defaultCategoryLocales[idx] = JDKLocaleHelper.getDefault(cat);
5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                defaultCategoryULocales[idx] = forLocale(defaultCategoryLocales[idx]);
5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Make sure the current default Locale is original.
5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // If not, it means that someone updated the default Locale.
5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // In this case, user.XXX properties are already out of date
5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // and we should not use user.script.
5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (JDKLocaleHelper.isOriginalDefaultLocale(defaultLocale)) {
5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Use "user.script" if available
5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String userScript = JDKLocaleHelper.getSystemProperty("user.script");
5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (userScript != null && LanguageTag.isScript(userScript)) {
6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Note: Builder or forLanguageTag cannot be used here
6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // when one of Locale fields is not well-formed.
6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    BaseLocale base = defaultULocale.base();
6037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    BaseLocale newBase = BaseLocale.getInstance(base.getLanguage(), userScript,
6047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            base.getRegion(), base.getVariant());
6057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    defaultULocale = getInstance(newBase, defaultULocale.extensions());
6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Java 6 or older does not have separated category locales,
6107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // use the non-category default for all
6117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Category cat: Category.values()) {
6127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int idx = cat.ordinal();
6137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                defaultCategoryLocales[idx] = defaultLocale;
6147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                defaultCategoryULocales[idx] = defaultULocale;
6157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the current default ULocale.
6217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>
6227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The default ULocale is synchronized to the default Java Locale. This method checks
6237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the current default Java Locale and returns an equivalent ULocale.
6247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>
6252a7db240a11150ab654325587d653754c62b71a7Yoshito Umaoka     * <b>Note:</b> Before Java 7, the {@link java.util.Locale} was not able to represent a
6262a7db240a11150ab654325587d653754c62b71a7Yoshito Umaoka     * locale's script. Therefore, the script field in the default ULocale is always empty unless
6277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a ULocale with non-empty script is explicitly set by {@link #setDefault(ULocale)}
6287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * on Java 6 or older systems.
6297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>
6307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <b>Note for ICU 49 or later:</b> Some JRE implementations allow users to override the default
6312a7db240a11150ab654325587d653754c62b71a7Yoshito Umaoka     * {@link java.util.Locale} using system properties - <code>user.language</code>,
6322a7db240a11150ab654325587d653754c62b71a7Yoshito Umaoka     * <code>user.country</code> and <code>user.variant</code>. In addition to these system
6332a7db240a11150ab654325587d653754c62b71a7Yoshito Umaoka     * properties, some Java 7 implementations support <code>user.script</code> for overriding the
6342a7db240a11150ab654325587d653754c62b71a7Yoshito Umaoka     * default Locale's script.
6357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * ICU 49 and later versions use the <code>user.script</code> system property on Java 6
6367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or older systems supporting other <code>user.*</code> system properties to initialize
6377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the default ULocale. The <code>user.script</code> override for default ULocale is not
6387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * used on Java 7, or if the current Java default Locale is changed after start up.
6392d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
6407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the default ULocale.
6417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 2.8
6427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static ULocale getDefault() {
6447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        synchronized (ULocale.class) {
6457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (defaultULocale == null) {
6467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // When Java's default locale has extensions (such as ja-JP-u-ca-japanese),
6477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Locale -> ULocale mapping requires BCP47 keyword mapping data that is currently
6487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // stored in a resource bundle. However, UResourceBundle currently requires
6497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // non-null default ULocale. For now, this implementation returns ULocale.ROOT
6507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // to avoid the problem.
6517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // TODO: Consider moving BCP47 mapping data out of resource bundle later.
6537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return ULocale.ROOT;
6557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Locale currentDefault = Locale.getDefault();
6577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!defaultLocale.equals(currentDefault)) {
6587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                defaultLocale = currentDefault;
6597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                defaultULocale = forLocale(currentDefault);
6607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6619713d6ad72cf9922638a2a56f61089a3db31adabroubert                if (!JDKLocaleHelper.hasLocaleCategories()) {
6627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Detected Java default Locale change.
6637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // We need to update category defaults to match the
6647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Java 7's behavior on Java 6 or older environment.
6657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (Category cat : Category.values()) {
6667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        int idx = cat.ordinal();
6677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        defaultCategoryLocales[idx] = currentDefault;
6687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        defaultCategoryULocales[idx] = forLocale(currentDefault);
6697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
6707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return defaultULocale;
6737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Sets the default ULocale.  This also sets the default Locale.
6787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If the caller does not have write permission to the
6797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * user.language property, a security exception will be thrown,
6807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and the default ULocale will remain unchanged.
6817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>
6827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * By setting the default ULocale with this method, all of the default categoy locales
6837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * are also set to the specified default ULocale.
6847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param newLocale the new default locale
6857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws SecurityException if a security manager exists and its
6867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        <code>checkPermission</code> method doesn't allow the operation.
6877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws NullPointerException if <code>newLocale</code> is null
6887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see SecurityManager#checkPermission(java.security.Permission)
6897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see java.util.PropertyPermission
6907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see ULocale#setDefault(Category, ULocale)
6917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
6927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static synchronized void setDefault(ULocale newLocale){
6947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        defaultLocale = newLocale.toLocale();
6957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Locale.setDefault(defaultLocale);
6967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        defaultULocale = newLocale;
6977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // This method also updates all category default locales
6987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Category cat : Category.values()) {
6997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            setDefault(cat, newLocale);
7007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the current default ULocale for the specified category.
7052d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
7067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param category the category
7077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the default ULocale for the specified category.
7087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 49
7097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static ULocale getDefault(Category category) {
7117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        synchronized (ULocale.class) {
7127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int idx = category.ordinal();
7137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (defaultCategoryULocales[idx] == null) {
7147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Just in case this method is called during ULocale class
7157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // initialization. Unlike getDefault(), we do not have
7167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // cyclic dependency for category default.
7177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return ULocale.ROOT;
7187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7199713d6ad72cf9922638a2a56f61089a3db31adabroubert            if (JDKLocaleHelper.hasLocaleCategories()) {
7207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Locale currentCategoryDefault = JDKLocaleHelper.getDefault(category);
7217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (!defaultCategoryLocales[idx].equals(currentCategoryDefault)) {
7227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    defaultCategoryLocales[idx] = currentCategoryDefault;
7237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    defaultCategoryULocales[idx] = forLocale(currentCategoryDefault);
7247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
7257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
7267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // java.util.Locale.setDefault(Locale) in Java 7 updates
7277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // category locale defaults. On Java 6 or older environment,
7287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // ICU4J checks if the default locale has changed and update
7297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // category ULocales here if necessary.
730f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
7317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Note: When java.util.Locale.setDefault(Locale) is called
7327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // with a Locale same with the previous one, Java 7 still
7337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // updates category locale defaults. On Java 6 or older env,
7347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // there is no good way to detect the event, ICU4J simply
7357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // check if the default Java Locale has changed since last
7367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // time.
7377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Locale currentDefault = Locale.getDefault();
7397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (!defaultLocale.equals(currentDefault)) {
7407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    defaultLocale = currentDefault;
7417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    defaultULocale = forLocale(currentDefault);
7427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (Category cat : Category.values()) {
7447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        int tmpIdx = cat.ordinal();
7457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        defaultCategoryLocales[tmpIdx] = currentDefault;
7467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        defaultCategoryULocales[tmpIdx] = forLocale(currentDefault);
7477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
7487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
749f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
7507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // No synchronization with JDK Locale, because category default
7517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // is not supported in Java 6 or older versions
7527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return defaultCategoryULocales[idx];
7547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Sets the default <code>ULocale</code> for the specified <code>Category</code>.
7597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This also sets the default <code>Locale</code> for the specified <code>Category</code>
7607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * of the JVM. If the caller does not have write permission to the
7617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * user.language property, a security exception will be thrown,
7627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and the default ULocale for the specified Category will remain unchanged.
7632d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
7647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param category the specified category to set the default locale
7657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param newLocale the new default locale
7667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see SecurityManager#checkPermission(java.security.Permission)
7677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see java.util.PropertyPermission
7687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 49
7697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static synchronized void setDefault(Category category, ULocale newLocale) {
7717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Locale newJavaDefault = newLocale.toLocale();
7727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int idx = category.ordinal();
7737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        defaultCategoryULocales[idx] = newLocale;
7747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        defaultCategoryLocales[idx] = newJavaDefault;
7757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        JDKLocaleHelper.setDefault(category, newJavaDefault);
7767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is for compatibility with Locale-- in actuality, since ULocale is
7807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * immutable, there is no reason to clone it, so this API returns 'this'.
7817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
7827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7832d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Override
7847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Object clone() {
7857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return this;
7867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the hashCode.
7907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
7917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7922d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Override
7937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int hashCode() {
7947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return localeID.hashCode();
7957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns true if the other object is another ULocale with the
7997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * same full name.
8007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Note that since names are not canonicalized, two ULocales that
8017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * function identically might not compare equal.
8027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
8037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return true if this Locale is equal to the specified object.
8047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
8057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8062d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Override
8077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean equals(Object obj) {
8087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (this == obj) {
8097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return true;
8107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (obj instanceof ULocale) {
8127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return localeID.equals(((ULocale)obj).localeID);
8137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return false;
8157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Compares two ULocale for ordering.
8199e281ba4837cba4a1cf9523d6f8b0621b150063dScott Russell     * <p><b>Note:</b> The order might change in future.
8202d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
8217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param other the ULocale to be compared.
8227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return a negative integer, zero, or a positive integer as this ULocale is less than, equal to, or greater
8237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * than the specified ULocale.
8247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws NullPointerException if <code>other</code> is null.
8252d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
826f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @stable ICU 53
8277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8282d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Override
8297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int compareTo(ULocale other) {
8307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (this == other) {
8317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return 0;
8327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int cmp = 0;
8357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Language
8377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        cmp = getLanguage().compareTo(other.getLanguage());
8387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (cmp == 0) {
8397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Script
8407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            cmp = getScript().compareTo(other.getScript());
8417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (cmp == 0) {
8427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Region
8437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                cmp = getCountry().compareTo(other.getCountry());
8447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (cmp == 0) {
8457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Variant
8467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    cmp = getVariant().compareTo(other.getVariant());
8477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (cmp == 0) {
8487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // Keywords
8497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        Iterator<String> thisKwdItr = getKeywords();
8507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        Iterator<String> otherKwdItr = other.getKeywords();
8517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (thisKwdItr == null) {
8537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            cmp = otherKwdItr == null ? 0 : -1;
8547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (otherKwdItr == null) {
8557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            cmp = 1;
8567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else {
8577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // Both have keywords
8587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            while (cmp == 0 && thisKwdItr.hasNext()) {
8597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                if (!otherKwdItr.hasNext()) {
8607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    cmp = 1;
8617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    break;
8627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
8637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                // Compare keyword keys
8647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                String thisKey = thisKwdItr.next();
8657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                String otherKey = otherKwdItr.next();
8667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                cmp = thisKey.compareTo(otherKey);
8677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                if (cmp == 0) {
8687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    // Compare keyword values
8697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    String thisVal = getKeywordValue(thisKey);
8707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    String otherVal = other.getKeywordValue(otherKey);
8717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    if (thisVal == null) {
8727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        cmp = otherVal == null ? 0 : -1;
8737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    } else if (otherVal == null) {
8747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        cmp = 1;
8757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    } else {
8767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        cmp = thisVal.compareTo(otherVal);
8777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    }
8787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
8797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
8807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (cmp == 0 && otherKwdItr.hasNext()) {
8817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                cmp = -1;
8827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
8837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
8847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
8857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
8867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
8877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Normalize the result value:
8907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Note: String.compareTo() may return value other than -1, 0, 1.
8917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // A value other than those are OK by the definition, but we don't want
8927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // associate any semantics other than negative/zero/positive.
8937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return (cmp < 0) ? -1 : ((cmp > 0) ? 1 : 0);
8947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icunote} Unlike the Locale API, this returns an array of <code>ULocale</code>,
8987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * not <code>Locale</code>.  Returns a list of all installed locales.
8997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
9007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static ULocale[] getAvailableLocales() {
9027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return ICUResourceBundle.getAvailableULocales();
9037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a list of all 2-letter country codes defined in ISO 3166.
9077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Can be used to create Locales.
9087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
9097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String[] getISOCountries() {
9117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleIDs.getISOCountries();
9127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a list of all 2-letter language codes defined in ISO 639.
9167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Can be used to create Locales.
9177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * [NOTE:  ISO 639 is not a stable standard-- some languages' codes have changed.
9187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The list this function returns includes both the new and the old codes for the
9197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * languages whose codes have changed.]
9207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
9217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String[] getISOLanguages() {
9237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleIDs.getISOLanguages();
9247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the language code for this locale, which will either be the empty string
9287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or a lowercase ISO 639 code.
9297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayLanguage()
9307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayLanguage(ULocale)
9317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
9327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getLanguage() {
9347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return base().getLanguage();
9357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the language code for the locale ID,
9397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * which will either be the empty string
9407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or a lowercase ISO 639 code.
9417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayLanguage()
9427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayLanguage(ULocale)
9437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
9447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getLanguage(String localeID) {
9467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new LocaleIDParser(localeID).getLanguage();
9477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the script code for this locale, which might be the empty string.
9517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayScript()
9527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayScript(ULocale)
9537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
9547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getScript() {
9567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return base().getScript();
9577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the script code for the specified locale, which might be the empty
9617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * string.
9627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayScript()
9637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayScript(ULocale)
9647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
9657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getScript(String localeID) {
9677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new LocaleIDParser(localeID).getScript();
9687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the country/region code for this locale, which will either be the empty string
9727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or an uppercase ISO 3166 2-letter code.
9737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayCountry()
9747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayCountry(ULocale)
9757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
9767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getCountry() {
9787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return base().getRegion();
9797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the country/region code for this locale, which will either be the empty string
9837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or an uppercase ISO 3166 2-letter code.
9847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID The locale identification string.
9857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayCountry()
9867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayCountry(ULocale)
9877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
9887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getCountry(String localeID) {
9907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new LocaleIDParser(localeID).getCountry();
9917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
99487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * {@icu} Get the region to use for supplemental data lookup.
99587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * Uses
99687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * (1) any region specified by locale tag "rg"; if none then
99787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * (2) any unicode_region_tag in the locale ID; if none then
99887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * (3) if inferRegion is TRUE, the region suggested by
99987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     *     getLikelySubtags on the localeID.
100087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * If no region is found, returns empty string ""
100187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     *
100287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * @param locale
100387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     *     The locale (includes any keywords) from which
100487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     *     to get the region to use for supplemental data.
100587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * @param inferRegion
100687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     *     If TRUE, will try to infer region from other
100787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     *     locale elements if not found any other way.
100887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * @return
100987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     *     String with region to use ("" if none found).
101087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * @internal ICU 57
101187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * @deprecated This API is ICU internal only.
101287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     */
101387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert    @Deprecated
101487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert    public static String getRegionForSupplementalData(
101587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert                            ULocale locale, boolean inferRegion) {
101687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert        String region = locale.getKeywordValue("rg");
101787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert        if (region != null && region.length() == 6) {
101887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert            String regionUpper = AsciiUtil.toUpperString(region);
101987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert            if (regionUpper.endsWith("ZZZZ")) {
102087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert            	return regionUpper.substring(0,2);
102187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert            }
102287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert        }
102387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert        region = locale.getCountry();
102487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert        if (region.length() == 0 && inferRegion) {
102587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert            ULocale maximized = addLikelySubtags(locale);
102687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert            region = maximized.getCountry();
102787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert        }
102887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert        return region;
102987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert    }
103087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert
103187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert    /**
10327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the variant code for this locale, which might be the empty string.
10337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayVariant()
10347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayVariant(ULocale)
10357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
10367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getVariant() {
10387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return base().getVariant();
10397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the variant code for the specified locale, which might be the empty string.
10437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayVariant()
10447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getDisplayVariant(ULocale)
10457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
10467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getVariant(String localeID) {
10487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new LocaleIDParser(localeID).getVariant();
10497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the fallback locale for the specified locale, which might be the
10537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * empty string.
10547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.2
10557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getFallback(String localeID) {
10577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getFallbackString(getName(localeID));
10587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the fallback locale for this locale.  If this locale is root,
10627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * returns null.
10637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.2
10647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public ULocale getFallback() {
10667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (localeID.length() == 0 || localeID.charAt(0) == '@') {
10677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
10687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new ULocale(getFallbackString(localeID), (Locale)null);
10707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the given (canonical) locale id minus the last part before the tags.
10747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String getFallbackString(String fallback) {
10767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int extStart = fallback.indexOf('@');
10777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (extStart == -1) {
10787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            extStart = fallback.length();
10797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int last = fallback.lastIndexOf('_', extStart);
10817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (last == -1) {
10827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            last = 0;
10837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
10847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // truncate empty segment
10857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            while (last > 0) {
10867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (fallback.charAt(last - 1) != '_') {
10877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
10887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
10897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                last--;
10907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return fallback.substring(0, last) + fallback.substring(extStart);
10937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the (normalized) base name for this locale,
10977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * like {@link #getName()}, but without keywords.
10987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
10997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the base name as a String.
11007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
11017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
11027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getBaseName() {
11037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getBaseName(localeID);
11047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
11057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
11077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the (normalized) base name for the specified locale,
11087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * like {@link #getName(String)}, but without keywords.
11097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
11107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the locale ID as a string
11117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the base name as a String.
11127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
11137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
11147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getBaseName(String localeID){
11157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (localeID.indexOf('@') == -1) {
11167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return localeID;
11177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new LocaleIDParser(localeID).getBaseName();
11197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
11207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
11227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the (normalized) full name for this locale.
11237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
11247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return String the full name of the localeID
11257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
11267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
11277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getName() {
11287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return localeID; // always normalized
11297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1130f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
11317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
11327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Gets the shortest length subtag's size.
11337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
11347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID
11357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The size of the shortest length subtag
11367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     **/
11377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static int getShortestSubtagLength(String localeID) {
11387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int localeIDLength = localeID.length();
11397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int length = localeIDLength;
11407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean reset = true;
11417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int tmpLength = 0;
1142f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
11437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < localeIDLength; i++) {
11447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (localeID.charAt(i) != '_' && localeID.charAt(i) != '-') {
11457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (reset) {
11467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    reset = false;
11477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    tmpLength = 0;
11487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
11497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                tmpLength++;
11507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
11517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (tmpLength != 0 && tmpLength < length) {
11527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    length = tmpLength;
11537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
11547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                reset = true;
11557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1157f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
11587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return length;
11597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
11607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
11627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the (normalized) full name for the specified locale.
11637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
11647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the localeID as a string
11657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return String the full name of the localeID
11667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
11677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
11687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getName(String localeID){
11697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String tmpLocaleID;
11707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Convert BCP47 id if necessary
11717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (localeID != null && !localeID.contains("@") && getShortestSubtagLength(localeID) == 1) {
11727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tmpLocaleID = forLanguageTag(localeID).getName();
11737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (tmpLocaleID.length() == 0) {
11747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                tmpLocaleID = localeID;
11757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
11777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tmpLocaleID = localeID;
11787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11792d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        return nameCache.getInstance(tmpLocaleID, null /* unused */);
11807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
11817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
11837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a string representation of this object.
11847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
11857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
11862d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Override
11877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String toString() {
11887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return localeID;
11897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
11907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
11927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns an iterator over keywords for this locale.  If there
11937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * are no keywords, returns null.
11947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return iterator over keywords, or null if there are no keywords.
11957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
11967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
11977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Iterator<String> getKeywords() {
11987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getKeywords(localeID);
11997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
12007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
12027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns an iterator over keywords for the specified locale.  If there
12037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * are no keywords, returns null.
12047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return an iterator over the keywords in the specified locale, or null
12057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * if there are no keywords.
12067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
12077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
12087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static Iterator<String> getKeywords(String localeID){
12097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new LocaleIDParser(localeID).getKeywords();
12107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
12117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
12137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the value for a keyword in this locale. If the keyword is not
12147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * defined, returns null.
12157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keywordName name of the keyword whose value is desired. Case insensitive.
12167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the value of the keyword, or null.
12177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
12187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
12197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getKeywordValue(String keywordName){
12207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getKeywordValue(localeID, keywordName);
12217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
12227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
12247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the value for a keyword in the specified locale. If the keyword is
12257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * not defined, returns null.  The locale name does not need to be normalized.
12267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keywordName name of the keyword whose value is desired. Case insensitive.
12277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return String the value of the keyword as a string
12287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
12297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
12307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getKeywordValue(String localeID, String keywordName) {
12317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new LocaleIDParser(localeID).getKeywordValue(keywordName);
12327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
12337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
12357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the canonical name for the specified locale ID.  This is used to
12367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * convert POSIX and other grandfathered IDs to standard ICU form.
12377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the locale id
12387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the canonicalized id
12397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
12407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
12417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String canonicalize(String localeID){
12427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        LocaleIDParser parser = new LocaleIDParser(localeID, true);
12437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String baseName = parser.getBaseName();
12447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean foundVariant = false;
12457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // formerly, we always set to en_US_POSIX if the basename was empty, but
12477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // now we require that the entire id be empty, so that "@foo=bar"
12487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // will pass through unchanged.
12497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // {dlf} I'd rather keep "" unchanged.
12507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (localeID.equals("")) {
12517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return "";
1252f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            //              return "en_US_POSIX";
12537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
12547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // we have an ID in the form xx_Yyyy_ZZ_KKKKK
12567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        initCANONICALIZE_MAP();
12587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* convert the variants to appropriate ID */
12607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < variantsToKeywords.length; i++) {
12617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String[] vals = variantsToKeywords[i];
12627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int idx = baseName.lastIndexOf("_" + vals[0]);
12637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (idx > -1) {
12647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                foundVariant = true;
12657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                baseName = baseName.substring(0, idx);
12677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (baseName.endsWith("_")) {
12687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    baseName = baseName.substring(0, --idx);
12697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
12707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                parser.setBaseName(baseName);
12717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                parser.defaultKeywordValue(vals[1], vals[2]);
12727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
12737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
12757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* See if this is an already known locale */
12777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < CANONICALIZE_MAP.length; i++) {
12787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (CANONICALIZE_MAP[i][0].equals(baseName)) {
12797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                foundVariant = true;
12807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String[] vals = CANONICALIZE_MAP[i];
12827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                parser.setBaseName(vals[1]);
12837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (vals[2] != null) {
12847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    parser.defaultKeywordValue(vals[2], vals[3]);
12857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
12867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
12877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
12897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* total mondo hack for Norwegian, fortunately the main NY case is handled earlier */
12917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!foundVariant) {
12927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (parser.getLanguage().equals("nb") && parser.getVariant().equals("NY")) {
12937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                parser.setBaseName(lscvToID("nn", parser.getScript(), parser.getCountry(), null));
12947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
12967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return parser.getName();
12987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
12997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
13017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Given a keyword and a value, return a new locale with an updated
13027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * keyword and value.  If the keyword is null, this removes all keywords from the locale id.
13037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Otherwise, if the value is null, this removes the value for this keyword from the
13047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * locale id.  Otherwise, this adds/replaces the value for this keyword in the locale id.
13057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The keyword and value must not be empty.
13067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
13077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Related: {@link #getBaseName()} returns the locale ID string with all keywords removed.
13087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
13097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword to add/remove, or null to remove all keywords.
13107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param value the value to add/set, or null to remove this particular keyword.
13117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the updated locale
13127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.2
13137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
13147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public ULocale setKeywordValue(String keyword, String value) {
13157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new ULocale(setKeywordValue(localeID, keyword, value), (Locale)null);
13167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
13177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
13197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Given a locale id, a keyword, and a value, return a new locale id with an updated
13207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * keyword and value.  If the keyword is null, this removes all keywords from the locale id.
13217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Otherwise, if the value is null, this removes the value for this keyword from the
13227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * locale id.  Otherwise, this adds/replaces the value for this keyword in the locale id.
13237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The keyword and value must not be empty.
13247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
13257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Related: {@link #getBaseName(String)} returns the locale ID string with all keywords removed.
13267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
13277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the locale id to modify
13287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword to add/remove, or null to remove all keywords.
13297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param value the value to add/set, or null to remove this particular keyword.
13307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the updated locale id
13317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.2
13327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
13337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String setKeywordValue(String localeID, String keyword, String value) {
13347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        LocaleIDParser parser = new LocaleIDParser(localeID);
13357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        parser.setKeywordValue(keyword, value);
13367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return parser.getName();
13377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
13387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
13407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Given a locale id, a keyword, and a value, return a new locale id with an updated
13417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * keyword and value, if the keyword does not already have a value.  The keyword and
13427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * value must not be null or empty.
13437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the locale id to modify
13447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword to add, if not already present
13457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param value the value to add, if not already present
13467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the updated locale id
13477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1348f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    /*    private static String defaultKeywordValue(String localeID, String keyword, String value) {
13497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        LocaleIDParser parser = new LocaleIDParser(localeID);
13507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        parser.defaultKeywordValue(keyword, value);
13517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return parser.getName();
13527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }*/
13537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
13557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a three-letter abbreviation for this locale's language.  If the locale
13567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * doesn't specify a language, returns the empty string.  Otherwise, returns
13577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a lowercase ISO 639-2/T language code.
13587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The ISO 639-2 language codes can be found on-line at
13597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
13607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @exception MissingResourceException Throws MissingResourceException if the
13617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * three-letter language abbreviation is not available for this locale.
13627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
13637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
13647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getISO3Language(){
13657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getISO3Language(localeID);
13667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
13677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
13697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a three-letter abbreviation for this locale's language.  If the locale
13707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * doesn't specify a language, returns the empty string.  Otherwise, returns
13717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a lowercase ISO 639-2/T language code.
13727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The ISO 639-2 language codes can be found on-line at
13737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
13747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @exception MissingResourceException Throws MissingResourceException if the
13757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * three-letter language abbreviation is not available for this locale.
13767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
13777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
13787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getISO3Language(String localeID) {
13797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleIDs.getISO3Language(getLanguage(localeID));
13807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
13817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
13837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a three-letter abbreviation for this locale's country/region.  If the locale
13847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * doesn't specify a country, returns the empty string.  Otherwise, returns
13857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * an uppercase ISO 3166 3-letter country code.
13867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @exception MissingResourceException Throws MissingResourceException if the
13877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * three-letter country abbreviation is not available for this locale.
13887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
13897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
13907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getISO3Country() {
13917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getISO3Country(localeID);
13927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
13937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
13957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a three-letter abbreviation for this locale's country/region.  If the locale
13967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * doesn't specify a country, returns the empty string.  Otherwise, returns
13977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * an uppercase ISO 3166 3-letter country code.
13987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @exception MissingResourceException Throws MissingResourceException if the
13997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * three-letter country abbreviation is not available for this locale.
14007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
14017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
14027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getISO3Country(String localeID) {
14037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleIDs.getISO3Country(getCountry(localeID));
14047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
14057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
14077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Pairs of (language subtag, + or -) for finding out fast if common languages
14087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * are LTR (minus) or RTL (plus).
14097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
14107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String LANG_DIR_STRING =
14117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            "root-en-es-pt-zh-ja-ko-de-fr-it-ar+he+fa+ru-nl-pl-th-tr-";
14127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
14147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns whether this locale's script is written right-to-left.
14157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If there is no script subtag, then the likely script is used,
14167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * see {@link #addLikelySubtags(ULocale)}.
14177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If no likely script is known, then false is returned.
14187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
14197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>A script is right-to-left according to the CLDR script metadata
14207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * which corresponds to whether the script's letters have Bidi_Class=R or AL.
14217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
14227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Returns true for "ar" and "en-Hebr", false for "zh" and "fa-Cyrl".
14237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
14247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return true if the locale's script is written right-to-left
1425bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * @stable ICU 54
14267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
14277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean isRightToLeft() {
14287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String script = getScript();
14297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (script.length() == 0) {
14307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Fastpath: We know the likely scripts and their writing direction
14317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // for some common languages.
14327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String lang = getLanguage();
14337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (lang.length() == 0) {
14347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return false;
14357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int langIndex = LANG_DIR_STRING.indexOf(lang);
14377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (langIndex >= 0) {
14387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                switch (LANG_DIR_STRING.charAt(langIndex + lang.length())) {
14397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                case '-': return false;
14407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                case '+': return true;
14417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                default: break;  // partial match of a longer code
14427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
14437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Otherwise, find the likely script.
14457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ULocale likely = addLikelySubtags(this);
14467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            script = likely.getScript();
14477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (script.length() == 0) {
14487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return false;
14497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int scriptCode = UScript.getCodeFromName(script);
14527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return UScript.isRightToLeft(scriptCode);
14537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
14547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // display names
14567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
14587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns this locale's language localized for display in the default <code>DISPLAY</code> locale.
14597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized language name.
14607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#DISPLAY
14617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
14627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
14637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayLanguage() {
14647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayLanguageInternal(this, getDefault(Category.DISPLAY), false);
14657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
14667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
14687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns this locale's language localized for display in the provided locale.
14697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the name.
14707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized language name.
14717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
14727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
14737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayLanguage(ULocale displayLocale) {
14747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayLanguageInternal(this, displayLocale, false);
14757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
14767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
14787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a locale's language localized for display in the provided locale.
14797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
14807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the id of the locale whose language will be displayed
14817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocaleID the id of the locale in which to display the name.
14827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized language name.
14837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
14847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
14857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayLanguage(String localeID, String displayLocaleID) {
14867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayLanguageInternal(new ULocale(localeID), new ULocale(displayLocaleID),
14877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                false);
14887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
14897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
14917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a locale's language localized for display in the provided locale.
14927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
14937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the id of the locale whose language will be displayed.
14947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the name.
14957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized language name.
14967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
14977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
14987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayLanguage(String localeID, ULocale displayLocale) {
14997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, false);
15007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
15027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns this locale's language localized for display in the default <code>DISPLAY</code> locale.
15037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If a dialect name is present in the data, then it is returned.
15047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized language name.
15057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#DISPLAY
15067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
15077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
15087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayLanguageWithDialect() {
15097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayLanguageInternal(this, getDefault(Category.DISPLAY), true);
15107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
15137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns this locale's language localized for display in the provided locale.
15147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If a dialect name is present in the data, then it is returned.
15157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the name.
15167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized language name.
15177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
15187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
15197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayLanguageWithDialect(ULocale displayLocale) {
15207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayLanguageInternal(this, displayLocale, true);
15217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
15247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a locale's language localized for display in the provided locale.
15257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If a dialect name is present in the data, then it is returned.
15267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
15277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the id of the locale whose language will be displayed
15287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocaleID the id of the locale in which to display the name.
15297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized language name.
15307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
15317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
15327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayLanguageWithDialect(String localeID, String displayLocaleID) {
15337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayLanguageInternal(new ULocale(localeID), new ULocale(displayLocaleID),
15347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                true);
15357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
15387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a locale's language localized for display in the provided locale.
15397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If a dialect name is present in the data, then it is returned.
15407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
15417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the id of the locale whose language will be displayed.
15427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the name.
15437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized language name.
15447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
15457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
15467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayLanguageWithDialect(String localeID, ULocale displayLocale) {
15477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, true);
15487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String getDisplayLanguageInternal(ULocale locale, ULocale displayLocale,
15517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean useDialect) {
15527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String lang = useDialect ? locale.getBaseName() : locale.getLanguage();
15537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleDisplayNames.getInstance(displayLocale).languageDisplayName(lang);
15547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
15577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns this locale's script localized for display in the default <code>DISPLAY</code> locale.
15587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized script name.
15597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#DISPLAY
15607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
15617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
15627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayScript() {
15637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayScriptInternal(this, getDefault(Category.DISPLAY));
15647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
15677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns this locale's script localized for display in the default <code>DISPLAY</code> locale.
15687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized script name.
15697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#DISPLAY
15707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
15717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
15727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
15737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
15747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayScriptInContext() {
15757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayScriptInContextInternal(this, getDefault(Category.DISPLAY));
15767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
15797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns this locale's script localized for display in the provided locale.
15807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the name.
15817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized script name.
15827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
15837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
15847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayScript(ULocale displayLocale) {
15857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayScriptInternal(this, displayLocale);
15867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
15897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns this locale's script localized for display in the provided locale.
15907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the name.
15917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized script name.
15927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
15937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
15947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
15957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
15967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayScriptInContext(ULocale displayLocale) {
15977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayScriptInContextInternal(this, displayLocale);
15987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
16017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a locale's script localized for display in the provided locale.
16027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
16037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the id of the locale whose script will be displayed
16047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocaleID the id of the locale in which to display the name.
16057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized script name.
16067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
16077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
16087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayScript(String localeID, String displayLocaleID) {
16097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayScriptInternal(new ULocale(localeID), new ULocale(displayLocaleID));
16107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
16127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a locale's script localized for display in the provided locale.
16137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
16147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the id of the locale whose script will be displayed
16157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocaleID the id of the locale in which to display the name.
16167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized script name.
16177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
16187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
16197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
16207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
16217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayScriptInContext(String localeID, String displayLocaleID) {
16227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayScriptInContextInternal(new ULocale(localeID), new ULocale(displayLocaleID));
16237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
16267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a locale's script localized for display in the provided locale.
16277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the id of the locale whose script will be displayed.
16287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the name.
16297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized script name.
16307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
16317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
16327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayScript(String localeID, ULocale displayLocale) {
16337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayScriptInternal(new ULocale(localeID), displayLocale);
16347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
16367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a locale's script localized for display in the provided locale.
16377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the id of the locale whose script will be displayed.
16387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the name.
16397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized script name.
16407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
16417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
16427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
16437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
16447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayScriptInContext(String localeID, ULocale displayLocale) {
16457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayScriptInContextInternal(new ULocale(localeID), displayLocale);
16467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // displayLocaleID is canonical, localeID need not be since parsing will fix this.
16497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String getDisplayScriptInternal(ULocale locale, ULocale displayLocale) {
16507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleDisplayNames.getInstance(displayLocale)
1651f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                .scriptDisplayName(locale.getScript());
16527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String getDisplayScriptInContextInternal(ULocale locale, ULocale displayLocale) {
16557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleDisplayNames.getInstance(displayLocale)
1656f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                .scriptDisplayNameInContext(locale.getScript());
16577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
16607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns this locale's country localized for display in the default <code>DISPLAY</code> locale.
1661f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR").
1662f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead.
16637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized country name.
16647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#DISPLAY
16657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
16667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
16677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayCountry() {
16687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayCountryInternal(this, getDefault(Category.DISPLAY));
16697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
16727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns this locale's country localized for display in the provided locale.
1673f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR").
1674f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead.
16757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the name.
16767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized country name.
16777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
16787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
16797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayCountry(ULocale displayLocale){
16807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayCountryInternal(this, displayLocale);
16817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
16847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a locale's country localized for display in the provided locale.
1685f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR").
1686f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead.
16877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
16887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the id of the locale whose country will be displayed
16897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocaleID the id of the locale in which to display the name.
16907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized country name.
16917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
16927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
16937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayCountry(String localeID, String displayLocaleID) {
16947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayCountryInternal(new ULocale(localeID), new ULocale(displayLocaleID));
16957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
16987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a locale's country localized for display in the provided locale.
1699f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR").
1700f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead.
17017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
17027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the id of the locale whose country will be displayed.
17037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the name.
17047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized country name.
17057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
17067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayCountry(String localeID, ULocale displayLocale) {
17087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayCountryInternal(new ULocale(localeID), displayLocale);
17097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // displayLocaleID is canonical, localeID need not be since parsing will fix this.
17127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String getDisplayCountryInternal(ULocale locale, ULocale displayLocale) {
17137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleDisplayNames.getInstance(displayLocale)
1714f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                .regionDisplayName(locale.getCountry());
17157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
17187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns this locale's variant localized for display in the default <code>DISPLAY</code> locale.
17197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized variant name.
17207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#DISPLAY
17217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
17227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayVariant() {
17247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayVariantInternal(this, getDefault(Category.DISPLAY));
17257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
17287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns this locale's variant localized for display in the provided locale.
17297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the name.
17307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized variant name.
17317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
17327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayVariant(ULocale displayLocale) {
17347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayVariantInternal(this, displayLocale);
17357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
17387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a locale's variant localized for display in the provided locale.
17397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
17407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the id of the locale whose variant will be displayed
17417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocaleID the id of the locale in which to display the name.
17427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized variant name.
17437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
17447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayVariant(String localeID, String displayLocaleID){
17467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayVariantInternal(new ULocale(localeID), new ULocale(displayLocaleID));
17477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
17507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a locale's variant localized for display in the provided locale.
17517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
17527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the id of the locale whose variant will be displayed.
17537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the name.
17547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized variant name.
17557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
17567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayVariant(String localeID, ULocale displayLocale) {
17587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayVariantInternal(new ULocale(localeID), displayLocale);
17597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String getDisplayVariantInternal(ULocale locale, ULocale displayLocale) {
17627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleDisplayNames.getInstance(displayLocale)
1763f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                .variantDisplayName(locale.getVariant());
17647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
17677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a keyword localized for display in the default <code>DISPLAY</code> locale.
17687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword to be displayed.
17697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized keyword name.
17707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getKeywords()
17717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#DISPLAY
17727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
17737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayKeyword(String keyword) {
17757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayKeywordInternal(keyword, getDefault(Category.DISPLAY));
17767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
17797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a keyword localized for display in the specified locale.
17807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword to be displayed.
17817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocaleID the id of the locale in which to display the keyword.
17827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized keyword name.
17837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getKeywords(String)
17847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
17857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayKeyword(String keyword, String displayLocaleID) {
17877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayKeywordInternal(keyword, new ULocale(displayLocaleID));
17887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
17917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a keyword localized for display in the specified locale.
17927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword to be displayed.
17937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the keyword.
17947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized keyword name.
17957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getKeywords(String)
17967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
17977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayKeyword(String keyword, ULocale displayLocale) {
17997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayKeywordInternal(keyword, displayLocale);
18007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String getDisplayKeywordInternal(String keyword, ULocale displayLocale) {
18037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleDisplayNames.getInstance(displayLocale).keyDisplayName(keyword);
18047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
18077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a keyword value localized for display in the default <code>DISPLAY</code> locale.
18087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword whose value is to be displayed.
18097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized value name.
18107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#DISPLAY
18117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
18127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayKeywordValue(String keyword) {
18147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayKeywordValueInternal(this, keyword, getDefault(Category.DISPLAY));
18157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
18187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a keyword value localized for display in the specified locale.
18197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword whose value is to be displayed.
18207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the value.
18217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized value name.
18227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
18237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayKeywordValue(String keyword, ULocale displayLocale) {
18257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayKeywordValueInternal(this, keyword, displayLocale);
18267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
18297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a keyword value localized for display in the specified locale.
18307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
18317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the id of the locale whose keyword value is to be displayed.
18327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword whose value is to be displayed.
18337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocaleID the id of the locale in which to display the value.
18347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized value name.
18357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
18367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayKeywordValue(String localeID, String keyword,
18387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String displayLocaleID) {
18397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayKeywordValueInternal(new ULocale(localeID), keyword,
18407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                new ULocale(displayLocaleID));
18417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
18447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns a keyword value localized for display in the specified locale.
18457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
18467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the id of the locale whose keyword value is to be displayed.
18477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword whose value is to be displayed.
18487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the id of the locale in which to display the value.
18497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized value name.
18507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
18517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayKeywordValue(String localeID, String keyword,
18537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ULocale displayLocale) {
18547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayKeywordValueInternal(new ULocale(localeID), keyword, displayLocale);
18557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // displayLocaleID is canonical, localeID need not be since parsing will fix this.
18587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String getDisplayKeywordValueInternal(ULocale locale, String keyword,
18597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ULocale displayLocale) {
18607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        keyword = AsciiUtil.toLowerString(keyword.trim());
18617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String value = locale.getKeywordValue(keyword);
18627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleDisplayNames.getInstance(displayLocale).keyValueDisplayName(keyword, value);
18637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
18667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns this locale name localized for display in the default <code>DISPLAY</code> locale.
18677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized locale name.
18687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#DISPLAY
18697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
18707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayName() {
18727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayNameInternal(this, getDefault(Category.DISPLAY));
18737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
18767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns this locale name localized for display in the provided locale.
18777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the locale name.
18787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized locale name.
18797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
18807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayName(ULocale displayLocale) {
18827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayNameInternal(this, displayLocale);
18837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
18867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the locale ID localized for display in the provided locale.
18877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
18887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the locale whose name is to be displayed.
18897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocaleID the id of the locale in which to display the locale name.
18907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized locale name.
18917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
18927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayName(String localeID, String displayLocaleID) {
18947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayNameInternal(new ULocale(localeID), new ULocale(displayLocaleID));
18957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
18987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the locale ID localized for display in the provided locale.
18997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
19007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the locale whose name is to be displayed.
19017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the locale name.
19027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized locale name.
19037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.0
19047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayName(String localeID, ULocale displayLocale) {
19067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayNameInternal(new ULocale(localeID), displayLocale);
19077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String getDisplayNameInternal(ULocale locale, ULocale displayLocale) {
19107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleDisplayNames.getInstance(displayLocale).localeDisplayName(locale);
19117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
19147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns this locale name localized for display in the default <code>DISPLAY</code> locale.
19157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If a dialect name is present in the locale data, then it is returned.
19167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized locale name.
19177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#DISPLAY
19187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
19197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayNameWithDialect() {
19217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayNameWithDialectInternal(this, getDefault(Category.DISPLAY));
19227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
19257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns this locale name localized for display in the provided locale.
19267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If a dialect name is present in the locale data, then it is returned.
19277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the locale name.
19287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized locale name.
19297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
19307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getDisplayNameWithDialect(ULocale displayLocale) {
19327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayNameWithDialectInternal(this, displayLocale);
19337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
19367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the locale ID localized for display in the provided locale.
19377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If a dialect name is present in the locale data, then it is returned.
19387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
19397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the locale whose name is to be displayed.
19407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocaleID the id of the locale in which to display the locale name.
19417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized locale name.
19427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
19437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayNameWithDialect(String localeID, String displayLocaleID) {
19457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayNameWithDialectInternal(new ULocale(localeID),
19467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                new ULocale(displayLocaleID));
19477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
19507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns the locale ID localized for display in the provided locale.
19517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If a dialect name is present in the locale data, then it is returned.
19527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a cover for the ICU4C API.
19537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID the locale whose name is to be displayed.
19547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param displayLocale the locale in which to display the locale name.
19557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the localized locale name.
19567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
19577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String getDisplayNameWithDialect(String localeID, ULocale displayLocale) {
19597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getDisplayNameWithDialectInternal(new ULocale(localeID), displayLocale);
19607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String getDisplayNameWithDialectInternal(ULocale locale, ULocale displayLocale) {
19637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleDisplayNames.getInstance(displayLocale, DialectHandling.DIALECT_NAMES)
1964f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                .localeDisplayName(locale);
19657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
19687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns this locale's layout orientation for characters.  The possible
19697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * values are "left-to-right", "right-to-left", "top-to-bottom" or
19707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "bottom-to-top".
19717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The locale's layout orientation for characters.
19727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
19737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getCharacterOrientation() {
19752d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        return ICUResourceTableAccess.getTableString(ICUData.ICU_BASE_NAME, this,
19762d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                "layout", "characters", "characters");
19777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
19807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Returns this locale's layout orientation for lines.  The possible
19817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * values are "left-to-right", "right-to-left", "top-to-bottom" or
19827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "bottom-to-top".
19837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The locale's layout orientation for lines.
19847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
19857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getLineOrientation() {
19872d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        return ICUResourceTableAccess.getTableString(ICUData.ICU_BASE_NAME, this,
19882d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                "layout", "lines", "lines");
19897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
19927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Selector for <tt>getLocale()</tt> indicating the locale of the
19937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * resource containing the data.  This is always at or above the
19947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * valid locale.  If the valid locale does not contain the
19957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * specific data being requested, then the actual locale will be
19967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * above the valid locale.  If the object was not constructed from
19977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * locale data, then the valid locale is <i>null</i>.
19987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
19997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @draft ICU 2.8 (retain)
20007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @provisional This API might change or be removed in a future release.
20017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
20027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static Type ACTUAL_LOCALE = new Type();
20037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
20057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Selector for <tt>getLocale()</tt> indicating the most specific
20067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * locale for which any data exists.  This is always at or above
20077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the requested locale, and at or below the actual locale.  If
20087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the requested locale does not correspond to any resource data,
20097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * then the valid locale will be above the requested locale.  If
20107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the object was not constructed from locale data, then the
20117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * actual locale is <i>null</i>.
20127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
20137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Note: The valid locale will be returned correctly in ICU
20147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3.0 or later.  In ICU 2.8, it is not returned correctly.
20157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @draft ICU 2.8 (retain)
20167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @provisional This API might change or be removed in a future release.
20177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
20187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static Type VALID_LOCALE = new Type();
20197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
20217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Opaque selector enum for <tt>getLocale()</tt>.
20227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.ULocale
20237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
20247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.ULocale#VALID_LOCALE
20257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @draft ICU 2.8 (retainAll)
20267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @provisional This API might change or be removed in a future release.
20277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
20287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final class Type {
20297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private Type() {}
20307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
20317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2032f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    /**
2033f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * {@icu} Based on a HTTP formatted list of acceptable locales, determine an available
2034f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * locale for the user.  NullPointerException is thrown if acceptLanguageList or
2035f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * availableLocales is null.  If fallback is non-null, it will contain true if a
2036f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * fallback locale (one not in the acceptLanguageList) was returned.  The value on
2037f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * entry is ignored.  ULocale will be one of the locales in availableLocales, or the
2038f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
2039f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * availableLocales matched).  No ULocale array element should be null; behavior is
2040f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * undefined if this is the case.
2041f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
2042f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @param availableLocales list of available locales. One of these will be returned.
2043f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @param fallback if non-null, a 1-element array containing a boolean to be set with
2044f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * the fallback status
2045f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @return one of the locales from the availableLocales list, or null if none match
2046f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @stable ICU 3.4
2047f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     */
20487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static ULocale acceptLanguage(String acceptLanguageList, ULocale[] availableLocales,
2049f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            boolean[] fallback) {
20507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (acceptLanguageList == null) {
20517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new NullPointerException();
20527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
20537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ULocale acceptList[] = null;
20547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
20557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            acceptList = parseAcceptLanguage(acceptLanguageList, true);
20567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch (ParseException pe) {
20577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            acceptList = null;
20587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
20597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (acceptList == null) {
20607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
20617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
20627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return acceptLanguage(acceptList, availableLocales, fallback);
20637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
20647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2066f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * {@icu} Based on a list of acceptable locales, determine an available locale for the
2067f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * user.  NullPointerException is thrown if acceptLanguageList or availableLocales is
2068f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * null.  If fallback is non-null, it will contain true if a fallback locale (one not
2069f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * in the acceptLanguageList) was returned.  The value on entry is ignored.  ULocale
2070f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * will be one of the locales in availableLocales, or the ROOT ULocale if if a ROOT
2071f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * locale was used as a fallback (because nothing else in availableLocales matched).
2072f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * No ULocale array element should be null; behavior is undefined if this is the case.
2073f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @param acceptLanguageList list of acceptable locales
2074f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @param availableLocales list of available locales. One of these will be returned.
2075f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @param fallback if non-null, a 1-element array containing a boolean to be set with
2076f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * the fallback status
2077f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @return one of the locales from the availableLocales list, or null if none match
2078f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @stable ICU 3.4
2079f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     */
20807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static ULocale acceptLanguage(ULocale[] acceptLanguageList, ULocale[]
2082f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            availableLocales, boolean[] fallback) {
20837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // fallbacklist
20847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int i,j;
20857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(fallback != null) {
20867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fallback[0]=true;
20877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
20887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for(i=0;i<acceptLanguageList.length;i++) {
20897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ULocale aLocale = acceptLanguageList[i];
20907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean[] setFallback = fallback;
20917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            do {
20927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for(j=0;j<availableLocales.length;j++) {
20937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if(availableLocales[j].equals(aLocale)) {
20947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if(setFallback != null) {
20957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            setFallback[0]=false; // first time with this locale - not a fallback.
20967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
20977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        return availableLocales[j];
20987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
20997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // compare to scriptless alias, so locales such as
21007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // zh_TW, zh_CN are considered as available locales - see #7190
21017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (aLocale.getScript().length() == 0
21027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            && availableLocales[j].getScript().length() > 0
21037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            && availableLocales[j].getLanguage().equals(aLocale.getLanguage())
21047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            && availableLocales[j].getCountry().equals(aLocale.getCountry())
21057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            && availableLocales[j].getVariant().equals(aLocale.getVariant())) {
21067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        ULocale minAvail = ULocale.minimizeSubtags(availableLocales[j]);
21077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (minAvail.getScript().length() == 0) {
21087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if(setFallback != null) {
21097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                setFallback[0] = false; // not a fallback.
21107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
21117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            return aLocale;
21127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
21137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
21147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
21157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Locale loc = aLocale.toLocale();
21167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Locale parent = LocaleUtility.fallback(loc);
21177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if(parent != null) {
21187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    aLocale = new ULocale(parent);
21197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
21207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    aLocale = null;
21217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
21227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                setFallback = null; // Do not set fallback in later iterations
21237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } while (aLocale != null);
21247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
21257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return null;
21267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
21277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2128f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    /**
2129f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * {@icu} Based on a HTTP formatted list of acceptable locales, determine an available
2130f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * locale for the user.  NullPointerException is thrown if acceptLanguageList or
2131f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * availableLocales is null.  If fallback is non-null, it will contain true if a
2132f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * fallback locale (one not in the acceptLanguageList) was returned.  The value on
2133f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * entry is ignored.  ULocale will be one of the locales in availableLocales, or the
2134f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
2135f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * availableLocales matched).  No ULocale array element should be null; behavior is
2136f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * undefined if this is the case.  This function will choose a locale from the
2137f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * ULocale.getAvailableLocales() list as available.
2138f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
2139f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @param fallback if non-null, a 1-element array containing a boolean to be set with
2140f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * the fallback status
2141f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @return one of the locales from the ULocale.getAvailableLocales() list, or null if
2142f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * none match
2143f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @stable ICU 3.4
2144f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     */
21457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static ULocale acceptLanguage(String acceptLanguageList, boolean[] fallback) {
21467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
2147f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                fallback);
2148f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    }
2149f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
2150f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    /**
2151f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * {@icu} Based on an ordered array of acceptable locales, determine an available
2152f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * locale for the user.  NullPointerException is thrown if acceptLanguageList or
2153f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * availableLocales is null.  If fallback is non-null, it will contain true if a
2154f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * fallback locale (one not in the acceptLanguageList) was returned.  The value on
2155f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * entry is ignored.  ULocale will be one of the locales in availableLocales, or the
2156f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
2157f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * availableLocales matched).  No ULocale array element should be null; behavior is
2158f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * undefined if this is the case.  This function will choose a locale from the
2159f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * ULocale.getAvailableLocales() list as available.
2160f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @param acceptLanguageList ordered array of acceptable locales (preferred are listed first)
2161f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @param fallback if non-null, a 1-element array containing a boolean to be set with
2162f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * the fallback status
2163f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @return one of the locales from the ULocale.getAvailableLocales() list, or null if none match
2164f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @stable ICU 3.4
2165f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     */
21667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static ULocale acceptLanguage(ULocale[] acceptLanguageList, boolean[] fallback) {
21677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
21687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                fallback);
21697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
21707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
21717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
21727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Package local method used for parsing Accept-Language string
21737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
21742d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    static ULocale[] parseAcceptLanguage(String acceptLanguage, boolean isLenient)
2175f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            throws ParseException {
21767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        class ULocaleAcceptLanguageQ implements Comparable<ULocaleAcceptLanguageQ> {
21777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            private double q;
21787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            private double serial;
21797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            public ULocaleAcceptLanguageQ(double theq, int theserial) {
21807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                q = theq;
21817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                serial = theserial;
21827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
21832d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            @Override
21847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            public int compareTo(ULocaleAcceptLanguageQ other) {
21857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (q > other.q) { // reverse - to sort in descending order
21867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return -1;
21877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (q < other.q) {
21887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return 1;
21897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
21907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (serial < other.serial) {
21917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return -1;
21927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (serial > other.serial) {
21937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return 1;
21947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
21957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return 0; // same object
21967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
21977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
21987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
21997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
22007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // parse out the acceptLanguage into an array
22012d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        TreeMap<ULocaleAcceptLanguageQ, ULocale> map =
2202f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                new TreeMap<ULocaleAcceptLanguageQ, ULocale>();
22037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder languageRangeBuf = new StringBuilder();
22047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder qvalBuf = new StringBuilder();
22057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int state = 0;
22067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        acceptLanguage += ","; // append comma to simplify the parsing code
22077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int n;
22087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean subTag = false;
22097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean q1 = false;
22107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (n = 0; n < acceptLanguage.length(); n++) {
22117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean gotLanguageQ = false;
22127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            char c = acceptLanguage.charAt(n);
22137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            switch (state) {
22147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case 0: // before language-range start
22157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
22167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // in language-range
22177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    languageRangeBuf.append(c);
22187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = 1;
22197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    subTag = false;
22207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == '*') {
22217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    languageRangeBuf.append(c);
22227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = 2;
22237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c != ' ' && c != '\t') {
22247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // invalid character
22257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = -1;
22267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
22277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
22287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case 1: // in language-range
22297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
22307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    languageRangeBuf.append(c);
22317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == '-') {
22327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    subTag = true;
22337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    languageRangeBuf.append(c);
22347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == '_') {
22357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (isLenient) {
22367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        subTag = true;
22377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        languageRangeBuf.append(c);
22387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
22397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        state = -1;
22407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
22417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if ('0' <= c && c <= '9') {
22427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (subTag) {
22437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        languageRangeBuf.append(c);
22447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
22457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // DIGIT is allowed only in language sub tag
22467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        state = -1;
22477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
22487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == ',') {
22497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // language-q end
22507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    gotLanguageQ = true;
22517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == ' ' || c == '\t') {
22527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // language-range end
22537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = 3;
22547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == ';') {
22557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // before q
22567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = 4;
22577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
22587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // invalid character for language-range
22597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = -1;
22607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
22617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
22627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case 2: // saw wild card range
22637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (c == ',') {
22647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // language-q end
22657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    gotLanguageQ = true;
22667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == ' ' || c == '\t') {
22677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // language-range end
22687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = 3;
22697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == ';') {
22707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // before q
22717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = 4;
22727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
22737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // invalid
22747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = -1;
22757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
22767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
22777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case 3: // language-range end
22787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (c == ',') {
22797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // language-q end
22807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    gotLanguageQ = true;
22817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == ';') {
22827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // before q
22837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state =4;
22847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c != ' ' && c != '\t') {
22857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // invalid
22867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = -1;
22877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
22887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
22897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case 4: // before q
22907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (c == 'q') {
22917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // before equal
22927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = 5;
22937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c != ' ' && c != '\t') {
22947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // invalid
22957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = -1;
22967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
22977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
22987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case 5: // before equal
22997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (c == '=') {
23007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // before q value
23017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = 6;
23027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c != ' ' && c != '\t') {
23037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // invalid
23047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = -1;
23057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
23067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
23077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case 6: // before q value
23087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (c == '0') {
23097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // q value start with 0
23107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    q1 = false;
23117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    qvalBuf.append(c);
23127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = 7;
23137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == '1') {
23147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // q value start with 1
23157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    qvalBuf.append(c);
23167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = 7;
23177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == '.') {
23187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (isLenient) {
23197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        qvalBuf.append(c);
23207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        state = 8;
23217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
23227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        state = -1;
23237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
23247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c != ' ' && c != '\t') {
23257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // invalid
23267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = -1;
23277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
23287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
23297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case 7: // q value start
23307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (c == '.') {
23317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // before q value fraction part
23327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    qvalBuf.append(c);
23337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = 8;
23347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == ',') {
23357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // language-q end
23367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    gotLanguageQ = true;
23377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == ' ' || c == '\t') {
23387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // after q value
23397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = 10;
23407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
23417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // invalid
23427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = -1;
23437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
23447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
23457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case 8: // before q value fraction part
23462d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                if ('0' <= c && c <= '9') {
23477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (q1 && c != '0' && !isLenient) {
23487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // if q value starts with 1, the fraction part must be 0
23497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        state = -1;
23507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
23517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // in q value fraction part
23527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        qvalBuf.append(c);
23537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        state = 9;
23547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
23557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
23567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // invalid
23577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = -1;
23587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
23597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
23607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case 9: // in q value fraction part
23617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ('0' <= c && c <= '9') {
23627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (q1 && c != '0') {
23637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // if q value starts with 1, the fraction part must be 0
23647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        state = -1;
23657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
23667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        qvalBuf.append(c);
23677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
23687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == ',') {
23697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // language-q end
23707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    gotLanguageQ = true;
23717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c == ' ' || c == '\t') {
23727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // after q value
23737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = 10;
23747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
23757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // invalid
23767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = -1;
23777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
23787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
23797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case 10: // after q value
23807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (c == ',') {
23817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // language-q end
23827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    gotLanguageQ = true;
23837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (c != ' ' && c != '\t') {
23847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // invalid
23857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    state = -1;
23867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
23877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
23887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
23897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (state == -1) {
23907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // error state
23917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new ParseException("Invalid Accept-Language", n);
23927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
23937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (gotLanguageQ) {
23947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                double q = 1.0;
23957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (qvalBuf.length() != 0) {
23967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    try {
23977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        q = Double.parseDouble(qvalBuf.toString());
23987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } catch (NumberFormatException nfe) {
23997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // Already validated, so it should never happen
24007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        q = 1.0;
24017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
24027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (q > 1.0) {
24037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        q = 1.0;
24047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
24057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
24067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (languageRangeBuf.charAt(0) != '*') {
24077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int serial = map.size();
24087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ULocaleAcceptLanguageQ entry = new ULocaleAcceptLanguageQ(q, serial);
24097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // sort in reverse order..   1.0, 0.9, 0.8 .. etc
24107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    map.put(entry, new ULocale(canonicalize(languageRangeBuf.toString())));
24117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
24127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
24137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // reset buffer and parse state
24147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                languageRangeBuf.setLength(0);
24157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                qvalBuf.setLength(0);
24167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                state = 0;
24177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
24187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
24197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (state != 0) {
24207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Well, the parser should handle all cases.  So just in case.
24217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new ParseException("Invalid AcceptlLanguage", n);
24227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
24237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
24247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // pull out the map
24257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ULocale acceptList[] = map.values().toArray(new ULocale[map.size()]);
24267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return acceptList;
24277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
24287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
24297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String UNDEFINED_LANGUAGE = "und";
24307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String UNDEFINED_SCRIPT = "Zzzz";
24317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String UNDEFINED_REGION = "ZZ";
24327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
24337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
24347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Adds the likely subtags for a provided locale ID, per the algorithm
24357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * described in the following CLDR technical report:
24367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
24377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   http://www.unicode.org/reports/tr35/#Likely_Subtags
24387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
24397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If the provided ULocale instance is already in the maximal form, or there is no
24407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * data available available for maximization, it will be returned.  For example,
24417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "und-Zzzz" cannot be maximized, since there is no reasonable maximization.
24427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Otherwise, a new ULocale instance with the maximal form is returned.
24437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
24447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Examples:
24457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
24467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "en" maximizes to "en_Latn_US"
24477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
24487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "de" maximizes to "de_Latn_US"
24497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
24507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "sr" maximizes to "sr_Cyrl_RS"
24517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
24527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.)
24537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
24547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.)
24557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
24567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param loc The ULocale to maximize
24577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The maximized ULocale instance.
24587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
24597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
24607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static ULocale addLikelySubtags(ULocale loc) {
24617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String[] tags = new String[3];
24627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String trailing = null;
24637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
24647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int trailingIndex = parseTagString(
2465f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                loc.localeID,
2466f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                tags);
24677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
24687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (trailingIndex < loc.localeID.length()) {
24697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            trailing = loc.localeID.substring(trailingIndex);
24707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
24717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
24727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String newLocaleID =
2473f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                createLikelySubtagsString(
2474f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        tags[0],
2475f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        tags[1],
2476f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        tags[2],
2477f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        trailing);
24787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
24797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return newLocaleID == null ? loc : new ULocale(newLocaleID);
24807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
24817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
24827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
24837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Minimizes the subtags for a provided locale ID, per the algorithm described
24847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * in the following CLDR technical report:<blockquote>
24857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
24867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   <a href="http://www.unicode.org/reports/tr35/#Likely_Subtags"
24877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *>http://www.unicode.org/reports/tr35/#Likely_Subtags</a></blockquote>
24887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
24897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If the provided ULocale instance is already in the minimal form, or there
24907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * is no data available for minimization, it will be returned.  Since the
24917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * minimization algorithm relies on proper maximization, see the comments
24927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * for addLikelySubtags for reasons why there might not be any data.
24937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
24947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Examples:<pre>
24957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
24967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "en_Latn_US" minimizes to "en"
24977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
24987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "de_Latn_US" minimizes to "de"
24997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
25007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "sr_Cyrl_RS" minimizes to "sr"
25017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
25027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "zh_Hant_TW" minimizes to "zh_TW" (The region is preferred to the
25037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * script, and minimizing to "zh" would imply "zh_Hans_CN".) </pre>
25047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
25057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param loc The ULocale to minimize
25067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The minimized ULocale instance.
25077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
25087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
25097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static ULocale minimizeSubtags(ULocale loc) {
25102d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        return minimizeSubtags(loc, Minimize.FAVOR_REGION);
25112d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    }
2512f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
25132d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    /**
25142d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * Options for minimizeSubtags.
2515f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @internal
2516f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @deprecated This API is ICU internal only.
25172d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     */
2518f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    @Deprecated
25192d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    public enum Minimize {
25202d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        /**
2521f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert         * Favor including the script, when either the region <b>or</b> the script could be suppressed, but not both.
2522f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert         * @internal
2523f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert         * @deprecated This API is ICU internal only.
2524f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert         */
2525f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        @Deprecated
25262d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        FAVOR_SCRIPT,
2527f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        /**
2528f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert         * Favor including the region, when either the region <b>or</b> the script could be suppressed, but not both.
2529f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert         * @internal
2530f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert         * @deprecated This API is ICU internal only.
2531f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert         */
2532f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        @Deprecated
25332d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        FAVOR_REGION
25342d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    }
25352d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
25362d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    /**
25372d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * {@icu} Minimizes the subtags for a provided locale ID, per the algorithm described
25382d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * in the following CLDR technical report:<blockquote>
25392d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
25402d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *   <a href="http://www.unicode.org/reports/tr35/#Likely_Subtags"
25412d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *>http://www.unicode.org/reports/tr35/#Likely_Subtags</a></blockquote>
25422d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
25432d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * If the provided ULocale instance is already in the minimal form, or there
25442d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * is no data available for minimization, it will be returned.  Since the
25452d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * minimization algorithm relies on proper maximization, see the comments
25462d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * for addLikelySubtags for reasons why there might not be any data.
25472d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
25482d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * Examples:<pre>
25492d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
25502d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * "en_Latn_US" minimizes to "en"
25512d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
25522d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * "de_Latn_US" minimizes to "de"
25532d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
25542d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * "sr_Cyrl_RS" minimizes to "sr"
25552d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
25562d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * "zh_Hant_TW" minimizes to "zh_TW" if fieldToFavor == {@link Minimize#FAVOR_REGION}
25572d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * "zh_Hant_TW" minimizes to "zh_Hant" if fieldToFavor == {@link Minimize#FAVOR_SCRIPT}
25582d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * </pre>
25592d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * The fieldToFavor only has an effect if either the region or the script could be suppressed, but not both.
25602d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * @param loc The ULocale to minimize
25612d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * @param fieldToFavor Indicate which should be preferred, when either the region <b>or</b> the script could be suppressed, but not both.
25622d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * @return The minimized ULocale instance.
2563f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @internal
2564f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @deprecated This API is ICU internal only.
25652d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     */
2566f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    @Deprecated
25672d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    public static ULocale minimizeSubtags(ULocale loc, Minimize fieldToFavor) {
25687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String[] tags = new String[3];
25697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
25707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int trailingIndex = parseTagString(
25717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                loc.localeID,
25727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                tags);
25737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
25747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String originalLang = tags[0];
25757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String originalScript = tags[1];
25767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String originalRegion = tags[2];
25777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String originalTrailing = null;
25787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
25797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (trailingIndex < loc.localeID.length()) {
25807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /*
25817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * Create a String that contains everything
25827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * after the language, script, and region.
25837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
25847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            originalTrailing = loc.localeID.substring(trailingIndex);
25857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
25867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
25877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
25887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * First, we need to first get the maximization
25897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * by adding any likely subtags.
25907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         **/
25917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String maximizedLocaleID =
2592f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                createLikelySubtagsString(
2593f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        originalLang,
2594f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        originalScript,
2595f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        originalRegion,
2596f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        null);
25977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
25987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
25997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * If maximization fails, there's nothing
26007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * we can do.
26017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         **/
26027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (isEmptyString(maximizedLocaleID)) {
26037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return loc;
26047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
26057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else {
26067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /**
26077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * Start first with just the language.
26087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             **/
26097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String tag =
2610f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    createLikelySubtagsString(
2611f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            originalLang,
2612f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            null,
2613f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            null,
2614f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            null);
26157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
26167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (tag.equals(maximizedLocaleID)) {
26177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String newLocaleID =
2618f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        createTagString(
2619f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                                originalLang,
2620f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                                null,
2621f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                                null,
2622f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                                originalTrailing);
26237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
26247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return new ULocale(newLocaleID);
26257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
26267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
26277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
26287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
26297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Next, try the language and region.
26307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         **/
26312d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        if (fieldToFavor == Minimize.FAVOR_REGION) {
26322d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            if (originalRegion.length() != 0) {
26332d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                String tag =
26342d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        createLikelySubtagsString(
26352d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                originalLang,
26362d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                null,
26372d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                originalRegion,
26382d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                null);
26392d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
26402d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                if (tag.equals(maximizedLocaleID)) {
26412d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    String newLocaleID =
26422d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                            createTagString(
26432d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    originalLang,
26442d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    null,
26452d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    originalRegion,
26462d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    originalTrailing);
26472d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
26482d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    return new ULocale(newLocaleID);
26492d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                }
26502d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            }
26512d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            if (originalScript.length() != 0){
26522d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                String tag =
26532d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        createLikelySubtagsString(
26542d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                originalLang,
26552d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                originalScript,
26562d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                null,
26572d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                null);
26582d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
26592d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                if (tag.equals(maximizedLocaleID)) {
26602d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    String newLocaleID =
26612d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                            createTagString(
26622d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    originalLang,
26632d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    originalScript,
26642d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    null,
26652d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    originalTrailing);
26662d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
26672d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    return new ULocale(newLocaleID);
26682d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                }
26692d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            }
26702d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        } else { // FAVOR_SCRIPT, so
26712d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            if (originalScript.length() != 0){
26722d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                String tag =
26732d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        createLikelySubtagsString(
26742d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                originalLang,
26752d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                originalScript,
26762d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                null,
26772d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                null);
26782d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
26792d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                if (tag.equals(maximizedLocaleID)) {
26802d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    String newLocaleID =
26812d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                            createTagString(
26822d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    originalLang,
26832d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    originalScript,
26842d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    null,
26852d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    originalTrailing);
26862d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
26872d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    return new ULocale(newLocaleID);
26882d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                }
26892d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            }
26902d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            if (originalRegion.length() != 0) {
26912d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                String tag =
26922d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        createLikelySubtagsString(
26932d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                originalLang,
26942d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                null,
26952d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                originalRegion,
26962d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                null);
26972d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
26982d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                if (tag.equals(maximizedLocaleID)) {
26992d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    String newLocaleID =
27002d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                            createTagString(
27012d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    originalLang,
27022d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    null,
27032d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    originalRegion,
27042d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    originalTrailing);
27052d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
27062d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    return new ULocale(newLocaleID);
27072d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                }
27082d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            }
27092d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
27107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return loc;
27117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
27127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
27137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
27147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * A trivial utility function that checks for a null
27157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * reference or checks the length of the supplied String.
27167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
27177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   @param string The string to check
27187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
27197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   @return true if the String is empty, or if the reference is null.
27207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
27217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static boolean isEmptyString(String string) {
2722f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        return string == null || string.length() == 0;
27237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
27247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
27257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
27267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Append a tag to a StringBuilder, adding the separator if necessary.The tag must
27277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * not be a zero-length string.
27287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
27297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param tag The tag to add.
27307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param buffer The output buffer.
27317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     **/
27327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static void appendTag(String tag, StringBuilder buffer) {
27337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (buffer.length() != 0) {
27347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buffer.append(UNDERSCORE);
27357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
27367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
27377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buffer.append(tag);
27387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
27397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
27407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
27417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Create a tag string from the supplied parameters.  The lang, script and region
27427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * parameters may be null references.
27437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
27447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If any of the language, script or region parameters are empty, and the alternateTags
27457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * parameter is not null, it will be parsed for potential language, script and region tags
27467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * to be used when constructing the new tag.  If the alternateTags parameter is null, or
27477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * it contains no language tag, the default tag for the unknown language is used.
27487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
27497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param lang The language tag to use.
27507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param script The script tag to use.
27517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param region The region tag to use.
27527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param trailing Any trailing data to append to the new tag.
27537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param alternateTags A string containing any alternate tags.
27547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The new tag string.
27557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     **/
27567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String createTagString(String lang, String script, String region,
2757f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            String trailing, String alternateTags) {
27587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
27597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        LocaleIDParser parser = null;
27607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean regionAppended = false;
27617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
27627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder tag = new StringBuilder();
27637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
27647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!isEmptyString(lang)) {
27657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            appendTag(
2766f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    lang,
2767f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    tag);
27687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
27697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else if (isEmptyString(alternateTags)) {
27707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /*
27717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * Append the value for an unknown language, if
27727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * we found no language.
27737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
27747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            appendTag(
2775f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    UNDEFINED_LANGUAGE,
2776f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    tag);
27777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
27787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else {
27797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            parser = new LocaleIDParser(alternateTags);
27807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
27817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String alternateLang = parser.getLanguage();
27827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
27837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /*
27847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * Append the value for an unknown language, if
27857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * we found no language.
27867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
27877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            appendTag(
2788f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    !isEmptyString(alternateLang) ? alternateLang : UNDEFINED_LANGUAGE,
2789f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            tag);
27907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
27917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
27927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!isEmptyString(script)) {
27937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            appendTag(
2794f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    script,
2795f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    tag);
27967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
27977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else if (!isEmptyString(alternateTags)) {
27987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /*
27997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * Parse the alternateTags string for the script.
28007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
28017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (parser == null) {
28027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                parser = new LocaleIDParser(alternateTags);
28037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
28047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
28057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String alternateScript = parser.getScript();
28067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
28077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!isEmptyString(alternateScript)) {
28087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                appendTag(
2809f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        alternateScript,
2810f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        tag);
28117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
28127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
28137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
28147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!isEmptyString(region)) {
28157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            appendTag(
2816f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    region,
2817f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    tag);
28187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
28197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            regionAppended = true;
28207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
28217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else if (!isEmptyString(alternateTags)) {
28227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /*
28237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * Parse the alternateTags string for the region.
28247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
28257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (parser == null) {
28267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                parser = new LocaleIDParser(alternateTags);
28277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
28287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
28297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String alternateRegion = parser.getCountry();
28307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
28317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!isEmptyString(alternateRegion)) {
28327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                appendTag(
2833f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        alternateRegion,
2834f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        tag);
28357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
28367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                regionAppended = true;
28377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
28387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
28397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
28407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (trailing != null && trailing.length() > 1) {
28417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /*
28427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * The current ICU format expects two underscores
28437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * will separate the variant from the preceeding
28447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * parts of the tag, if there is no region.
28457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
28467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int separators = 0;
28477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
28487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (trailing.charAt(0) == UNDERSCORE) {
28497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (trailing.charAt(1) == UNDERSCORE) {
28507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    separators = 2;
28517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2852f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            }
2853f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            else {
2854f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                separators = 1;
2855f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            }
28567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
28577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (regionAppended) {
28587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                /*
28597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 * If we appended a region, we may need to strip
28607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 * the extra separator from the variant portion.
28617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 */
28627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (separators == 2) {
28637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    tag.append(trailing.substring(1));
28647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
28657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                else {
28667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    tag.append(trailing);
28677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
28687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
28697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            else {
28707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                /*
28717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 * If we did not append a region, we may need to add
28727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 * an extra separator to the variant portion.
28737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 */
28747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (separators == 1) {
28757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    tag.append(UNDERSCORE);
28767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
28777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                tag.append(trailing);
28787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
28797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
28807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
28817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return tag.toString();
28827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
28837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
28847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
28857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Create a tag string from the supplied parameters.  The lang, script and region
28867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * parameters may be null references.If the lang parameter is an empty string, the
28877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * default value for an unknown language is written to the output buffer.
28887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
28897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param lang The language tag to use.
28907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param script The script tag to use.
28917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param region The region tag to use.
28927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param trailing Any trailing data to append to the new tag.
28937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The new String.
28947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     **/
28957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static String createTagString(String lang, String script, String region, String trailing) {
28967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return createTagString(lang, script, region, trailing, null);
28977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
28987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
28997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
29007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Parse the language, script, and region subtags from a tag string, and return the results.
29017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
29027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This function does not return the canonical strings for the unknown script and region.
29037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
29047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param localeID The locale ID to parse.
29057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param tags An array of three String references to return the subtag strings.
29067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The number of chars of the localeID parameter consumed.
29077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     **/
29087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static int parseTagString(String localeID, String tags[]) {
29097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        LocaleIDParser parser = new LocaleIDParser(localeID);
29107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String lang = parser.getLanguage();
29127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String script = parser.getScript();
29137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String region = parser.getCountry();
29147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (isEmptyString(lang)) {
29167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tags[0] = UNDEFINED_LANGUAGE;
29177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
29187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else {
29197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tags[0] = lang;
29207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
29217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (script.equals(UNDEFINED_SCRIPT)) {
29237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tags[1] = "";
29247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
29257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else {
29267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tags[1] = script;
29277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
29287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (region.equals(UNDEFINED_REGION)) {
29307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tags[2] = "";
29317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
29327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else {
29337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tags[2] = region;
29347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
29357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /*
29377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Search for the variant.  If there is one, then return the index of
29387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * the preceeding separator.
29397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * If there's no variant, search for the keyword delimiter,
29407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * and return its index.  Otherwise, return the length of the
29417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * string.
29427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
29437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * $TOTO(dbertoni) we need to take into account that we might
29447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * find a part of the language as the variant, since it can
29457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * can have a variant portion that is long enough to contain
29467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * the same characters as the variant.
29477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
29487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String variant = parser.getVariant();
29497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!isEmptyString(variant)){
29517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int index = localeID.indexOf(variant);
29527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return  index > 0 ? index - 1 : index;
29557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
29567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else
29577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {
29587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int index = localeID.indexOf('@');
29597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return index == -1 ? localeID.length() : index;
29617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
29627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
29637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String lookupLikelySubtags(String localeId) {
29657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        UResourceBundle bundle =
2966f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                UResourceBundle.getBundleInstance(
29672d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        ICUData.ICU_BASE_NAME, "likelySubtags");
29687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
29697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return bundle.getString(localeId);
29707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
29717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        catch(MissingResourceException e) {
29727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
29737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
29747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
29757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String createLikelySubtagsString(String lang, String script, String region,
2977f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            String variants) {
29787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
29807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Try the language with the script and region first.
29817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
29827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!isEmptyString(script) && !isEmptyString(region)) {
29837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String searchTag =
2985f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    createTagString(
2986f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            lang,
2987f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            script,
2988f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            region,
2989f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            null);
29907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String likelySubtags = lookupLikelySubtags(searchTag);
29927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
29937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /*
29947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (likelySubtags == null) {
29957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (likelySubtags2 != null) {
29967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    System.err.println("Tag mismatch: \"(null)\" \"" + likelySubtags2 + "\"");
29977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
29987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
29997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            else if (likelySubtags2 == null) {
30007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                System.err.println("Tag mismatch: \"" + likelySubtags + "\" \"(null)\"");
30017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
30027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            else if (!likelySubtags.equals(likelySubtags2)) {
30032d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                System.err.println("Tag mismatch: \"" + likelySubtags + "\" \"" + likelySubtags2
30047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    + "\"");
30057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3006f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert             */
30077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (likelySubtags != null) {
30087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Always use the language tag from the
30097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // maximal string, since it may be more
30107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // specific than the one provided.
30117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return createTagString(
3012f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        null,
3013f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        null,
3014f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        null,
3015f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        variants,
3016f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        likelySubtags);
30177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
30187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
30197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
30207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
30217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Try the language with just the script.
30227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         **/
30237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!isEmptyString(script)) {
30247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
30257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String searchTag =
3026f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    createTagString(
3027f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            lang,
3028f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            script,
3029f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            null,
3030f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            null);
30317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
30327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String likelySubtags = lookupLikelySubtags(searchTag);
30337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (likelySubtags != null) {
30347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Always use the language tag from the
30357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // maximal string, since it may be more
30367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // specific than the one provided.
30377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return createTagString(
3038f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        null,
3039f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        null,
3040f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        region,
3041f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        variants,
3042f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        likelySubtags);
30437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
30447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
30457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
30467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
30477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Try the language with just the region.
30487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         **/
30497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!isEmptyString(region)) {
30507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
30517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String searchTag =
3052f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    createTagString(
3053f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            lang,
3054f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            null,
3055f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            region,
3056f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            null);
30577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
30587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String likelySubtags = lookupLikelySubtags(searchTag);
30597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
30607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (likelySubtags != null) {
30617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Always use the language tag from the
30627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // maximal string, since it may be more
30637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // specific than the one provided.
30647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return createTagString(
3065f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        null,
3066f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        script,
3067f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        null,
3068f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        variants,
3069f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        likelySubtags);
30707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
30717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
30727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
30737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
30747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Finally, try just the language.
30757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         **/
30767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {
30777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String searchTag =
3078f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    createTagString(
3079f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            lang,
3080f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            null,
3081f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            null,
3082f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            null);
30837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
30847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String likelySubtags = lookupLikelySubtags(searchTag);
30857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
30867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (likelySubtags != null) {
30877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Always use the language tag from the
30887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // maximal string, since it may be more
30897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // specific than the one provided.
30907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return createTagString(
3091f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        null,
3092f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        script,
3093f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        region,
3094f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        variants,
3095f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        likelySubtags);
30967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
30977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
30987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
30997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return null;
31007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
31017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
31027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // --------------------------------
31037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //      BCP47/OpenJDK APIs
31047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // --------------------------------
31057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
31067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
31077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The key for the private use locale extension ('x').
31087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
31097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getExtension(char)
31107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Builder#setExtension(char, String)
31117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
31127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.2
31137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
31147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final char PRIVATE_USE_EXTENSION = 'x';
31157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
31167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
31177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The key for Unicode locale extension ('u').
31187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
31197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #getExtension(char)
31207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Builder#setExtension(char, String)
31217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
31227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.2
31237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
31247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final char UNICODE_LOCALE_EXTENSION = 'u';
31257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
31267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
31277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the extension (or private use) value associated with
31287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the specified key, or null if there is no extension
31297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * associated with the key. To be well-formed, the key must be one
31307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * of <code>[0-9A-Za-z]</code>. Keys are case-insensitive, so
31317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * for example 'z' and 'Z' represent the same extension.
31327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
31337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param key the extension key
31347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The extension, or null if this locale defines no
31357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * extension for the specified key.
31367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException if key is not well-formed
31377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #PRIVATE_USE_EXTENSION
31387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #UNICODE_LOCALE_EXTENSION
31397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
31407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.2
31417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
31427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getExtension(char key) {
31437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!LocaleExtensions.isValidKey(key)) {
31447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("Invalid extension key: " + key);
31457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
31467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return extensions().getExtensionValue(key);
31477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
31487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
31497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
31507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the set of extension keys associated with this locale, or the
31517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * empty set if it has no extensions. The returned set is unmodifiable.
31527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The keys will all be lower-case.
31537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
31547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the set of extension keys, or the empty set if this locale has
31557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * no extensions
31567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.2
31577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
31587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Set<Character> getExtensionKeys() {
31597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return extensions().getKeys();
31607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
31617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
31627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
31637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the set of unicode locale attributes associated with
31647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * this locale, or the empty set if it has no attributes. The
31657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * returned set is unmodifiable.
31667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
31677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The set of attributes.
31687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.6
31697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
31707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Set<String> getUnicodeLocaleAttributes() {
31717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return extensions().getUnicodeLocaleAttributes();
31727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
31737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
31747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
31757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the Unicode locale type associated with the specified Unicode locale key
31767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * for this locale. Returns the empty string for keys that are defined with no type.
31777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns null if the key is not defined. Keys are case-insensitive. The key must
31787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * be two alphanumeric characters ([0-9a-zA-Z]), or an IllegalArgumentException is
31797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * thrown.
31807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
31817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param key the Unicode locale key
31827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The Unicode locale type associated with the key, or null if the
31837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * locale does not define the key.
31847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException if the key is not well-formed
31857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws NullPointerException if <code>key</code> is null
31862d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
31877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
31887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
31897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getUnicodeLocaleType(String key) {
31907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!LocaleExtensions.isValidUnicodeLocaleKey(key)) {
31917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("Invalid Unicode locale key: " + key);
31927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
31937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return extensions().getUnicodeLocaleType(key);
31947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
31957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
31967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
31977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the set of Unicode locale keys defined by this locale, or the empty set if
31987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * this locale has none.  The returned set is immutable.  Keys are all lower case.
31997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
32007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The set of Unicode locale keys, or the empty set if this locale has
32017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * no Unicode locale keywords.
32022d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
32037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
32047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
32057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Set<String> getUnicodeLocaleKeys() {
32067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return extensions().getUnicodeLocaleKeys();
32077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
32087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
32097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
32107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a well-formed IETF BCP 47 language tag representing
32117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * this locale.
32127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
32137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>If this <code>ULocale</code> has a language, script, country, or
32147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * variant that does not satisfy the IETF BCP 47 language tag
32157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * syntax requirements, this method handles these fields as
32167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * described below:
32177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
32187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p><b>Language:</b> If language is empty, or not well-formed
32197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * (for example "a" or "e2"), it will be emitted as "und" (Undetermined).
32207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
32217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p><b>Script:</b> If script is not well-formed (for example "12"
32227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or "Latin"), it will be omitted.
32232d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
32247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p><b>Country:</b> If country is not well-formed (for example "12"
32257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or "USA"), it will be omitted.
32267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
32277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p><b>Variant:</b> If variant <b>is</b> well-formed, each sub-segment
32287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * (delimited by '-' or '_') is emitted as a subtag.  Otherwise:
32297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <ul>
32307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
32317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <li>if all sub-segments match <code>[0-9a-zA-Z]{1,8}</code>
32327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * (for example "WIN" or "Oracle_JDK_Standard_Edition"), the first
32337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * ill-formed sub-segment and all following will be appended to
32347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the private use subtag.  The first appended subtag will be
32357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "lvariant", followed by the sub-segments in order, separated by
32367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * hyphen. For example, "x-lvariant-WIN",
32377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "Oracle-x-lvariant-JDK-Standard-Edition".
32387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
32397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <li>if any sub-segment does not match
32407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <code>[0-9a-zA-Z]{1,8}</code>, the variant will be truncated
32417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and the problematic sub-segment and all following sub-segments
32427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * will be omitted.  If the remainder is non-empty, it will be
32437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * emitted as a private use subtag as above (even if the remainder
32447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * turns out to be well-formed).  For example,
32457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "Solaris_isjustthecoolestthing" is emitted as
32467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "x-lvariant-Solaris", not as "solaris".</li></ul>
32477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
32487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p><b>Note:</b> Although the language tag created by this
32497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * method is well-formed (satisfies the syntax requirements
32507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * defined by the IETF BCP 47 specification), it is not
32517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * necessarily a valid BCP 47 language tag.  For example,
32527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <pre>
32537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   new Locale("xx", "YY").toLanguageTag();</pre>
32542d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
32557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * will return "xx-YY", but the language subtag "xx" and the
32567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * region subtag "YY" are invalid because they are not registered
32577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * in the IANA Language Subtag Registry.
32587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
32597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return a BCP47 language tag representing the locale
32607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #forLanguageTag(String)
32617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
32627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.2
32637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
32647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String toLanguageTag() {
32657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        BaseLocale base = base();
32667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        LocaleExtensions exts = extensions();
32677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
32687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (base.getVariant().equalsIgnoreCase("POSIX")) {
32697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // special handling for variant POSIX
32707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            base = BaseLocale.getInstance(base.getLanguage(), base.getScript(), base.getRegion(), "");
32717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (exts.getUnicodeLocaleType("va") == null) {
32727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // add va-posix
32737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                InternalLocaleBuilder ilocbld = new InternalLocaleBuilder();
32747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                try {
32757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ilocbld.setLocale(BaseLocale.ROOT, exts);
32767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ilocbld.setUnicodeLocaleKeyword("va", "posix");
32777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    exts = ilocbld.getLocaleExtensions();
32787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (LocaleSyntaxException e) {
32797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // this should not happen
32807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    throw new RuntimeException(e);
32817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
32827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
32837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
32847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
32857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        LanguageTag tag = LanguageTag.parseLocale(base, exts);
32867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
32877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder buf = new StringBuilder();
32887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String subtag = tag.getLanguage();
32897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (subtag.length() > 0) {
32907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(LanguageTag.canonicalizeLanguage(subtag));
32917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3292f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
32937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        subtag = tag.getScript();
32947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (subtag.length() > 0) {
32957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(LanguageTag.SEP);
32967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(LanguageTag.canonicalizeScript(subtag));
32977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
32987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
32997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        subtag = tag.getRegion();
33007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (subtag.length() > 0) {
33017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(LanguageTag.SEP);
33027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(LanguageTag.canonicalizeRegion(subtag));
33037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
33047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
33057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        List<String>subtags = tag.getVariants();
33067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (String s : subtags) {
33077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(LanguageTag.SEP);
33087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(LanguageTag.canonicalizeVariant(s));
33097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
33107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
33117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        subtags = tag.getExtensions();
33127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (String s : subtags) {
33137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(LanguageTag.SEP);
33147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(LanguageTag.canonicalizeExtension(s));
33157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
33167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
33177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        subtag = tag.getPrivateuse();
33187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (subtag.length() > 0) {
33197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (buf.length() > 0) {
33207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(LanguageTag.SEP);
33217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
33227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(LanguageTag.PRIVATEUSE).append(LanguageTag.SEP);
33237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(LanguageTag.canonicalizePrivateuse(subtag));
33247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
33257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
33267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return buf.toString();
33277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
33287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
33297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
33307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a locale for the specified IETF BCP 47 language tag string.
33317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
33327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>If the specified language tag contains any ill-formed subtags,
33337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the first such subtag and all following subtags are ignored.  Compare
33347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * to {@link ULocale.Builder#setLanguageTag} which throws an exception
33357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * in this case.
33367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
33379e281ba4837cba4a1cf9523d6f8b0621b150063dScott Russell     * <p>The following <b>conversions</b> are performed:
33389e281ba4837cba4a1cf9523d6f8b0621b150063dScott Russell     * <ul>
33397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
33407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <li>The language code "und" is mapped to language "".
33417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
33427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <li>The portion of a private use subtag prefixed by "lvariant",
33437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * if any, is removed and appended to the variant field in the
33447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * result locale (without case normalization).  If it is then
33457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * empty, the private use subtag is discarded:
33467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
33477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <pre>
33487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     ULocale loc;
33497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     loc = ULocale.forLanguageTag("en-US-x-lvariant-icu4j);
33507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     loc.getVariant(); // returns "ICU4J"
33517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     loc.getExtension('x'); // returns null
33527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
33537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     loc = Locale.forLanguageTag("de-icu4j-x-URP-lvariant-Abc-Def");
33547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     loc.getVariant(); // returns "ICU4J_ABC_DEF"
33557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     loc.getExtension('x'); // returns "urp"
33567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * </pre>
33577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
33587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <li>When the languageTag argument contains an extlang subtag,
33597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the first such subtag is used as the language, and the primary
33607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * language subtag and other extlang subtags are ignored:
33617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
33627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <pre>
33637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     ULocale.forLanguageTag("ar-aao").getLanguage(); // returns "aao"
33647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     ULocale.forLanguageTag("en-abc-def-us").toString(); // returns "abc_US"
33657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * </pre>
33667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
33677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <li>Case is normalized. Language is normalized to lower case,
33687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * script to title case, country to upper case, variant to upper case,
33697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and extensions to lower case.
33707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
33719e281ba4837cba4a1cf9523d6f8b0621b150063dScott Russell     * </ul>
33729e281ba4837cba4a1cf9523d6f8b0621b150063dScott Russell     *
33737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>This implements the 'Language-Tag' production of BCP47, and
33747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * so supports grandfathered (regular and irregular) as well as
33757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * private use language tags.  Stand alone private use tags are
33767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * represented as empty language and extension 'x-whatever',
33777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and grandfathered tags are converted to their canonical replacements
33782d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * where they exist.
33797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
33807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Grandfathered tags with canonical replacements are as follows:
33817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
33827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <table>
33837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tbody align="center">
33847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><th>grandfathered tag</th><th>&nbsp;</th><th>modern replacement</th></tr>
33857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>art-lojban</td><td>&nbsp;</td><td>jbo</td></tr>
33867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>i-ami</td><td>&nbsp;</td><td>ami</td></tr>
33877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>i-bnn</td><td>&nbsp;</td><td>bnn</td></tr>
33887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>i-hak</td><td>&nbsp;</td><td>hak</td></tr>
33897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>i-klingon</td><td>&nbsp;</td><td>tlh</td></tr>
33907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>i-lux</td><td>&nbsp;</td><td>lb</td></tr>
33917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>i-navajo</td><td>&nbsp;</td><td>nv</td></tr>
33927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>i-pwn</td><td>&nbsp;</td><td>pwn</td></tr>
33937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>i-tao</td><td>&nbsp;</td><td>tao</td></tr>
33947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>i-tay</td><td>&nbsp;</td><td>tay</td></tr>
33957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>i-tsu</td><td>&nbsp;</td><td>tsu</td></tr>
33967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>no-bok</td><td>&nbsp;</td><td>nb</td></tr>
33977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>no-nyn</td><td>&nbsp;</td><td>nn</td></tr>
33987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>sgn-BE-FR</td><td>&nbsp;</td><td>sfb</td></tr>
33997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>sgn-BE-NL</td><td>&nbsp;</td><td>vgt</td></tr>
34007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>sgn-CH-DE</td><td>&nbsp;</td><td>sgg</td></tr>
34017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>zh-guoyu</td><td>&nbsp;</td><td>cmn</td></tr>
34027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>zh-hakka</td><td>&nbsp;</td><td>hak</td></tr>
34037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>zh-min-nan</td><td>&nbsp;</td><td>nan</td></tr>
34047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>zh-xiang</td><td>&nbsp;</td><td>hsn</td></tr>
34057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * </tbody>
34067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * </table>
34077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
34087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Grandfathered tags with no modern replacement will be
34097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * converted as follows:
34107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
34117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <table>
34127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tbody align="center">
34137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><th>grandfathered tag</th><th>&nbsp;</th><th>converts to</th></tr>
34147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>cel-gaulish</td><td>&nbsp;</td><td>xtg-x-cel-gaulish</td></tr>
34157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>en-GB-oed</td><td>&nbsp;</td><td>en-GB-x-oed</td></tr>
34167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>i-default</td><td>&nbsp;</td><td>en-x-i-default</td></tr>
34177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>i-enochian</td><td>&nbsp;</td><td>und-x-i-enochian</td></tr>
34187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>i-mingo</td><td>&nbsp;</td><td>see-x-i-mingo</td></tr>
34197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <tr><td>zh-min</td><td>&nbsp;</td><td>nan-x-zh-min</td></tr>
34207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * </tbody>
34217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * </table>
34227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
34237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>For a list of all grandfathered tags, see the
34247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * IANA Language Subtag Registry (search for "Type: grandfathered").
34257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
34267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p><b>Note</b>: there is no guarantee that <code>toLanguageTag</code>
34277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and <code>forLanguageTag</code> will round-trip.
34287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
34297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param languageTag the language tag
34307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The locale that best represents the language tag.
34317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws NullPointerException if <code>languageTag</code> is <code>null</code>
34327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #toLanguageTag()
34337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see ULocale.Builder#setLanguageTag(String)
34347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
34357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.2
34367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
34377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static ULocale forLanguageTag(String languageTag) {
34387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        LanguageTag tag = LanguageTag.parse(languageTag, null);
34397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        InternalLocaleBuilder bldr = new InternalLocaleBuilder();
34407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        bldr.setLanguageTag(tag);
34417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getInstance(bldr.getBaseLocale(), bldr.getLocaleExtensions());
34427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
34437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
34447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
34457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Converts the specified keyword (legacy key, or BCP 47 Unicode locale
34467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * extension key) to the equivalent BCP 47 Unicode locale extension key.
34477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For example, BCP 47 Unicode locale extension key "co" is returned for
34487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the input keyword "collation".
34497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>
34507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * When the specified keyword is unknown, but satisfies the BCP syntax,
34517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * then the lower-case version of the input keyword will be returned.
34527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For example,
34537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <code>toUnicodeLocaleKey("ZZ")</code> returns "zz".
34542d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
34557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword       the input locale keyword (either legacy key
34567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      such as "collation" or BCP 47 Unicode locale extension
34577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      key such as "co").
34587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return              the well-formed BCP 47 Unicode locale extension key,
34597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      or null if the specified locale keyword cannot be mapped
34602d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *                      to a well-formed BCP 47 Unicode locale extension key.
34617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #toLegacyKey(String)
3462bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * @stable ICU 54
34637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
34647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String toUnicodeLocaleKey(String keyword) {
34657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String bcpKey = KeyTypeData.toBcpKey(keyword);
34667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (bcpKey == null && UnicodeLocaleExtension.isKey(keyword)) {
34677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // unknown keyword, but syntax is fine..
34687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            bcpKey = AsciiUtil.toLowerString(keyword);
34697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
34707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return bcpKey;
34717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
34727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
34737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
34747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Converts the specified keyword value (legacy type, or BCP 47
34757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Unicode locale extension type) to the well-formed BCP 47 Unicode locale
34767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * extension type for the specified keyword (category). For example, BCP 47
34777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Unicode locale extension type "phonebk" is returned for the input
34787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * keyword value "phonebook", with the keyword "collation" (or "co").
34797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>
34807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * When the specified keyword is not recognized, but the specified value
34817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * satisfies the syntax of the BCP 47 Unicode locale extension type,
34827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or when the specified keyword allows 'variable' type and the specified
34837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * value satisfies the syntax, the lower-case version of the input value
34847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * will be returned. For example,
34857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <code>toUnicodeLocaleType("Foo", "Bar")</code> returns "bar",
34867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <code>toUnicodeLocaleType("variableTop", "00A4")</code> returns "00a4".
34872d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
34887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword       the locale keyword (either legacy key such as
34897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      "collation" or BCP 47 Unicode locale extension
34907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      key such as "co").
34917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param value         the locale keyword value (either legacy type
34927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      such as "phonebook" or BCP 47 Unicode locale extension
34937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      type such as "phonebk").
34947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return              the well-formed BCP47 Unicode locale extension type,
34957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      or null if the locale keyword value cannot be mapped to
34967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      a well-formed BCP 47 Unicode locale extension type.
34977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #toLegacyType(String, String)
3498bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * @stable ICU 54
34997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
35007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String toUnicodeLocaleType(String keyword, String value) {
35017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String bcpType = KeyTypeData.toBcpType(keyword, value, null, null);
35027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (bcpType == null && UnicodeLocaleExtension.isType(value)) {
35037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // unknown keyword, but syntax is fine..
35047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            bcpType = AsciiUtil.toLowerString(value);
35057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
35067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return bcpType;
35077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
35087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
35097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
35107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Converts the specified keyword (BCP 47 Unicode locale extension key, or
35117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * legacy key) to the legacy key. For example, legacy key "collation" is
35127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * returned for the input BCP 47 Unicode locale extension key "co".
35132d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
35147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword       the input locale keyword (either BCP 47 Unicode locale
35157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      extension key or legacy key).
35167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return              the well-formed legacy key, or null if the specified
35177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      keyword cannot be mapped to a well-formed legacy key.
35187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #toUnicodeLocaleKey(String)
3519bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * @stable ICU 54
35207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
35217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String toLegacyKey(String keyword) {
35227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String legacyKey = KeyTypeData.toLegacyKey(keyword);
35237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (legacyKey == null) {
35247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Checks if the specified locale key is well-formed with the legacy locale syntax.
35257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //
35267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Note:
35277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //  Neither ICU nor LDML/CLDR provides the definition of keyword syntax.
35287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //  However, a key should not contain '=' obviously. For now, all existing
35297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //  keys are using ASCII alphabetic letters only. We won't add any new key
35307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //  that is not compatible with the BCP 47 syntax. Therefore, we assume
35317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //  a valid key consist from [0-9a-zA-Z], no symbols.
35327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (keyword.matches("[0-9a-zA-Z]+")) {
35337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                legacyKey = AsciiUtil.toLowerString(keyword);
35347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
35357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
35367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return legacyKey;
35377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
35387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
35397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
35407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@icu} Converts the specified keyword value (BCP 47 Unicode locale extension type,
35417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or legacy type or type alias) to the canonical legacy type. For example,
35427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the legacy type "phonebook" is returned for the input BCP 47 Unicode
35437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * locale extension type "phonebk" with the keyword "collation" (or "co").
35447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>
35457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * When the specified keyword is not recognized, but the specified value
35467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * satisfies the syntax of legacy key, or when the specified keyword
35477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * allows 'variable' type and the specified value satisfies the syntax,
35487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the lower-case version of the input value will be returned.
35497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For example,
35507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <code>toLegacyType("Foo", "Bar")</code> returns "bar",
35517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <code>toLegacyType("vt", "00A4")</code> returns "00a4".
35527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
35537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword       the locale keyword (either legacy keyword such as
35547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      "collation" or BCP 47 Unicode locale extension
35557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      key such as "co").
35567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param value         the locale keyword value (either BCP 47 Unicode locale
35577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      extension type such as "phonebk" or legacy keyword value
35587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      such as "phonebook").
35597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return              the well-formed legacy type, or null if the specified
35607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      keyword value cannot be mapped to a well-formed legacy
35617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      type.
35627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see #toUnicodeLocaleType(String, String)
3563bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * @stable ICU 54
35647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
35657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String toLegacyType(String keyword, String value) {
35667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String legacyType = KeyTypeData.toLegacyType(keyword, value, null, null);
35677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (legacyType == null) {
35687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Checks if the specified locale type is well-formed with the legacy locale syntax.
35697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //
35707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Note:
35717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //  Neither ICU nor LDML/CLDR provides the definition of keyword syntax.
35727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //  However, a type should not contain '=' obviously. For now, all existing
35737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //  types are using ASCII alphabetic letters with a few symbol letters. We won't
35747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //  add any new type that is not compatible with the BCP 47 syntax except timezone
35757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //  IDs. For now, we assume a valid type start with [0-9a-zA-Z], but may contain
35767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //  '-' '_' '/' in the middle.
35777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (value.matches("[0-9a-zA-Z]+([_/\\-][0-9a-zA-Z]+)*")) {
35787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                legacyType = AsciiUtil.toLowerString(value);
35797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
35807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
35817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return legacyType;
35827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
35837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
35847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
35857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <code>Builder</code> is used to build instances of <code>ULocale</code>
35867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * from values configured by the setters.  Unlike the <code>ULocale</code>
35877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * constructors, the <code>Builder</code> checks if a value configured by a
35887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * setter satisfies the syntax requirements defined by the <code>ULocale</code>
35897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * class.  A <code>ULocale</code> object created by a <code>Builder</code> is
35907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * well-formed and can be transformed to a well-formed IETF BCP 47 language tag
35917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * without losing information.
35927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
35937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p><b>Note:</b> The <code>ULocale</code> class does not provide any
35947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * syntactic restrictions on variant, while BCP 47 requires each variant
35957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * subtag to be 5 to 8 alphanumerics or a single numeric followed by 3
35967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * alphanumerics.  The method <code>setVariant</code> throws
35977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <code>IllformedLocaleException</code> for a variant that does not satisfy
35987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * this restriction. If it is necessary to support such a variant, use a
35997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * ULocale constructor.  However, keep in mind that a <code>ULocale</code>
36007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * object created this way might lose the variant information when
36017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * transformed to a BCP 47 language tag.
36027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
36037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>The following example shows how to create a <code>Locale</code> object
36047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * with the <code>Builder</code>.
36057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <blockquote>
36067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <pre>
36077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     ULocale aLocale = new Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build();
36087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * </pre>
36097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * </blockquote>
36107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
36117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Builders can be reused; <code>clear()</code> resets all
36127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * fields to their default values.
36137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
36147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see ULocale#toLanguageTag()
36157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
36167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.2
36177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
36187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final class Builder {
36197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
36207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final InternalLocaleBuilder _locbld;
36217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
36227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
36237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Constructs an empty Builder. The default value of all
36247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * fields, extensions, and private use information is the
36257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * empty string.
36267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
36277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.2
36287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
36297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Builder() {
36307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            _locbld = new InternalLocaleBuilder();
36317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
36327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
36337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
36347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Resets the <code>Builder</code> to match the provided
36357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <code>locale</code>.  Existing state is discarded.
36367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
36377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <p>All fields of the locale must be well-formed, see {@link Locale}.
36387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
36397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <p>Locales with any ill-formed fields cause
36407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <code>IllformedLocaleException</code> to be thrown.
36417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
36427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param locale the locale
36437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return This builder.
36447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws IllformedLocaleException if <code>locale</code> has
36457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * any ill-formed fields.
36467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws NullPointerException if <code>locale</code> is null.
36477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
36487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.2
36497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
36507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Builder setLocale(ULocale locale) {
36517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
36527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                _locbld.setLocale(locale.base(), locale.extensions());
36537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (LocaleSyntaxException e) {
36547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
36557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
36567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
36577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
36587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
36597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
36607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Resets the Builder to match the provided IETF BCP 47
36617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * language tag.  Discards the existing state.  Null and the
36627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * empty string cause the builder to be reset, like {@link
36637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * #clear}.  Grandfathered tags (see {@link
36647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * ULocale#forLanguageTag}) are converted to their canonical
36657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * form before being processed.  Otherwise, the language tag
36667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * must be well-formed (see {@link ULocale}) or an exception is
36677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * thrown (unlike <code>ULocale.forLanguageTag</code>, which
36687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * just discards ill-formed and following portions of the
36697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * tag).
36707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
36717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param languageTag the language tag
36727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return This builder.
36737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws IllformedLocaleException if <code>languageTag</code> is ill-formed
36747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @see ULocale#forLanguageTag(String)
36757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
36767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.2
36777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
36787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Builder setLanguageTag(String languageTag) {
36797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ParseStatus sts = new ParseStatus();
36807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            LanguageTag tag = LanguageTag.parse(languageTag, sts);
36817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (sts.isError()) {
36827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllformedLocaleException(sts.getErrorMessage(), sts.getErrorIndex());
36837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
36847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            _locbld.setLanguageTag(tag);
36857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
36867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
36877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
36887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
36897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
36907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Sets the language.  If <code>language</code> is the empty string or
36917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * null, the language in this <code>Builder</code> is removed.  Otherwise,
36927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * the language must be <a href="./Locale.html#def_language">well-formed</a>
36937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * or an exception is thrown.
36947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
36957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <p>The typical language value is a two or three-letter language
36967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * code as defined in ISO639.
36977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
36987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param language the language
36997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return This builder.
37007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws IllformedLocaleException if <code>language</code> is ill-formed
37017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
37027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.2
37037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
37047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Builder setLanguage(String language) {
37057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
37067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                _locbld.setLanguage(language);
37077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (LocaleSyntaxException e) {
37087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
37097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
37107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
37117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
37127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
37137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
37147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Sets the script. If <code>script</code> is null or the empty string,
37157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * the script in this <code>Builder</code> is removed.
37167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Otherwise, the script must be well-formed or an exception is thrown.
37177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
37187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <p>The typical script value is a four-letter script code as defined by ISO 15924.
37197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
37207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param script the script
37217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return This builder.
37227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws IllformedLocaleException if <code>script</code> is ill-formed
37237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
37247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.2
37257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
37267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Builder setScript(String script) {
37277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
37287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                _locbld.setScript(script);
37297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (LocaleSyntaxException e) {
37307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
37317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
37327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
37337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
37347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
37357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
37367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Sets the region.  If region is null or the empty string, the region
37377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * in this <code>Builder</code> is removed.  Otherwise,
37387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * the region must be well-formed or an exception is thrown.
37397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
37407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <p>The typical region value is a two-letter ISO 3166 code or a
37417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * three-digit UN M.49 area code.
37427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
37437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <p>The country value in the <code>Locale</code> created by the
37447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <code>Builder</code> is always normalized to upper case.
37457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
37467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param region the region
37477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return This builder.
37487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws IllformedLocaleException if <code>region</code> is ill-formed
37497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
37507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.2
37517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
37527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Builder setRegion(String region) {
37537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
37547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                _locbld.setRegion(region);
37557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (LocaleSyntaxException e) {
37567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
37577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
37587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
37597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
37607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
37617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
37627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Sets the variant.  If variant is null or the empty string, the
37637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * variant in this <code>Builder</code> is removed.  Otherwise, it
37647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * must consist of one or more well-formed subtags, or an exception is thrown.
37657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
37667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <p><b>Note:</b> This method checks if <code>variant</code>
37677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * satisfies the IETF BCP 47 variant subtag's syntax requirements,
37687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * and normalizes the value to lowercase letters.  However,
37697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * the <code>ULocale</code> class does not impose any syntactic
37707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * restriction on variant.  To set such a variant,
37717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * use a ULocale constructor.
37727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
37737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param variant the variant
37747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return This builder.
37757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws IllformedLocaleException if <code>variant</code> is ill-formed
37767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
37777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.2
37787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
37797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Builder setVariant(String variant) {
37807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
37817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                _locbld.setVariant(variant);
37827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (LocaleSyntaxException e) {
37837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
37847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
37857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
37867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
37877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
37887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
37897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Sets the extension for the given key. If the value is null or the
37907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * empty string, the extension is removed.  Otherwise, the extension
37917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * must be well-formed or an exception is thrown.
37927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
37937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <p><b>Note:</b> The key {@link ULocale#UNICODE_LOCALE_EXTENSION
37947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * UNICODE_LOCALE_EXTENSION} ('u') is used for the Unicode locale extension.
37957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Setting a value for this key replaces any existing Unicode locale key/type
37967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * pairs with those defined in the extension.
37977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
37987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <p><b>Note:</b> The key {@link ULocale#PRIVATE_USE_EXTENSION
37997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * PRIVATE_USE_EXTENSION} ('x') is used for the private use code. To be
38007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * well-formed, the value for this key needs only to have subtags of one to
38017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * eight alphanumeric characters, not two to eight as in the general case.
38027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
38037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param key the extension key
38047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param value the extension value
38057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return This builder.
38067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws IllformedLocaleException if <code>key</code> is illegal
38077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * or <code>value</code> is ill-formed
38087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @see #setUnicodeLocaleKeyword(String, String)
38097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
38107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.2
38117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
38127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Builder setExtension(char key, String value) {
38137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
38147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                _locbld.setExtension(key, value);
38157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (LocaleSyntaxException e) {
38167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
38177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
38187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
38197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
38207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
38217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
38227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Sets the Unicode locale keyword type for the given key.  If the type
38237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * is null, the Unicode keyword is removed.  Otherwise, the key must be
38247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * non-null and both key and type must be well-formed or an exception
38257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * is thrown.
38267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
38277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <p>Keys and types are converted to lower case.
38287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
38297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <p><b>Note</b>:Setting the 'u' extension via {@link #setExtension}
38307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * replaces all Unicode locale keywords with those defined in the
38317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * extension.
38327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
38337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param key the Unicode locale key
38347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param type the Unicode locale type
38357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return This builder.
38367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws IllformedLocaleException if <code>key</code> or <code>type</code>
38377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * is ill-formed
38387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws NullPointerException if <code>key</code> is null
38397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @see #setExtension(char, String)
38407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
38417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.4
38427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
38437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Builder setUnicodeLocaleKeyword(String key, String type) {
38447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
38457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                _locbld.setUnicodeLocaleKeyword(key, type);
38467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (LocaleSyntaxException e) {
38477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
38487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
38497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
38507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
38517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
38527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
38537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Adds a unicode locale attribute, if not already present, otherwise
38547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * has no effect.  The attribute must not be null and must be well-formed
38557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * or an exception is thrown.
38567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
38577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param attribute the attribute
38587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return This builder.
38597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws NullPointerException if <code>attribute</code> is null
38607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws IllformedLocaleException if <code>attribute</code> is ill-formed
38617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @see #setExtension(char, String)
38627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
38637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.6
38647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
38657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Builder addUnicodeLocaleAttribute(String attribute) {
38667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
38677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                _locbld.addUnicodeLocaleAttribute(attribute);
38687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (LocaleSyntaxException e) {
38697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
38707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
38717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
38727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
38737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
38747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
38757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Removes a unicode locale attribute, if present, otherwise has no
38767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * effect.  The attribute must not be null and must be well-formed
38777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * or an exception is thrown.
38787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
38797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <p>Attribute comparision for removal is case-insensitive.
38807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
38817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param attribute the attribute
38827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return This builder.
38837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws NullPointerException if <code>attribute</code> is null
38847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @throws IllformedLocaleException if <code>attribute</code> is ill-formed
38857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @see #setExtension(char, String)
38867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
38877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.6
38887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
38897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Builder removeUnicodeLocaleAttribute(String attribute) {
38907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
38917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                _locbld.removeUnicodeLocaleAttribute(attribute);
38927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (LocaleSyntaxException e) {
38937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
38947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
38957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
38967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
38977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
38987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
38997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Resets the builder to its initial, empty state.
39007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
39017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return this builder
39027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
39037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.2
39047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
39057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Builder clear() {
39067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            _locbld.clear();
39077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
39087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
39097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
39107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
39117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Resets the extensions to their initial, empty state.
39127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Language, script, region and variant are unchanged.
39137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
39147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return this builder
39157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @see #setExtension(char, String)
39167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
39177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.2
39187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
39197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Builder clearExtensions() {
39207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            _locbld.clearExtensions();
39217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
39227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
39237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
39247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
39257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Returns an instance of <code>ULocale</code> created from the fields set
39267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * on this builder.
39277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
39287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return a new Locale
39297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
39307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.4
39317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
39327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public ULocale build() {
39337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return getInstance(_locbld.getBaseLocale(), _locbld.getLocaleExtensions());
39347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
39357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
39367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
39377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static ULocale getInstance(BaseLocale base, LocaleExtensions exts) {
39387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String id = lscvToID(base.getLanguage(), base.getScript(), base.getRegion(),
39397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                base.getVariant());
39407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
39417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Set<Character> extKeys = exts.getKeys();
39427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!extKeys.isEmpty()) {
39437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // legacy locale ID assume Unicode locale keywords and
39447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // other extensions are at the same level.
39457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // e.g. @a=ext-for-aa;calendar=japanese;m=ext-for-mm;x=priv-use
39467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
39477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            TreeMap<String, String> kwds = new TreeMap<String, String>();
39487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Character key : extKeys) {
39497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Extension ext = exts.getExtension(key);
39507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (ext instanceof UnicodeLocaleExtension) {
39517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    UnicodeLocaleExtension uext = (UnicodeLocaleExtension)ext;
39527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    Set<String> ukeys = uext.getUnicodeLocaleKeys();
39537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (String bcpKey : ukeys) {
39547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String bcpType = uext.getUnicodeLocaleType(bcpKey);
39557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // convert to legacy key/type
39567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String lkey = toLegacyKey(bcpKey);
39577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String ltype = toLegacyType(bcpKey, ((bcpType.length() == 0) ? "yes" : bcpType)); // use "yes" as the value of typeless keywords
39587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // special handling for u-va-posix, since this is a variant, not a keyword
39597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (lkey.equals("va") && ltype.equals("posix") && base.getVariant().length() == 0) {
39607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            id = id + "_POSIX";
39617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else {
39627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            kwds.put(lkey, ltype);
39637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
39647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
39657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Mapping Unicode locale attribute to the special keyword, attribute=xxx-yyy
39667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    Set<String> uattributes = uext.getUnicodeLocaleAttributes();
39677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (uattributes.size() > 0) {
39687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        StringBuilder attrbuf = new StringBuilder();
39697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        for (String attr : uattributes) {
39707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (attrbuf.length() > 0) {
39717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                attrbuf.append('-');
39727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
39737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            attrbuf.append(attr);
39747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
39757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        kwds.put(LOCALE_ATTRIBUTE_KEY, attrbuf.toString());
39767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
39777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
39787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    kwds.put(String.valueOf(key), ext.getValue());
39797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
39807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
39817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
39827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!kwds.isEmpty()) {
39837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                StringBuilder buf = new StringBuilder(id);
39847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append("@");
39857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Set<Map.Entry<String, String>> kset = kwds.entrySet();
39867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                boolean insertSep = false;
39877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (Map.Entry<String, String> kwd : kset) {
39887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (insertSep) {
39897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        buf.append(";");
39907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
39917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        insertSep = true;
39927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
39937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    buf.append(kwd.getKey());
39947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    buf.append("=");
39957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    buf.append(kwd.getValue());
39967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
39977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
39987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                id = buf.toString();
39997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
40007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
40017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new ULocale(id);
40027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
40037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
40047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private BaseLocale base() {
40057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (baseLocale == null) {
40067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String language, script, region, variant;
40077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            language = script = region = variant = "";
40087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!equals(ULocale.ROOT)) {
40097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                LocaleIDParser lp = new LocaleIDParser(localeID);
40107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                language = lp.getLanguage();
40117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                script = lp.getScript();
40127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                region = lp.getCountry();
40137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                variant = lp.getVariant();
40147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
40157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            baseLocale = BaseLocale.getInstance(language, script, region, variant);
40167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
40177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return baseLocale;
40187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
40197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
40207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private LocaleExtensions extensions() {
40217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (extensions == null) {
40227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Iterator<String> kwitr = getKeywords();
40237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (kwitr == null) {
40247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                extensions = LocaleExtensions.EMPTY_EXTENSIONS;
40257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
40267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                InternalLocaleBuilder intbld = new InternalLocaleBuilder();
40277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                while (kwitr.hasNext()) {
40287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String key = kwitr.next();
40297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (key.equals(LOCALE_ATTRIBUTE_KEY)) {
40307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // special keyword used for representing Unicode locale attributes
40317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String[] uattributes = getKeywordValue(key).split("[-_]");
40327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        for (String uattr : uattributes) {
40337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            try {
40347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                intbld.addUnicodeLocaleAttribute(uattr);
40357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } catch (LocaleSyntaxException e) {
40367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                // ignore and fall through
40377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
40387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
40397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else if (key.length() >= 2) {
40407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String bcpKey = toUnicodeLocaleKey(key);
40417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String bcpType = toUnicodeLocaleType(key, getKeywordValue(key));
40427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (bcpKey != null && bcpType != null) {
40437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            try {
40447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                intbld.setUnicodeLocaleKeyword(bcpKey, bcpType);
40457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } catch (LocaleSyntaxException e) {
40467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                // ignore and fall through
40477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
40487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
40497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else if (key.length() == 1 && (key.charAt(0) != UNICODE_LOCALE_EXTENSION)) {
40507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        try  {
40517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            intbld.setExtension(key.charAt(0), getKeywordValue(key).replace("_",
40527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    LanguageTag.SEP));
40537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } catch (LocaleSyntaxException e) {
40547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // ignore and fall through
40557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
40567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
40577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
40587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                extensions = intbld.getLocaleExtensions();
40597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
40607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
40617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return extensions;
40627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
40637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
40647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
40657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * JDK Locale Helper
40667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
40677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final class JDKLocaleHelper {
40689713d6ad72cf9922638a2a56f61089a3db31adabroubert        private static boolean hasScriptsAndUnicodeExtensions = false;
40699713d6ad72cf9922638a2a56f61089a3db31adabroubert        private static boolean hasLocaleCategories = false;
40707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
40717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /*
40727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * New methods in Java 7 Locale class
40737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
40747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static Method mGetScript;
40757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static Method mGetExtensionKeys;
40767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static Method mGetExtension;
40777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static Method mGetUnicodeLocaleKeys;
40787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static Method mGetUnicodeLocaleAttributes;
40797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static Method mGetUnicodeLocaleType;
40807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static Method mForLanguageTag;
40817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
40827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static Method mGetDefault;
40837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static Method mSetDefault;
40847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static Object eDISPLAY;
40857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static Object eFORMAT;
40867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
40877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /*
40887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * This table is used for mapping between ICU and special Java
40897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 6 locales.  When an ICU locale matches <minumum base> with
40907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <keyword>/<value>, the ICU locale is mapped to <Java> locale.
40917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * For example, both ja_JP@calendar=japanese and ja@calendar=japanese
40927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * are mapped to Java locale "ja_JP_JP".  ICU locale "nn" is mapped
40937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * to Java locale "no_NO_NY".
40947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
40957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final String[][] JAVA6_MAPDATA = {
4096f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            //  { <Java>,       <ICU base>, <keyword>,  <value>,    <minimum base>
40977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            { "ja_JP_JP",   "ja_JP",    "calendar", "japanese", "ja"},
40987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            { "no_NO_NY",   "nn_NO",    null,       null,       "nn"},
40997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            { "th_TH_TH",   "th_TH",    "numbers",  "thai",     "th"},
41007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        };
41017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
41027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        static {
41037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            do {
41047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                try {
41057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    mGetScript = Locale.class.getMethod("getScript", (Class[]) null);
41067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    mGetExtensionKeys = Locale.class.getMethod("getExtensionKeys", (Class[]) null);
41077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    mGetExtension = Locale.class.getMethod("getExtension", char.class);
41087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    mGetUnicodeLocaleKeys = Locale.class.getMethod("getUnicodeLocaleKeys", (Class[]) null);
41097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    mGetUnicodeLocaleAttributes = Locale.class.getMethod("getUnicodeLocaleAttributes", (Class[]) null);
41107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    mGetUnicodeLocaleType = Locale.class.getMethod("getUnicodeLocaleType", String.class);
41117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    mForLanguageTag = Locale.class.getMethod("forLanguageTag", String.class);
4112f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
41139713d6ad72cf9922638a2a56f61089a3db31adabroubert                    hasScriptsAndUnicodeExtensions = true;
41149713d6ad72cf9922638a2a56f61089a3db31adabroubert                } catch (NoSuchMethodException e) {
41159713d6ad72cf9922638a2a56f61089a3db31adabroubert                } catch (IllegalArgumentException e) {
41169713d6ad72cf9922638a2a56f61089a3db31adabroubert                } catch (SecurityException e) {
41179713d6ad72cf9922638a2a56f61089a3db31adabroubert                    // TODO : report?
41189713d6ad72cf9922638a2a56f61089a3db31adabroubert                }
41199713d6ad72cf9922638a2a56f61089a3db31adabroubert
41209713d6ad72cf9922638a2a56f61089a3db31adabroubert                try {
41217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    Class<?> cCategory = null;
41227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    Class<?>[] classes = Locale.class.getDeclaredClasses();
41237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (Class<?> c : classes) {
41247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (c.getName().equals("java.util.Locale$Category")) {
41257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            cCategory = c;
41267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            break;
41277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
41287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
41297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (cCategory == null) {
41307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        break;
41317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
41327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    mGetDefault = Locale.class.getDeclaredMethod("getDefault", cCategory);
41337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    mSetDefault = Locale.class.getDeclaredMethod("setDefault", cCategory, Locale.class);
4134f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
41357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    Method mName = cCategory.getMethod("name", (Class[]) null);
41367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    Object[] enumConstants = cCategory.getEnumConstants();
41377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (Object e : enumConstants) {
41387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String catVal = (String)mName.invoke(e, (Object[])null);
41397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (catVal.equals("DISPLAY")) {
41407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            eDISPLAY = e;
41417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (catVal.equals("FORMAT")) {
41427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            eFORMAT = e;
41437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
41447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
41457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (eDISPLAY == null || eFORMAT == null) {
41467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        break;
41479713d6ad72cf9922638a2a56f61089a3db31adabroubert                    }
41489713d6ad72cf9922638a2a56f61089a3db31adabroubert
41499713d6ad72cf9922638a2a56f61089a3db31adabroubert                    hasLocaleCategories = true;
41507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (NoSuchMethodException e) {
41517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (IllegalArgumentException e) {
41527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (IllegalAccessException e) {
41537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (InvocationTargetException e) {
41547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (SecurityException e) {
41557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // TODO : report?
41567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
41577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } while (false);
41587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
41597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
41607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private JDKLocaleHelper() {
41617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
41627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
41639713d6ad72cf9922638a2a56f61089a3db31adabroubert        public static boolean hasLocaleCategories() {
41649713d6ad72cf9922638a2a56f61089a3db31adabroubert            return hasLocaleCategories;
41657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
41667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
41677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static ULocale toULocale(Locale loc) {
41689713d6ad72cf9922638a2a56f61089a3db31adabroubert            return hasScriptsAndUnicodeExtensions ? toULocale7(loc) : toULocale6(loc);
41697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
41707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
41717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static Locale toLocale(ULocale uloc) {
41729713d6ad72cf9922638a2a56f61089a3db31adabroubert            return hasScriptsAndUnicodeExtensions ? toLocale7(uloc) : toLocale6(uloc);
41737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
41747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
41757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static ULocale toULocale7(Locale loc) {
41767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String language = loc.getLanguage();
41777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String script = "";
41787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String country = loc.getCountry();
41797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String variant = loc.getVariant();
41807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
41817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Set<String> attributes = null;
41827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Map<String, String> keywords = null;
41837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
41847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
41857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                script = (String) mGetScript.invoke(loc, (Object[]) null);
41867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                @SuppressWarnings("unchecked")
41877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Set<Character> extKeys = (Set<Character>) mGetExtensionKeys.invoke(loc, (Object[]) null);
41887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (!extKeys.isEmpty()) {
41897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (Character extKey : extKeys) {
41907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (extKey.charValue() == 'u') {
41917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // Found Unicode locale extension
41927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
41937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // attributes
41947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            @SuppressWarnings("unchecked")
41957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            Set<String> uAttributes = (Set<String>) mGetUnicodeLocaleAttributes.invoke(loc, (Object[]) null);
41967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (!uAttributes.isEmpty()) {
41977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                attributes = new TreeSet<String>();
41987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                for (String attr : uAttributes) {
41997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    attributes.add(attr);
42007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
42017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
42027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
42037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // keywords
42047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            @SuppressWarnings("unchecked")
42057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            Set<String> uKeys = (Set<String>) mGetUnicodeLocaleKeys.invoke(loc, (Object[]) null);
42067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            for (String kwKey : uKeys) {
42077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                String kwVal = (String) mGetUnicodeLocaleType.invoke(loc, kwKey);
42087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                if (kwVal != null) {
42097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    if (kwKey.equals("va")) {
42107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        // va-* is interpreted as a variant
42117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        variant = (variant.length() == 0) ? kwVal : kwVal + "_" + variant;
42127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    } else {
42137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        if (keywords == null) {
42147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            keywords = new TreeMap<String, String>();
42157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        }
42167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        keywords.put(kwKey, kwVal);
42177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    }
42187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
42197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
42207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else {
42217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            String extVal = (String) mGetExtension.invoke(loc, extKey);
42227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (extVal != null) {
42237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                if (keywords == null) {
42247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    keywords = new TreeMap<String, String>();
42257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
42267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                keywords.put(String.valueOf(extKey), extVal);
42277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
42287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
42297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
42307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
42317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (IllegalAccessException e) {
42327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new RuntimeException(e);
42337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (InvocationTargetException e) {
42347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new RuntimeException(e);
42357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
42367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
42377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // JDK locale no_NO_NY is not interpreted as Nynorsk by ICU,
42387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // and it should be transformed to nn_NO.
42397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
42407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Note: JDK7+ unerstand both no_NO_NY and nn_NO. When convert
42417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // ICU locale to JDK, we do not need to map nn_NO back to no_NO_NY.
42427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
42437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (language.equals("no") && country.equals("NO") && variant.equals("NY")) {
42447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                language = "nn";
42457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                variant = "";
42467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
42477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
42487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Constructing ID
42497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuilder buf = new StringBuilder(language);
42507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
42517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (script.length() > 0) {
42527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append('_');
42537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(script);
42547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
42557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
42567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (country.length() > 0) {
42577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append('_');
42587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(country);
42597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
42607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
42617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (variant.length() > 0) {
42627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (country.length() == 0) {
42637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    buf.append('_');
42647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
42657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append('_');
42667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(variant);
42677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
42687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
42697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (attributes != null) {
42707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // transform Unicode attributes into a keyword
42717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                StringBuilder attrBuf = new StringBuilder();
42727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (String attr : attributes) {
42737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (attrBuf.length() != 0) {
42747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        attrBuf.append('-');
42757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
42767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    attrBuf.append(attr);
42777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
42787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (keywords == null) {
42797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    keywords = new TreeMap<String, String>();
42807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
42817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                keywords.put(LOCALE_ATTRIBUTE_KEY, attrBuf.toString());
42827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
42837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
42847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (keywords != null) {
42857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append('@');
42867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                boolean addSep = false;
42877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (Entry<String, String> kwEntry : keywords.entrySet()) {
42887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String kwKey = kwEntry.getKey();
42897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String kwVal = kwEntry.getValue();
42907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
42917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (kwKey.length() != 1) {
42927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // Unicode locale key
42937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        kwKey = toLegacyKey(kwKey);
42947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // use "yes" as the value of typeless keywords
42957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        kwVal = toLegacyType(kwKey, ((kwVal.length() == 0) ? "yes" : kwVal));
42967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
42977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
42987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (addSep) {
42997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        buf.append(';');
43007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
43017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        addSep = true;
43027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
43037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    buf.append(kwKey);
43047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    buf.append('=');
43057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    buf.append(kwVal);
43067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
43077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
43087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
43097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return new ULocale(getName(buf.toString()), loc);
43107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
43117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
43127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static ULocale toULocale6(Locale loc) {
43137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ULocale uloc = null;
43147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String locStr = loc.toString();
43157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (locStr.length() == 0) {
43167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                uloc = ULocale.ROOT;
43177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
43187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = 0; i < JAVA6_MAPDATA.length; i++) {
43197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (JAVA6_MAPDATA[i][0].equals(locStr)) {
43207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        LocaleIDParser p = new LocaleIDParser(JAVA6_MAPDATA[i][1]);
43217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        p.setKeywordValue(JAVA6_MAPDATA[i][2], JAVA6_MAPDATA[i][3]);
43227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        locStr = p.getName();
43237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        break;
43247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
43257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
43267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                uloc = new ULocale(getName(locStr), loc);
43277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
43287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return uloc;
43297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
43307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
43317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static Locale toLocale7(ULocale uloc) {
43327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Locale loc = null;
43337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String ulocStr = uloc.getName();
43347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (uloc.getScript().length() > 0 || ulocStr.contains("@")) {
43357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // With script or keywords available, the best way
43367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // to get a mapped Locale is to go through a language tag.
43377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // A Locale with script or keywords can only have variants
43387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // that is 1 to 8 alphanum. If this ULocale has a variant
43397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // subtag not satisfying the criteria, the variant subtag
43407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // will be lost.
43417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String tag = uloc.toLanguageTag();
43427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
43437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Workaround for variant casing problem:
43447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //
43457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // The variant field in ICU is case insensitive and normalized
43467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // to upper case letters by getVariant(), while
43477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // the variant field in JDK Locale is case sensitive.
43487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // ULocale#toLanguageTag use lower case characters for
43497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // BCP 47 variant and private use x-lvariant.
43507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //
43517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Locale#forLanguageTag in JDK preserves character casing
43527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // for variant. Because ICU always normalizes variant to
43537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // upper case, we convert language tag to upper case here.
43547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                tag = AsciiUtil.toUpperString(tag);
43557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
43567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                try {
43577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    loc = (Locale)mForLanguageTag.invoke(null, tag);
43587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (IllegalAccessException e) {
43597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    throw new RuntimeException(e);
43607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (InvocationTargetException e) {
43617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    throw new RuntimeException(e);
43627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
43637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
43647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (loc == null) {
43657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Without script or keywords, use a Locale constructor,
43667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // so we can preserve any ill-formed variants.
43677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                loc = new Locale(uloc.getLanguage(), uloc.getCountry(), uloc.getVariant());
43687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
43697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return loc;
43707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
43717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
43727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static Locale toLocale6(ULocale uloc) {
43737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String locstr = uloc.getBaseName();
43747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < JAVA6_MAPDATA.length; i++) {
43757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (locstr.equals(JAVA6_MAPDATA[i][1]) || locstr.equals(JAVA6_MAPDATA[i][4])) {
43767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (JAVA6_MAPDATA[i][2] != null) {
43777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String val = uloc.getKeywordValue(JAVA6_MAPDATA[i][2]);
43787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (val != null && val.equals(JAVA6_MAPDATA[i][3])) {
43797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            locstr = JAVA6_MAPDATA[i][0];
43807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            break;
43817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
43827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
43837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        locstr = JAVA6_MAPDATA[i][0];
43847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        break;
43857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
43867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
43877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
43887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            LocaleIDParser p = new LocaleIDParser(locstr);
43897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String[] names = p.getLanguageScriptCountryVariant();
43907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return new Locale(names[0], names[2], names[3]);
43917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
43927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
43937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static Locale getDefault(Category category) {
43947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Locale loc = Locale.getDefault();
43959713d6ad72cf9922638a2a56f61089a3db31adabroubert            if (hasLocaleCategories) {
43967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Object cat = null;
43977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                switch (category) {
43987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                case DISPLAY:
43997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    cat = eDISPLAY;
44007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
44017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                case FORMAT:
44027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    cat = eFORMAT;
44037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
44047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
44057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (cat != null) {
44067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    try {
44077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        loc = (Locale)mGetDefault.invoke(null, cat);
44087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } catch (InvocationTargetException e) {
44097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // fall through - use the base default
44107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } catch (IllegalArgumentException e) {
44117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // fall through - use the base default
44127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } catch (IllegalAccessException e) {
44137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // fall through - use the base default
44147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
44157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
44167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
44177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return loc;
44187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
44197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
44207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static void setDefault(Category category, Locale newLocale) {
44219713d6ad72cf9922638a2a56f61089a3db31adabroubert            if (hasLocaleCategories) {
44227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Object cat = null;
44237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                switch (category) {
44247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                case DISPLAY:
44257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    cat = eDISPLAY;
44267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
44277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                case FORMAT:
44287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    cat = eFORMAT;
44297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
44307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
44317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (cat != null) {
44327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    try {
44337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        mSetDefault.invoke(null, cat, newLocale);
44347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } catch (InvocationTargetException e) {
44357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // fall through - no effects
44367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } catch (IllegalArgumentException e) {
44377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // fall through - no effects
44387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } catch (IllegalAccessException e) {
44397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // fall through - no effects
44407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
44417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
44427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
44437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
44447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
44457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Returns true if the given Locale matches the original
44467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // default locale initialized by JVM by checking user.XXX
44477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // system properties. When the system properties are not accessible,
44487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // this method returns false.
44497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static boolean isOriginalDefaultLocale(Locale loc) {
44509713d6ad72cf9922638a2a56f61089a3db31adabroubert            if (hasScriptsAndUnicodeExtensions) {
44517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String script = "";
44527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                try {
44537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    script = (String) mGetScript.invoke(loc, (Object[]) null);
44547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (Exception e) {
44557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return false;
44567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
44577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
44587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return loc.getLanguage().equals(getSystemProperty("user.language"))
44597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        && loc.getCountry().equals(getSystemProperty("user.country"))
44607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        && loc.getVariant().equals(getSystemProperty("user.variant"))
44617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        && script.equals(getSystemProperty("user.script"));
44627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
44637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return loc.getLanguage().equals(getSystemProperty("user.language"))
44647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    && loc.getCountry().equals(getSystemProperty("user.country"))
44657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    && loc.getVariant().equals(getSystemProperty("user.variant"));
44667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
44677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
44687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static String getSystemProperty(String key) {
44697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String val = null;
44707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            final String fkey = key;
44717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (System.getSecurityManager() != null) {
44727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                try {
44737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    val = AccessController.doPrivileged(new PrivilegedAction<String>() {
44742d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        @Override
44757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        public String run() {
44767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            return System.getProperty(fkey);
44777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
44787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    });
44797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (AccessControlException e) {
44807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // ignore
44817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
44827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
44837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                val = System.getProperty(fkey);
44847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
44857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return val;
44867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
44877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
44887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
4489