jdk/src/share/classes/java/util/Locale.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/util/Locale.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1143 @@
+/*
+ * Copyright 1996-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
+ * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
+ *
+ * The original version of this source code and documentation
+ * is copyrighted and owned by Taligent, Inc., a wholly-owned
+ * subsidiary of IBM. These materials are provided under terms
+ * of a License Agreement between Taligent and Sun. This technology
+ * is protected by multiple US and International patents.
+ *
+ * This notice and attribution to Taligent may not be removed.
+ * Taligent is a registered trademark of Taligent, Inc.
+ *
+ */
+
+package java.util;
+
+import java.io.*;
+import java.security.AccessController;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.spi.LocaleNameProvider;
+import java.util.spi.LocaleServiceProvider;
+import sun.security.action.GetPropertyAction;
+import sun.util.LocaleServiceProviderPool;
+import sun.util.resources.LocaleData;
+import sun.util.resources.OpenListResourceBundle;
+
+/**
+ *
+ * A <code>Locale</code> object represents a specific geographical, political,
+ * or cultural region. An operation that requires a <code>Locale</code> to perform
+ * its task is called <em>locale-sensitive</em> and uses the <code>Locale</code>
+ * to tailor information for the user. For example, displaying a number
+ * is a locale-sensitive operation--the number should be formatted
+ * according to the customs/conventions of the user's native country,
+ * region, or culture.
+ *
+ * <P>
+ * Create a <code>Locale</code> object using the constructors in this class:
+ * <blockquote>
+ * <pre>
+ * Locale(String language)
+ * Locale(String language, String country)
+ * Locale(String language, String country, String variant)
+ * </pre>
+ * </blockquote>
+ * The language argument is a valid <STRONG>ISO Language Code.</STRONG>
+ * These codes are the lower-case, two-letter codes as defined by ISO-639.
+ * You can find a full list of these codes at a number of sites, such as:
+ * <BR><a href ="http://www.loc.gov/standards/iso639-2/php/English_list.php">
+ * <code>http://www.loc.gov/standards/iso639-2/php/English_list.php</code></a>
+ *
+ * <P>
+ * The country argument is a valid <STRONG>ISO Country Code.</STRONG> These
+ * codes are the upper-case, two-letter codes as defined by ISO-3166.
+ * You can find a full list of these codes at a number of sites, such as:
+ * <BR><a href="http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html">
+ * <code>http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html</code></a>
+ *
+ * <P>
+ * The variant argument is a vendor or browser-specific code.
+ * For example, use WIN for Windows, MAC for Macintosh, and POSIX for POSIX.
+ * Where there are two variants, separate them with an underscore, and
+ * put the most important one first. For example, a Traditional Spanish collation
+ * might construct a locale with parameters for language, country and variant as:
+ * "es", "ES", "Traditional_WIN".
+ *
+ * <P>
+ * Because a <code>Locale</code> object is just an identifier for a region,
+ * no validity check is performed when you construct a <code>Locale</code>.
+ * If you want to see whether particular resources are available for the
+ * <code>Locale</code> you construct, you must query those resources. For
+ * example, ask the <code>NumberFormat</code> for the locales it supports
+ * using its <code>getAvailableLocales</code> method.
+ * <BR><STRONG>Note:</STRONG> When you ask for a resource for a particular
+ * locale, you get back the best available match, not necessarily
+ * precisely what you asked for. For more information, look at
+ * {@link ResourceBundle}.
+ *
+ * <P>
+ * The <code>Locale</code> class provides a number of convenient constants
+ * that you can use to create <code>Locale</code> objects for commonly used
+ * locales. For example, the following creates a <code>Locale</code> object
+ * for the United States:
+ * <blockquote>
+ * <pre>
+ * Locale.US
+ * </pre>
+ * </blockquote>
+ *
+ * <P>
+ * Once you've created a <code>Locale</code> you can query it for information about
+ * itself. Use <code>getCountry</code> to get the ISO Country Code and
+ * <code>getLanguage</code> to get the ISO Language Code. You can
+ * use <code>getDisplayCountry</code> to get the
+ * name of the country suitable for displaying to the user. Similarly,
+ * you can use <code>getDisplayLanguage</code> to get the name of
+ * the language suitable for displaying to the user. Interestingly,
+ * the <code>getDisplayXXX</code> methods are themselves locale-sensitive
+ * and have two versions: one that uses the default locale and one
+ * that uses the locale specified as an argument.
+ *
+ * <P>
+ * The Java Platform provides a number of classes that perform locale-sensitive
+ * operations. For example, the <code>NumberFormat</code> class formats
+ * numbers, currency, or percentages in a locale-sensitive manner. Classes
+ * such as <code>NumberFormat</code> have a number of convenience methods
+ * for creating a default object of that type. For example, the
+ * <code>NumberFormat</code> class provides these three convenience methods
+ * for creating a default <code>NumberFormat</code> object:
+ * <blockquote>
+ * <pre>
+ * NumberFormat.getInstance()
+ * NumberFormat.getCurrencyInstance()
+ * NumberFormat.getPercentInstance()
+ * </pre>
+ * </blockquote>
+ * These methods have two variants; one with an explicit locale
+ * and one without; the latter using the default locale.
+ * <blockquote>
+ * <pre>
+ * NumberFormat.getInstance(myLocale)
+ * NumberFormat.getCurrencyInstance(myLocale)
+ * NumberFormat.getPercentInstance(myLocale)
+ * </pre>
+ * </blockquote>
+ * A <code>Locale</code> is the mechanism for identifying the kind of object
+ * (<code>NumberFormat</code>) that you would like to get. The locale is
+ * <STRONG>just</STRONG> a mechanism for identifying objects,
+ * <STRONG>not</STRONG> a container for the objects themselves.
+ *
+ * @see         ResourceBundle
+ * @see         java.text.Format
+ * @see         java.text.NumberFormat
+ * @see         java.text.Collator
+ * @author      Mark Davis
+ * @since       1.1
+ */
+
+public final class Locale implements Cloneable, Serializable {
+
+    // cache to store singleton Locales
+    private final static ConcurrentHashMap<String, Locale> cache =
+        new ConcurrentHashMap<String, Locale>(32);
+
+    /** Useful constant for language.
+     */
+    static public final Locale ENGLISH = createSingleton("en__", "en", "");
+
+    /** Useful constant for language.
+     */
+    static public final Locale FRENCH = createSingleton("fr__", "fr", "");
+
+    /** Useful constant for language.
+     */
+    static public final Locale GERMAN = createSingleton("de__", "de", "");
+
+    /** Useful constant for language.
+     */
+    static public final Locale ITALIAN = createSingleton("it__", "it", "");
+
+    /** Useful constant for language.
+     */
+    static public final Locale JAPANESE = createSingleton("ja__", "ja", "");
+
+    /** Useful constant for language.
+     */
+    static public final Locale KOREAN = createSingleton("ko__", "ko", "");
+
+    /** Useful constant for language.
+     */
+    static public final Locale CHINESE = createSingleton("zh__", "zh", "");
+
+    /** Useful constant for language.
+     */
+    static public final Locale SIMPLIFIED_CHINESE = createSingleton("zh_CN_", "zh", "CN");
+
+    /** Useful constant for language.
+     */
+    static public final Locale TRADITIONAL_CHINESE = createSingleton("zh_TW_", "zh", "TW");
+
+    /** Useful constant for country.
+     */
+    static public final Locale FRANCE = createSingleton("fr_FR_", "fr", "FR");
+
+    /** Useful constant for country.
+     */
+    static public final Locale GERMANY = createSingleton("de_DE_", "de", "DE");
+
+    /** Useful constant for country.
+     */
+    static public final Locale ITALY = createSingleton("it_IT_", "it", "IT");
+
+    /** Useful constant for country.
+     */
+    static public final Locale JAPAN = createSingleton("ja_JP_", "ja", "JP");
+
+    /** Useful constant for country.
+     */
+    static public final Locale KOREA = createSingleton("ko_KR_", "ko", "KR");
+
+    /** Useful constant for country.
+     */
+    static public final Locale CHINA = SIMPLIFIED_CHINESE;
+
+    /** Useful constant for country.
+     */
+    static public final Locale PRC = SIMPLIFIED_CHINESE;
+
+    /** Useful constant for country.
+     */
+    static public final Locale TAIWAN = TRADITIONAL_CHINESE;
+
+    /** Useful constant for country.
+     */
+    static public final Locale UK = createSingleton("en_GB_", "en", "GB");
+
+    /** Useful constant for country.
+     */
+    static public final Locale US = createSingleton("en_US_", "en", "US");
+
+    /** Useful constant for country.
+     */
+    static public final Locale CANADA = createSingleton("en_CA_", "en", "CA");
+
+    /** Useful constant for country.
+     */
+    static public final Locale CANADA_FRENCH = createSingleton("fr_CA_", "fr", "CA");
+
+    /**
+     * Useful constant for the root locale.  The root locale is the locale whose
+     * language, country, and variant are empty ("") strings.  This is regarded
+     * as the base locale of all locales, and is used as the language/country
+     * neutral locale for the locale sensitive operations.
+     *
+     * @since 1.6
+     */
+    static public final Locale ROOT = createSingleton("__", "", "");
+
+    /** serialization ID
+     */
+    static final long serialVersionUID = 9149081749638150636L;
+
+    /**
+     * Display types for retrieving localized names from the name providers.
+     */
+    private static final int DISPLAY_LANGUAGE = 0;
+    private static final int DISPLAY_COUNTRY  = 1;
+    private static final int DISPLAY_VARIANT  = 2;
+
+    /**
+     * Construct a locale from language, country, variant.
+     * NOTE:  ISO 639 is not a stable standard; some of the language codes it defines
+     * (specifically iw, ji, and in) have changed.  This constructor accepts both the
+     * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
+     * API on Locale will return only the OLD codes.
+     * @param language lowercase two-letter ISO-639 code.
+     * @param country uppercase two-letter ISO-3166 code.
+     * @param variant vendor and browser specific code. See class description.
+     * @exception NullPointerException thrown if any argument is null.
+     */
+    public Locale(String language, String country, String variant) {
+        this.language = convertOldISOCodes(language);
+        this.country = toUpperCase(country).intern();
+        this.variant = variant.intern();
+    }
+
+    /**
+     * Construct a locale from language, country.
+     * NOTE:  ISO 639 is not a stable standard; some of the language codes it defines
+     * (specifically iw, ji, and in) have changed.  This constructor accepts both the
+     * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
+     * API on Locale will return only the OLD codes.
+     * @param language lowercase two-letter ISO-639 code.
+     * @param country uppercase two-letter ISO-3166 code.
+     * @exception NullPointerException thrown if either argument is null.
+     */
+    public Locale(String language, String country) {
+        this(language, country, "");
+    }
+
+    /**
+     * Construct a locale from a language code.
+     * NOTE:  ISO 639 is not a stable standard; some of the language codes it defines
+     * (specifically iw, ji, and in) have changed.  This constructor accepts both the
+     * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
+     * API on Locale will return only the OLD codes.
+     * @param language lowercase two-letter ISO-639 code.
+     * @exception NullPointerException thrown if argument is null.
+     * @since 1.4
+     */
+    public Locale(String language) {
+        this(language, "", "");
+    }
+
+    /**
+     * Constructs a <code>Locale</code> using <code>language</code>
+     * and <code>country</code>.  This constructor assumes that
+     * <code>language</code> and <code>contry</code> are interned and
+     * it is invoked by createSingleton only. (flag is just for
+     * avoiding the conflict with the public constructors.
+     */
+    private Locale(String language, String country, boolean flag) {
+        this.language = language;
+        this.country = country;
+        this.variant = "";
+    }
+
+    /**
+     * Creates a <code>Locale</code> instance with the given
+     * <code>language</code> and <code>counry</code> and puts the
+     * instance under the given <code>key</code> in the cache. This
+     * method must be called only when initializing the Locale
+     * constants.
+     */
+    private static Locale createSingleton(String key, String language, String country) {
+        Locale locale = new Locale(language, country, false);
+        cache.put(key, locale);
+        return locale;
+    }
+
+    /**
+     * Returns a <code>Locale</code> constructed from the given
+     * <code>language</code>, <code>country</code> and
+     * <code>variant</code>. If the same <code>Locale</code> instance
+     * is available in the cache, then that instance is
+     * returned. Otherwise, a new <code>Locale</code> instance is
+     * created and cached.
+     *
+     * @param language lowercase two-letter ISO-639 code.
+     * @param country uppercase two-letter ISO-3166 code.
+     * @param variant vendor and browser specific code. See class description.
+     * @return the <code>Locale</code> instance requested
+     * @exception NullPointerException if any argument is null.
+     */
+    static Locale getInstance(String language, String country, String variant) {
+        if (language== null || country == null || variant == null) {
+            throw new NullPointerException();
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append(language).append('_').append(country).append('_').append(variant);
+        String key = sb.toString();
+        Locale locale = cache.get(key);
+        if (locale == null) {
+            locale = new Locale(language, country, variant);
+            Locale l = cache.putIfAbsent(key, locale);
+            if (l != null) {
+                locale = l;
+            }
+        }
+        return locale;
+    }
+
+    /**
+     * Gets the current value of the default locale for this instance
+     * of the Java Virtual Machine.
+     * <p>
+     * The Java Virtual Machine sets the default locale during startup
+     * based on the host environment. It is used by many locale-sensitive
+     * methods if no locale is explicitly specified.
+     * It can be changed using the
+     * {@link #setDefault(java.util.Locale) setDefault} method.
+     *
+     * @return the default locale for this instance of the Java Virtual Machine
+     */
+    public static Locale getDefault() {
+        // do not synchronize this method - see 4071298
+        // it's OK if more than one default locale happens to be created
+        if (defaultLocale == null) {
+            String language, region, country, variant;
+            language = AccessController.doPrivileged(
+                new GetPropertyAction("user.language", "en"));
+            // for compatibility, check for old user.region property
+            region = AccessController.doPrivileged(
+                new GetPropertyAction("user.region"));
+            if (region != null) {
+                // region can be of form country, country_variant, or _variant
+                int i = region.indexOf('_');
+                if (i >= 0) {
+                    country = region.substring(0, i);
+                    variant = region.substring(i + 1);
+                } else {
+                    country = region;
+                    variant = "";
+                }
+            } else {
+                country = AccessController.doPrivileged(
+                    new GetPropertyAction("user.country", ""));
+                variant = AccessController.doPrivileged(
+                    new GetPropertyAction("user.variant", ""));
+            }
+            defaultLocale = getInstance(language, country, variant);
+        }
+        return defaultLocale;
+    }
+
+    /**
+     * Sets the default locale for this instance of the Java Virtual Machine.
+     * This does not affect the host locale.
+     * <p>
+     * If there is a security manager, its <code>checkPermission</code>
+     * method is called with a <code>PropertyPermission("user.language", "write")</code>
+     * permission before the default locale is changed.
+     * <p>
+     * The Java Virtual Machine sets the default locale during startup
+     * based on the host environment. It is used by many locale-sensitive
+     * methods if no locale is explicitly specified.
+     * <p>
+     * Since changing the default locale may affect many different areas
+     * of functionality, this method should only be used if the caller
+     * is prepared to reinitialize locale-sensitive code running
+     * within the same Java Virtual Machine.
+     *
+     * @throws SecurityException
+     *        if a security manager exists and its
+     *        <code>checkPermission</code> method doesn't allow the operation.
+     * @throws NullPointerException if <code>newLocale</code> is null
+     * @param newLocale the new default locale
+     * @see SecurityManager#checkPermission
+     * @see java.util.PropertyPermission
+     */
+    public static synchronized void setDefault(Locale newLocale) {
+        if (newLocale == null)
+            throw new NullPointerException("Can't set default locale to NULL");
+
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) sm.checkPermission(new PropertyPermission
+                        ("user.language", "write"));
+            defaultLocale = newLocale;
+    }
+
+    /**
+     * Returns an array of all installed locales.
+     * The returned array represents the union of locales supported
+     * by the Java runtime environment and by installed
+     * {@link java.util.spi.LocaleServiceProvider LocaleServiceProvider}
+     * implementations.  It must contain at least a <code>Locale</code>
+     * instance equal to {@link java.util.Locale#US Locale.US}.
+     *
+     * @return An array of installed locales.
+     */
+    public static Locale[] getAvailableLocales() {
+        return LocaleServiceProviderPool.getAllAvailableLocales();
+    }
+
+    /**
+     * Returns a list of all 2-letter country codes defined in ISO 3166.
+     * Can be used to create Locales.
+     */
+    public static String[] getISOCountries() {
+        if (isoCountries == null) {
+            isoCountries = getISO2Table(LocaleISOData.isoCountryTable);
+        }
+        String[] result = new String[isoCountries.length];
+        System.arraycopy(isoCountries, 0, result, 0, isoCountries.length);
+        return result;
+    }
+
+    /**
+     * Returns a list of all 2-letter language codes defined in ISO 639.
+     * Can be used to create Locales.
+     * [NOTE:  ISO 639 is not a stable standard-- some languages' codes have changed.
+     * The list this function returns includes both the new and the old codes for the
+     * languages whose codes have changed.]
+     */
+    public static String[] getISOLanguages() {
+        if (isoLanguages == null) {
+            isoLanguages = getISO2Table(LocaleISOData.isoLanguageTable);
+        }
+        String[] result = new String[isoLanguages.length];
+        System.arraycopy(isoLanguages, 0, result, 0, isoLanguages.length);
+        return result;
+    }
+
+    private static final String[] getISO2Table(String table) {
+        int len = table.length() / 5;
+        String[] isoTable = new String[len];
+        for (int i = 0, j = 0; i < len; i++, j += 5) {
+            isoTable[i] = table.substring(j, j + 2);
+        }
+        return isoTable;
+    }
+
+    /**
+     * Returns the language code for this locale, which will either be the empty string
+     * or a lowercase ISO 639 code.
+     * <p>NOTE:  ISO 639 is not a stable standard-- some languages' codes have changed.
+     * Locale's constructor recognizes both the new and the old codes for the languages
+     * whose codes have changed, but this function always returns the old code.  If you
+     * want to check for a specific language whose code has changed, don't do <pre>
+     * if (locale.getLanguage().equals("he"))
+     *    ...
+     * </pre>Instead, do<pre>
+     * if (locale.getLanguage().equals(new Locale("he", "", "").getLanguage()))
+     *    ...</pre>
+     * @see #getDisplayLanguage
+     */
+    public String getLanguage() {
+        return language;
+    }
+
+    /**
+     * Returns the country/region code for this locale, which will
+     * either be the empty string or an uppercase ISO 3166 2-letter code.
+     * @see #getDisplayCountry
+     */
+    public String getCountry() {
+        return country;
+    }
+
+    /**
+     * Returns the variant code for this locale.
+     * @see #getDisplayVariant
+     */
+    public String getVariant() {
+        return variant;
+    }
+
+    /**
+     * Getter for the programmatic name of the entire locale,
+     * with the language, country and variant separated by underbars.
+     * Language is always lower case, and country is always upper case.
+     * If the language is missing, the string will begin with an underbar.
+     * If both the language and country fields are missing, this function
+     * will return the empty string, even if the variant field is filled in
+     * (you can't have a locale with just a variant-- the variant must accompany
+     * a valid language or country code).
+     * Examples: "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC"
+     * @see #getDisplayName
+     */
+    public final String toString() {
+        boolean l = language.length() != 0;
+        boolean c = country.length() != 0;
+        boolean v = variant.length() != 0;
+        StringBuilder result = new StringBuilder(language);
+        if (c||(l&&v)) {
+            result.append('_').append(country); // This may just append '_'
+        }
+        if (v&&(l||c)) {
+            result.append('_').append(variant);
+        }
+        return result.toString();
+    }
+
+    /**
+     * Returns a three-letter abbreviation for this locale's language.  If the locale
+     * doesn't specify a language, this will be the empty string.  Otherwise, this will
+     * be a lowercase ISO 639-2/T language code.
+     * The ISO 639-2 language codes can be found on-line at
+     * <a href="http://www.loc.gov/standards/iso639-2/englangn.html">
+     * <code>http://www.loc.gov/standards/iso639-2/englangn.html</code>.</a>
+     * @exception MissingResourceException Throws MissingResourceException if the
+     * three-letter language abbreviation is not available for this locale.
+     */
+    public String getISO3Language() throws MissingResourceException {
+        String language3 = getISO3Code(language, LocaleISOData.isoLanguageTable);
+        if (language3 == null) {
+            throw new MissingResourceException("Couldn't find 3-letter language code for "
+                    + language, "FormatData_" + toString(), "ShortLanguage");
+        }
+        return language3;
+    }
+
+    /**
+     * Returns a three-letter abbreviation for this locale's country.  If the locale
+     * doesn't specify a country, this will be the empty string.  Otherwise, this will
+     * be an uppercase ISO 3166 3-letter country code.
+     * The ISO 3166-2 country codes can be found on-line at
+     * <a href="http://www.davros.org/misc/iso3166.txt">
+     * <code>http://www.davros.org/misc/iso3166.txt</code>.</a>
+     * @exception MissingResourceException Throws MissingResourceException if the
+     * three-letter country abbreviation is not available for this locale.
+     */
+    public String getISO3Country() throws MissingResourceException {
+        String country3 = getISO3Code(country, LocaleISOData.isoCountryTable);
+        if (country3 == null) {
+            throw new MissingResourceException("Couldn't find 3-letter country code for "
+                    + country, "FormatData_" + toString(), "ShortCountry");
+        }
+        return country3;
+    }
+
+    private static final String getISO3Code(String iso2Code, String table) {
+        int codeLength = iso2Code.length();
+        if (codeLength == 0) {
+            return "";
+        }
+
+        int tableLength = table.length();
+        int index = tableLength;
+        if (codeLength == 2) {
+            char c1 = iso2Code.charAt(0);
+            char c2 = iso2Code.charAt(1);
+            for (index = 0; index < tableLength; index += 5) {
+                if (table.charAt(index) == c1
+                    && table.charAt(index + 1) == c2) {
+                    break;
+                }
+            }
+        }
+        return index < tableLength ? table.substring(index + 2, index + 5) : null;
+    }
+
+    /**
+     * Returns a name for the locale's language that is appropriate for display to the
+     * user.
+     * If possible, the name returned will be localized for the default locale.
+     * For example, if the locale is fr_FR and the default locale
+     * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
+     * the default locale is fr_FR, getDisplayLanguage() will return "anglais".
+     * If the name returned cannot be localized for the default locale,
+     * (say, we don't have a Japanese name for Croatian),
+     * this function falls back on the English name, and uses the ISO code as a last-resort
+     * value.  If the locale doesn't specify a language, this function returns the empty string.
+     */
+    public final String getDisplayLanguage() {
+        return getDisplayLanguage(getDefault());
+    }
+
+    /**
+     * Returns a name for the locale's language that is appropriate for display to the
+     * user.
+     * If possible, the name returned will be localized according to inLocale.
+     * For example, if the locale is fr_FR and inLocale
+     * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
+     * inLocale is fr_FR, getDisplayLanguage() will return "anglais".
+     * If the name returned cannot be localized according to inLocale,
+     * (say, we don't have a Japanese name for Croatian),
+     * this function falls back on the English name, and finally
+     * on the ISO code as a last-resort value.  If the locale doesn't specify a language,
+     * this function returns the empty string.
+     *
+     * @exception NullPointerException if <code>inLocale</code> is <code>null</code>
+     */
+    public String getDisplayLanguage(Locale inLocale) {
+        return getDisplayString(language, inLocale, DISPLAY_LANGUAGE);
+    }
+
+    /**
+     * Returns a name for the locale's country that is appropriate for display to the
+     * user.
+     * If possible, the name returned will be localized for the default locale.
+     * For example, if the locale is fr_FR and the default locale
+     * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
+     * the default locale is fr_FR, getDisplayCountry() will return "Etats-Unis".
+     * If the name returned cannot be localized for the default locale,
+     * (say, we don't have a Japanese name for Croatia),
+     * this function falls back on the English name, and uses the ISO code as a last-resort
+     * value.  If the locale doesn't specify a country, this function returns the empty string.
+     */
+    public final String getDisplayCountry() {
+        return getDisplayCountry(getDefault());
+    }
+
+    /**
+     * Returns a name for the locale's country that is appropriate for display to the
+     * user.
+     * If possible, the name returned will be localized according to inLocale.
+     * For example, if the locale is fr_FR and inLocale
+     * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
+     * inLocale is fr_FR, getDisplayCountry() will return "Etats-Unis".
+     * If the name returned cannot be localized according to inLocale.
+     * (say, we don't have a Japanese name for Croatia),
+     * this function falls back on the English name, and finally
+     * on the ISO code as a last-resort value.  If the locale doesn't specify a country,
+     * this function returns the empty string.
+     *
+     * @exception NullPointerException if <code>inLocale</code> is <code>null</code>
+     */
+    public String getDisplayCountry(Locale inLocale) {
+        return getDisplayString(country, inLocale, DISPLAY_COUNTRY);
+    }
+
+    private String getDisplayString(String code, Locale inLocale, int type) {
+        if (code.length() == 0) {
+            return "";
+        }
+
+        if (inLocale == null) {
+            throw new NullPointerException();
+        }
+
+        try {
+            OpenListResourceBundle bundle = LocaleData.getLocaleNames(inLocale);
+            String key = (type == DISPLAY_VARIANT ? "%%"+code : code);
+            String result = null;
+
+            // Check whether a provider can provide an implementation that's closer
+            // to the requested locale than what the Java runtime itself can provide.
+            LocaleServiceProviderPool pool =
+                LocaleServiceProviderPool.getPool(LocaleNameProvider.class);
+            if (pool.hasProviders()) {
+                result = pool.getLocalizedObject(
+                                    LocaleNameGetter.INSTANCE,
+                                    inLocale, bundle, key,
+                                    type, code);
+            }
+
+            if (result == null) {
+                result = bundle.getString(key);
+            }
+
+            if (result != null) {
+                return result;
+            }
+        }
+        catch (Exception e) {
+            // just fall through
+        }
+        return code;
+    }
+
+    /**
+     * Returns a name for the locale's variant code that is appropriate for display to the
+     * user.  If possible, the name will be localized for the default locale.  If the locale
+     * doesn't specify a variant code, this function returns the empty string.
+     */
+    public final String getDisplayVariant() {
+        return getDisplayVariant(getDefault());
+    }
+
+    /**
+     * Returns a name for the locale's variant code that is appropriate for display to the
+     * user.  If possible, the name will be localized for inLocale.  If the locale
+     * doesn't specify a variant code, this function returns the empty string.
+     *
+     * @exception NullPointerException if <code>inLocale</code> is <code>null</code>
+     */
+    public String getDisplayVariant(Locale inLocale) {
+        if (variant.length() == 0)
+            return "";
+
+        OpenListResourceBundle bundle = LocaleData.getLocaleNames(inLocale);
+
+        String names[] = getDisplayVariantArray(bundle, inLocale);
+
+        // Get the localized patterns for formatting a list, and use
+        // them to format the list.
+        String listPattern = null;
+        String listCompositionPattern = null;
+        try {
+            listPattern = bundle.getString("ListPattern");
+            listCompositionPattern = bundle.getString("ListCompositionPattern");
+        } catch (MissingResourceException e) {
+        }
+        return formatList(names, listPattern, listCompositionPattern);
+    }
+
+    /**
+     * Returns a name for the locale that is appropriate for display to the
+     * user.  This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
+     * and getDisplayVariant() assembled into a single string.  The display name will have
+     * one of the following forms:<p><blockquote>
+     * language (country, variant)<p>
+     * language (country)<p>
+     * language (variant)<p>
+     * country (variant)<p>
+     * language<p>
+     * country<p>
+     * variant<p></blockquote>
+     * depending on which fields are specified in the locale.  If the language, country,
+     * and variant fields are all empty, this function returns the empty string.
+     */
+    public final String getDisplayName() {
+        return getDisplayName(getDefault());
+    }
+
+    /**
+     * Returns a name for the locale that is appropriate for display to the
+     * user.  This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
+     * and getDisplayVariant() assembled into a single string.  The display name will have
+     * one of the following forms:<p><blockquote>
+     * language (country, variant)<p>
+     * language (country)<p>
+     * language (variant)<p>
+     * country (variant)<p>
+     * language<p>
+     * country<p>
+     * variant<p></blockquote>
+     * depending on which fields are specified in the locale.  If the language, country,
+     * and variant fields are all empty, this function returns the empty string.
+     *
+     * @exception NullPointerException if <code>inLocale</code> is <code>null</code>
+     */
+    public String getDisplayName(Locale inLocale) {
+        OpenListResourceBundle bundle = LocaleData.getLocaleNames(inLocale);
+
+        String languageName = getDisplayLanguage(inLocale);
+        String countryName = getDisplayCountry(inLocale);
+        String[] variantNames = getDisplayVariantArray(bundle, inLocale);
+
+        // Get the localized patterns for formatting a display name.
+        String displayNamePattern = null;
+        String listPattern = null;
+        String listCompositionPattern = null;
+        try {
+            displayNamePattern = bundle.getString("DisplayNamePattern");
+            listPattern = bundle.getString("ListPattern");
+            listCompositionPattern = bundle.getString("ListCompositionPattern");
+        } catch (MissingResourceException e) {
+        }
+
+        // The display name consists of a main name, followed by qualifiers.
+        // Typically, the format is "MainName (Qualifier, Qualifier)" but this
+        // depends on what pattern is stored in the display locale.
+        String   mainName       = null;
+        String[] qualifierNames = null;
+
+        // The main name is the language, or if there is no language, the country.
+        // If there is neither language nor country (an anomalous situation) then
+        // the display name is simply the variant's display name.
+        if (languageName.length() != 0) {
+            mainName = languageName;
+            if (countryName.length() != 0) {
+                qualifierNames = new String[variantNames.length + 1];
+                System.arraycopy(variantNames, 0, qualifierNames, 1, variantNames.length);
+                qualifierNames[0] = countryName;
+            }
+            else qualifierNames = variantNames;
+        }
+        else if (countryName.length() != 0) {
+            mainName = countryName;
+            qualifierNames = variantNames;
+        }
+        else {
+            return formatList(variantNames, listPattern, listCompositionPattern);
+        }
+
+        // Create an array whose first element is the number of remaining
+        // elements.  This serves as a selector into a ChoiceFormat pattern from
+        // the resource.  The second and third elements are the main name and
+        // the qualifier; if there are no qualifiers, the third element is
+        // unused by the format pattern.
+        Object[] displayNames = {
+            new Integer(qualifierNames.length != 0 ? 2 : 1),
+            mainName,
+            // We could also just call formatList() and have it handle the empty
+            // list case, but this is more efficient, and we want it to be
+            // efficient since all the language-only locales will not have any
+            // qualifiers.
+            qualifierNames.length != 0 ? formatList(qualifierNames, listPattern, listCompositionPattern) : null
+        };
+
+        if (displayNamePattern != null) {
+            return new MessageFormat(displayNamePattern).format(displayNames);
+        }
+        else {
+            // If we cannot get the message format pattern, then we use a simple
+            // hard-coded pattern.  This should not occur in practice unless the
+            // installation is missing some core files (FormatData etc.).
+            StringBuilder result = new StringBuilder();
+            result.append((String)displayNames[1]);
+            if (displayNames.length > 2) {
+                result.append(" (");
+                result.append((String)displayNames[2]);
+                result.append(')');
+            }
+            return result.toString();
+        }
+    }
+
+    /**
+     * Overrides Cloneable
+     */
+    public Object clone()
+    {
+        try {
+            Locale that = (Locale)super.clone();
+            return that;
+        } catch (CloneNotSupportedException e) {
+            throw new InternalError();
+        }
+    }
+
+    /**
+     * Override hashCode.
+     * Since Locales are often used in hashtables, caches the value
+     * for speed.
+     */
+    public int hashCode() {
+        int hc = hashCodeValue;
+        if (hc == 0) {
+            hc = (language.hashCode() << 8) ^ country.hashCode() ^ (variant.hashCode() << 4);
+            hashCodeValue = hc;
+        }
+        return hc;
+    }
+
+    // Overrides
+
+    /**
+     * Returns true if this Locale is equal to another object.  A Locale is
+     * deemed equal to another Locale with identical language, country,
+     * and variant, and unequal to all other objects.
+     *
+     * @return true if this Locale is equal to the specified object.
+     */
+
+    public boolean equals(Object obj) {
+        if (this == obj)                      // quick check
+            return true;
+        if (!(obj instanceof Locale))
+            return false;
+        Locale other = (Locale) obj;
+        return language == other.language
+            && country == other.country
+            && variant == other.variant;
+    }
+
+    // ================= privates =====================================
+
+    // XXX instance and class variables. For now keep these separate, since it is
+    // faster to match. Later, make into single string.
+
+    /**
+     * @serial
+     * @see #getLanguage
+     */
+    private final String language;
+
+    /**
+     * @serial
+     * @see #getCountry
+     */
+    private final String country;
+
+    /**
+     * @serial
+     * @see #getVariant
+     */
+    private final String variant;
+
+    /**
+     * Placeholder for the object's hash code.  Always -1.
+     * @serial
+     */
+    private volatile int hashcode = -1;        // lazy evaluate
+
+    /**
+     * Calculated hashcode to fix 4518797.
+     */
+    private transient volatile int hashCodeValue = 0;
+
+    private static Locale defaultLocale = null;
+
+    /**
+     * Return an array of the display names of the variant.
+     * @param bundle the ResourceBundle to use to get the display names
+     * @return an array of display names, possible of zero length.
+     */
+    private String[] getDisplayVariantArray(OpenListResourceBundle bundle, Locale inLocale) {
+        // Split the variant name into tokens separated by '_'.
+        StringTokenizer tokenizer = new StringTokenizer(variant, "_");
+        String[] names = new String[tokenizer.countTokens()];
+
+        // For each variant token, lookup the display name.  If
+        // not found, use the variant name itself.
+        for (int i=0; i<names.length; ++i) {
+            names[i] = getDisplayString(tokenizer.nextToken(),
+                                inLocale, DISPLAY_VARIANT);
+        }
+
+        return names;
+    }
+
+    /**
+     * Format a list using given pattern strings.
+     * If either of the patterns is null, then a the list is
+     * formatted by concatenation with the delimiter ','.
+     * @param stringList the list of strings to be formatted.
+     * @param listPattern should create a MessageFormat taking 0-3 arguments
+     * and formatting them into a list.
+     * @param listCompositionPattern should take 2 arguments
+     * and is used by composeList.
+     * @return a string representing the list.
+     */
+    private static String formatList(String[] stringList, String listPattern, String listCompositionPattern) {
+        // If we have no list patterns, compose the list in a simple,
+        // non-localized way.
+        if (listPattern == null || listCompositionPattern == null) {
+            StringBuffer result = new StringBuffer();
+            for (int i=0; i<stringList.length; ++i) {
+                if (i>0) result.append(',');
+                result.append(stringList[i]);
+            }
+            return result.toString();
+        }
+
+        // Compose the list down to three elements if necessary
+        if (stringList.length > 3) {
+            MessageFormat format = new MessageFormat(listCompositionPattern);
+            stringList = composeList(format, stringList);
+        }
+
+        // Rebuild the argument list with the list length as the first element
+        Object[] args = new Object[stringList.length + 1];
+        System.arraycopy(stringList, 0, args, 1, stringList.length);
+        args[0] = new Integer(stringList.length);
+
+        // Format it using the pattern in the resource
+        MessageFormat format = new MessageFormat(listPattern);
+        return format.format(args);
+    }
+
+    /**
+     * Given a list of strings, return a list shortened to three elements.
+     * Shorten it by applying the given format to the first two elements
+     * recursively.
+     * @param format a format which takes two arguments
+     * @param list a list of strings
+     * @return if the list is three elements or shorter, the same list;
+     * otherwise, a new list of three elements.
+     */
+    private static String[] composeList(MessageFormat format, String[] list) {
+        if (list.length <= 3) return list;
+
+        // Use the given format to compose the first two elements into one
+        String[] listItems = { list[0], list[1] };
+        String newItem = format.format(listItems);
+
+        // Form a new list one element shorter
+        String[] newList = new String[list.length-1];
+        System.arraycopy(list, 2, newList, 1, newList.length-1);
+        newList[0] = newItem;
+
+        // Recurse
+        return composeList(format, newList);
+    }
+
+    /**
+     * Replace the deserialized Locale object with a newly
+     * created object. Newer language codes are replaced with older ISO
+     * codes. The country and variant codes are replaced with internalized
+     * String copies.
+     */
+    private Object readResolve() throws java.io.ObjectStreamException {
+        return getInstance(language, country, variant);
+    }
+
+    private static volatile String[] isoLanguages = null;
+
+    private static volatile String[] isoCountries = null;
+
+    /*
+     * Locale needs its own, locale insensitive version of toLowerCase to
+     * avoid circularity problems between Locale and String.
+     * The most straightforward algorithm is used. Look at optimizations later.
+     */
+    private String toLowerCase(String str) {
+        char[] buf = new char[str.length()];
+        for (int i = 0; i < buf.length; i++) {
+            buf[i] = Character.toLowerCase(str.charAt(i));
+        }
+        return new String( buf );
+    }
+
+    /*
+     * Locale needs its own, locale insensitive version of toUpperCase to
+     * avoid circularity problems between Locale and String.
+     * The most straightforward algorithm is used. Look at optimizations later.
+     */
+    private String toUpperCase(String str) {
+        char[] buf = new char[str.length()];
+        for (int i = 0; i < buf.length; i++) {
+            buf[i] = Character.toUpperCase(str.charAt(i));
+        }
+        return new String( buf );
+    }
+
+    private String convertOldISOCodes(String language) {
+        // we accept both the old and the new ISO codes for the languages whose ISO
+        // codes have changed, but we always store the OLD code, for backward compatibility
+        language = toLowerCase(language).intern();
+        if (language == "he") {
+            return "iw";
+        } else if (language == "yi") {
+            return "ji";
+        } else if (language == "id") {
+            return "in";
+        } else {
+            return language;
+        }
+    }
+
+    /**
+     * Obtains a localized locale names from a LocaleNameProvider
+     * implementation.
+     */
+    private static class LocaleNameGetter
+        implements LocaleServiceProviderPool.LocalizedObjectGetter<LocaleNameProvider, String> {
+        private static final LocaleNameGetter INSTANCE = new LocaleNameGetter();
+
+        public String getObject(LocaleNameProvider localeNameProvider,
+                                Locale locale,
+                                String key,
+                                Object... params) {
+            assert params.length == 2;
+            int type = (Integer)params[0];
+            String code = (String)params[1];
+
+            switch(type) {
+            case DISPLAY_LANGUAGE:
+                return localeNameProvider.getDisplayLanguage(code, locale);
+            case DISPLAY_COUNTRY:
+                return localeNameProvider.getDisplayCountry(code, locale);
+            case DISPLAY_VARIANT:
+                return localeNameProvider.getDisplayVariant(code, locale);
+            default:
+                assert false; // shouldn't happen
+            }
+
+            return null;
+        }
+    }
+}