home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / java / util / Locale.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  50.8 KB  |  1,218 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)Locale.java    1.47 98/10/02
  3.  *
  4.  * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
  5.  * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
  6.  *
  7.  * Portions copyright (c) 1996-1998 Sun Microsystems, Inc.
  8.  * All Rights Reserved.
  9.  *
  10.  * The original version of this source code and documentation
  11.  * is copyrighted and owned by Taligent, Inc., a wholly-owned
  12.  * subsidiary of IBM. These materials are provided under terms
  13.  * of a License Agreement between Taligent and Sun. This technology
  14.  * is protected by multiple US and International patents.
  15.  *
  16.  * This notice and attribution to Taligent may not be removed.
  17.  * Taligent is a registered trademark of Taligent, Inc.
  18.  *
  19.  * Permission to use, copy, modify, and distribute this software
  20.  * and its documentation for NON-COMMERCIAL purposes and without
  21.  * fee is hereby granted provided that this copyright notice
  22.  * appears in all copies. Please refer to the file "copyright.html"
  23.  * for further important copyright and licensing information.
  24.  *
  25.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  26.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  27.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  28.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  29.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  30.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  31.  *
  32.  */
  33.  
  34. package java.util;
  35. import java.io.*;
  36. import java.security.AccessController;
  37. import sun.security.action.GetPropertyAction;
  38. import java.text.resources.LocaleData;
  39. import java.text.MessageFormat;
  40.  
  41. /**
  42.  *
  43.  * A <code>Locale</code> object represents a specific geographical, political,
  44.  * or cultural region. An operation that requires a <code>Locale</code> to perform
  45.  * its task is called <em>locale-sensitive</em> and uses the <code>Locale</code>
  46.  * to tailor information for the user. For example, displaying a number
  47.  * is a locale-sensitive operation--the number should be formatted
  48.  * according to the customs/conventions of the user's native country,
  49.  * region, or culture.
  50.  *
  51.  * <P>
  52.  * You create a <code>Locale</code> object using one of the two constructors in
  53.  * this class:
  54.  * <blockquote>
  55.  * <pre>
  56.  * Locale(String language, String country)
  57.  * Locale(String language, String country, String variant)
  58.  * </pre>
  59.  * </blockquote>
  60.  * The first argument to both constructors is a valid <STRONG>ISO
  61.  * Language Code.</STRONG> These codes are the lower-case two-letter
  62.  * codes as defined by ISO-639.
  63.  * You can find a full list of these codes at a number of sites, such as:
  64.  * <BR><a href ="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt">
  65.  * <code>http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt</code></a>
  66.  *
  67.  * <P>
  68.  * The second argument to both constructors is a valid <STRONG>ISO Country
  69.  * Code.</STRONG> These codes are the upper-case two-letter codes
  70.  * as defined by ISO-3166.
  71.  * You can find a full list of these codes at a number of sites, such as:
  72.  * <BR><a href="http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html">
  73.  * <code>http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html</code></a>
  74.  *
  75.  * <P>
  76.  * The second constructor requires a third argument--the <STRONG>Variant.</STRONG>
  77.  * The Variant codes are vendor and browser-specific.
  78.  * For example, use WIN for Windows, MAC for Macintosh, and POSIX for POSIX.
  79.  * Where there are two variants, separate them with an underscore, and
  80.  * put the most important one first. For
  81.  * example, a Traditional Spanish collation might construct a locale with
  82.  * parameters for language, country and variant as: "es", "ES", "Traditional_WIN".
  83.  *
  84.  * <P>
  85.  * Because a <code>Locale</code> object is just an identifier for a region,
  86.  * no validity check is performed when you construct a <code>Locale</code>.
  87.  * If you want to see whether particular resources are available for the
  88.  * <code>Locale</code> you construct, you must query those resources. For
  89.  * example, ask the <code>NumberFormat</code> for the locales it supports
  90.  * using its <code>getAvailableLocales</code> method.
  91.  * <BR><STRONG>Note:</STRONG> When you ask for a resource for a particular
  92.  * locale, you get back the best available match, not necessarily
  93.  * precisely what you asked for. For more information, look at
  94.  * {@link ResourceBundle}.
  95.  *
  96.  * <P>
  97.  * The <code>Locale</code> class provides a number of convenient constants
  98.  * that you can use to create <code>Locale</code> objects for commonly used
  99.  * locales. For example, the following creates a <code>Locale</code> object
  100.  * for the United States:
  101.  * <blockquote>
  102.  * <pre>
  103.  * Locale.US
  104.  * </pre>
  105.  * </blockquote>
  106.  *
  107.  * <P>
  108.  * Once you've created a <code>Locale</code> you can query it for information about
  109.  * itself. Use <code>getCountry</code> to get the ISO Country Code and
  110.  * <code>getLanguage</code> to get the ISO Language Code. You can
  111.  * use <code>getDisplayCountry</code> to get the
  112.  * name of the country suitable for displaying to the user. Similarly,
  113.  * you can use <code>getDisplayLanguage</code> to get the name of
  114.  * the language suitable for displaying to the user. Interestingly,
  115.  * the <code>getDisplayXXX</code> methods are themselves locale-sensitive
  116.  * and have two versions: one that uses the default locale and one
  117.  * that uses the locale specified as an argument.
  118.  *
  119.  * <P>
  120.  * The JDK provides a number of classes that perform locale-sensitive
  121.  * operations. For example, the <code>NumberFormat</code> class formats
  122.  * numbers, currency, or percentages in a locale-sensitive manner. Classes
  123.  * such as <code>NumberFormat</code> have a number of convenience methods
  124.  * for creating a default object of that type. For example, the
  125.  * <code>NumberFormat</code> class provides these three convenience methods
  126.  * for creating a default <code>NumberFormat</code> object:
  127.  * <blockquote>
  128.  * <pre>
  129.  * NumberFormat.getInstance()
  130.  * NumberFormat.getCurrencyInstance()
  131.  * NumberFormat.getPercentInstance()
  132.  * </pre>
  133.  * </blockquote>
  134.  * These methods have two variants; one with an explicit locale
  135.  * and one without; the latter using the default locale.
  136.  * <blockquote>
  137.  * <pre>
  138.  * NumberFormat.getInstance(myLocale)
  139.  * NumberFormat.getCurrencyInstance(myLocale)
  140.  * NumberFormat.getPercentInstance(myLocale)
  141.  * </pre>
  142.  * </blockquote>
  143.  * A <code>Locale</code> is the mechanism for identifying the kind of object
  144.  * (<code>NumberFormat</code>) that you would like to get. The locale is
  145.  * <STRONG>just</STRONG> a mechanism for identifying objects,
  146.  * <STRONG>not</STRONG> a container for the objects themselves.
  147.  *
  148.  * <P>
  149.  * Each class that performs locale-sensitive operations allows you
  150.  * to get all the available objects of that type. You can sift
  151.  * through these objects by language, country, or variant,
  152.  * and use the display names to present a menu to the user.
  153.  * For example, you can create a menu of all the collation objects
  154.  * suitable for a given language. Such classes must implement these
  155.  * three class methods:
  156.  * <blockquote>
  157.  * <pre>
  158.  * public static Locale[] getAvailableLocales()
  159.  * public static String getDisplayName(Locale objectLocale,
  160.  *                                     Locale displayLocale)
  161.  * public static final String getDisplayName(Locale objectLocale)
  162.  *     // getDisplayName will throw MissingResourceException if the locale
  163.  *     // is not one of the available locales.
  164.  * </pre>
  165.  * </blockquote>
  166.  *
  167.  * @see         ResourceBundle
  168.  * @see         java.text.Format
  169.  * @see         java.text.NumberFormat
  170.  * @see         java.text.Collator
  171.  * @version     1.21 29 Jan 1997
  172.  * @author      Mark Davis
  173.  */
  174.  
  175. public final class Locale implements Cloneable, Serializable {
  176.  
  177.     /** Useful constant for language.
  178.      */
  179.     static public final Locale ENGLISH = new Locale("en","","");
  180.  
  181.     /** Useful constant for language.
  182.      */
  183.     static public final Locale FRENCH = new Locale("fr","","");
  184.  
  185.     /** Useful constant for language.
  186.      */
  187.     static public final Locale GERMAN = new Locale("de","","");
  188.  
  189.     /** Useful constant for language.
  190.      */
  191.     static public final Locale ITALIAN = new Locale("it","","");
  192.  
  193.     /** Useful constant for language.
  194.      */
  195.     static public final Locale JAPANESE = new Locale("ja","","");
  196.  
  197.     /** Useful constant for language.
  198.      */
  199.     static public final Locale KOREAN = new Locale("ko","","");
  200.  
  201.     /** Useful constant for language.
  202.      */
  203.     static public final Locale CHINESE = new Locale("zh","","");
  204.  
  205.     /** Useful constant for language.
  206.      */
  207.     static public final Locale SIMPLIFIED_CHINESE = new Locale("zh","CN","");
  208.  
  209.     /** Useful constant for language.
  210.      */
  211.     static public final Locale TRADITIONAL_CHINESE = new Locale("zh","TW","");
  212.  
  213.     /** Useful constant for country.
  214.      */
  215.     static public final Locale FRANCE = new Locale("fr","FR","");
  216.  
  217.     /** Useful constant for country.
  218.      */
  219.     static public final Locale GERMANY = new Locale("de","DE","");
  220.  
  221.     /** Useful constant for country.
  222.      */
  223.     static public final Locale ITALY = new Locale("it","IT","");
  224.  
  225.     /** Useful constant for country.
  226.      */
  227.     static public final Locale JAPAN = new Locale("ja","JP","");
  228.  
  229.     /** Useful constant for country.
  230.      */
  231.     static public final Locale KOREA = new Locale("ko","KR","");
  232.  
  233.     /** Useful constant for country.
  234.      */
  235.     static public final Locale CHINA = new Locale("zh","CN","");
  236.  
  237.     /** Useful constant for country.
  238.      */
  239.     static public final Locale PRC = new Locale("zh","CN","");
  240.  
  241.     /** Useful constant for country.
  242.      */
  243.     static public final Locale TAIWAN = new Locale("zh","TW","");
  244.  
  245.     /** Useful constant for country.
  246.      */
  247.     static public final Locale UK = new Locale("en","GB","");
  248.  
  249.     /** Useful constant for country.
  250.      */
  251.     static public final Locale US = new Locale("en","US","");
  252.  
  253.     /** Useful constant for country.
  254.      */
  255.     static public final Locale CANADA = new Locale("en","CA","");
  256.  
  257.     /** Useful constant for country.
  258.      */
  259.     static public final Locale CANADA_FRENCH = new Locale("fr","CA","");
  260.  
  261.     /** serialization ID
  262.      */
  263.     static final long serialVersionUID = 9149081749638150636L;
  264.  
  265.     /**
  266.      * Construct a locale from language, country, variant.
  267.      * NOTE:  ISO 639 is not a stable standard; some of the language codes it defines
  268.      * (specifically iw, ji, and in) have changed.  This constructor accepts both the
  269.      * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
  270.      * API on Locale will return only the OLD codes.
  271.      * @param language lowercase two-letter ISO-639 code.
  272.      * @param country uppercase two-letter ISO-3166 code.
  273.      * @param variant vendor and browser specific code. See class description.
  274.      */
  275.     public Locale(String language, String country, String variant) {
  276.         // we accept both the old and the new ISO codes for the languages whose ISO
  277.         // codes have changed, but we always store the OLD code, for backward compatibility
  278.         language = toLowerCase(language).intern();
  279.         if (language == "he")
  280.             language = "iw";
  281.         else if (language == "yi")
  282.             language = "ji";
  283.         else if (language == "id")
  284.             language = "in";
  285.  
  286.         this.language = language;
  287.         this.country = toUpperCase(country).intern();
  288.         this.variant = toUpperCase(variant).intern();
  289.     }
  290.  
  291.     /**
  292.      * Construct a locale from language, country.
  293.      * NOTE:  ISO 639 is not a stable standard; some of the language codes it defines
  294.      * (specifically iw, ji, and in) have changed.  This constructor accepts both the
  295.      * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
  296.      * API on Locale will return only the OLD codes.
  297.      * @param language lowercase two-letter ISO-639 code.
  298.      * @param country uppercase two-letter ISO-3166 code.
  299.      */
  300.     public Locale(String language, String country) {
  301.         this(language, country, "");
  302.     }
  303.  
  304.     /**
  305.      * Common method of getting the current default Locale.
  306.      * Used for the presentation: menus, dialogs, etc.
  307.      * Generally set once when your applet or application is initialized,
  308.      * then never reset. (If you do reset the default locale, you
  309.      * probably want to reload your GUI, so that the change is reflected
  310.      * in your interface.)
  311.      * <p>More advanced programs will allow users to use different locales
  312.      * for different fields, e.g. in a spreadsheet.
  313.      * <BR>Note that the initial setting will match the host system.
  314.      */
  315.     public static Locale getDefault() {
  316.         return defaultLocale;   // this variable is now initialized at static init time
  317.     }
  318.  
  319.     /**
  320.      * Sets the default locale for the whole JVM.
  321.      * Normally set once at the beginning of an application,
  322.      * then never reset. <code>setDefault</code> does not reset the host locale.
  323.      * 
  324.      * <p>If there is a security manager, its <code>checkPermission</code> 
  325.      * method is called with a <code>PropertyPermission("user.language", "write")</code> 
  326.      * permission. 
  327.      * 
  328.      * @throws SecurityException
  329.      *        if a security manager exists and its 
  330.      *        <code>checkPermission</code> method doesn't allow the operation.
  331.      * @param newLocale The new default Locale.
  332.      * @see SecurityManager#checkPermission
  333.      * @see java.util.PropertyPermission
  334.      */
  335.     public static synchronized void setDefault(Locale newLocale) {
  336.         if (newLocale == null)
  337.             throw new NullPointerException("Can't set default locale to NULL");
  338.  
  339.         SecurityManager sm = System.getSecurityManager();
  340.         if (sm != null) sm.checkPermission(new PropertyPermission
  341.                         ("user.language", "write"));
  342.             defaultLocale = newLocale;
  343.     }
  344.  
  345.     /**
  346.      * Returns a list of all installed locales.
  347.      */
  348.     public static Locale[] getAvailableLocales() {
  349.         return LocaleData.getAvailableLocales("LocaleString");
  350.     }
  351.  
  352.     /**
  353.      * Returns a list of all 2-letter country codes defined in ISO 3166.
  354.      * Can be used to create Locales.
  355.      */
  356.     public static String[] getISOCountries() {
  357.         if (isoCountries == null) {
  358.             isoCountries = new String[compressedIsoCountries.length() / 6];
  359.             for (int i = 0; i < isoCountries.length; i++)
  360.                 isoCountries[i] = compressedIsoCountries.substring((i * 6) + 1, (i * 6) + 3);
  361.         }
  362.         String[] result = new String[isoCountries.length];
  363.         System.arraycopy(isoCountries, 0, result, 0, isoCountries.length);
  364.         return result;
  365.     }
  366.  
  367.     /**
  368.      * Returns a list of all 2-letter language codes defined in ISO 639.
  369.      * Can be used to create Locales.
  370.      * [NOTE:  ISO 639 is not a stable standard-- some languages' codes have changed.
  371.      * The list this function returns includes both the new and the old codes for the
  372.      * languages whose codes have changed.]
  373.      */
  374.     public static String[] getISOLanguages() {
  375.         if (isoLanguages == null) {
  376.             isoLanguages = new String[compressedIsoLanguages.length() / 6];
  377.             for (int i = 0; i < isoLanguages.length; i++)
  378.                 isoLanguages[i] = compressedIsoLanguages.substring((i * 6) + 1, (i * 6) + 3);
  379.         }
  380.         String[] result = new String[isoLanguages.length];
  381.         System.arraycopy(isoLanguages, 0, result, 0, isoLanguages.length);
  382.         return result;
  383.     }
  384.  
  385.     /**
  386.      * Given an ISO country code, returns an array of Strings containing the ISO
  387.      * codes of the languages spoken in that country.  Official languages are listed
  388.      * in the returned table before unofficial languages, but other than that, the
  389.      * order of the returned list is indeterminate.  If the value the user passes in
  390.      * for "country" is not a valid ISO 316 country code, or if we don't have language
  391.      * information for the specified country, this function returns an empty array.
  392.      *
  393.      * [This function is not currently part of Locale's API, but is needed in the
  394.      * implementation.  We hope to add it to the API in a future release.]
  395.      */
  396.     static String[] getLanguagesForCountry(String country) {
  397.         // To save on the size of a static array in the .class file, we keep the
  398.         // data around encoded into a String.  The first time this function is called,
  399.         // the String s parsed to produce a Hashtable, which is then used for all
  400.         // lookups.
  401.         if (ctry2LangMapping == null) {
  402.             ctry2LangMapping = new Hashtable();
  403.  
  404.             int i = 0;
  405.             int j;
  406.             while (i < compressedCtry2LangMapping.length()) {
  407.                 String key = compressedCtry2LangMapping.substring(i, i + 2);
  408.                 i += 2;
  409.                 for (j = i; j < compressedCtry2LangMapping.length(); j += 2)
  410.                     if (Character.isUpperCase(compressedCtry2LangMapping.charAt(j)))
  411.                         break;
  412.                 String compressedValues = compressedCtry2LangMapping.substring(i, j);
  413.                 String[] values = new String[compressedValues.length() / 2];
  414.                 for (int k = 0; k < values.length; k++)
  415.                     values[k] = compressedValues.substring(k * 2, (k * 2) + 2);
  416.                 ctry2LangMapping.put(key, values);
  417.                 i = j;
  418.             }
  419.         }
  420.         String[] result = (String[])ctry2LangMapping.get(country);
  421.         if (result == null)
  422.             result = new String[0];
  423.         return result;
  424.     }
  425.  
  426.     /**
  427.      * Returns the language code for this locale, which will either be the empty string
  428.      * or a lowercase ISO 639 code.
  429.      * <p>NOTE:  ISO 639 is not a stable standard-- some languages' codes have changed.
  430.      * Locale's constructor recognizes both the new and the old codes for the languages
  431.      * whose codes have changed, but this function always returns the old code.  If you
  432.      * want to check for a specific language whose code has changed, don't do <pre>
  433.      * if (locale.getLanguage().equals("he")
  434.      *    ...
  435.      * </pre>Instead, do<pre>
  436.      * if (locale.getLanguage().equals(new Locale("he", "", "").getLanguage())
  437.      *    ...</pre>
  438.      * @see #getDisplayLanguage
  439.      */
  440.     public String getLanguage() {
  441.         return language;
  442.     }
  443.  
  444.     /**
  445.      * Returns the country/region code for this locale, which will either be the empty string
  446.      * or an upercase ISO 3166 2-letter code.
  447.      * @see #getDisplayCountry
  448.      */
  449.     public String getCountry() {
  450.         return country;
  451.     }
  452.  
  453.     /**
  454.      * Returns the variant code for this locale.
  455.      * @see #getDisplayVariant
  456.      */
  457.     public String getVariant() {
  458.         return variant;
  459.     }
  460.  
  461.     /**
  462.      * Getter for the programmatic name of the entire locale,
  463.      * with the language, country and variant separated by underbars.
  464.      * Language is always lower case, and country is always upper case.
  465.      * If the language is missing, the string will begin with an underbar.
  466.      * If both the language and country fields are missing, this function
  467.      * will return the empty string, even if the variant field is filled in
  468.      * (you can't have a locale with just a variant-- the variant must accompany
  469.      * a valid language or country code).
  470.      * Examples: "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr_MAC"
  471.      * @see #getDisplayName
  472.      */
  473.     public final String toString() {
  474.     boolean l = language.length() != 0;
  475.     boolean c = country.length() != 0;
  476.     boolean v = variant.length() != 0;
  477.         StringBuffer result = new StringBuffer(language);
  478.     if (c||(l&&v)) {
  479.         result.append('_').append(country); // This may just append '_'
  480.     }
  481.     if (v&&(l||c)) {
  482.         result.append('_').append(variant);
  483.     }
  484.         return result.toString();
  485.     }
  486.  
  487.     /**
  488.      * Returns a three-letter abbreviation for this locale's language.  If the locale
  489.      * doesn't specify a language, this will be the empty string.  Otherwise, this will
  490.      * be a lowercase ISO 639-2/T language code.
  491.      * @exception MissingResourceException Throws MissingResourceException if the
  492.      * three-letter language abbreviation is not available for this locale.
  493.      */
  494.     public String getISO3Language() throws MissingResourceException {
  495.         int length = language.length();
  496.  
  497.         if (length == 0) {
  498.             return "";
  499.         }
  500.  
  501.         int index = compressedIsoLanguages.indexOf("," + language);
  502.         if (index == -1 || length != 2) {
  503.             throw new MissingResourceException("Couldn't find 3-letter language code for "
  504.                     + language, "LocaleElements_" + toString(), "ShortLanguage");
  505.         }
  506.         return compressedIsoLanguages.substring(index + 3, index + 6);
  507.     }
  508.  
  509.     /**
  510.      * Returns a three-letter abbreviation for this locale's country.  If the locale
  511.      * doesn't specify a country, this will be tbe the empty string.  Otherwise, this will
  512.      * be an uppercase ISO 3166 3-letter country code.
  513.      * @exception MissingResourceException Throws MissingResourceException if the
  514.      * three-letter country abbreviation is not available for this locale.
  515.      */
  516.     public String getISO3Country() throws MissingResourceException {
  517.         int length = country.length();
  518.  
  519.         if (length == 0) {
  520.             return "";
  521.         }
  522.  
  523.         int index = compressedIsoCountries.indexOf("," + country);
  524.         if (index == -1 || length != 2) {
  525.             throw new MissingResourceException("Couldn't find 3-letter country code for "
  526.                     + country, "LocaleElements_" + toString(), "ShortCountry");
  527.         }
  528.         return compressedIsoCountries.substring(index + 3, index + 6);
  529.     }
  530.  
  531.     /**
  532.      * Returns a name for the locale's language that is appropriate for display to the
  533.      * user.
  534.      * If possible, the name returned will be localized for the default locale.
  535.      * For example, if the locale is fr_FR and the default locale
  536.      * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
  537.      * the default locale is fr_FR, getDisplayLanguage() will return "anglais".
  538.      * If the name returned cannot be localized for the default locale,
  539.      * (say, we don't have a Japanese name for Croatian),
  540.      * this function falls back on the English name, and uses the ISO code as a last-resort
  541.      * value.  If the locale doesn't specify a language, this function returns the empty string.
  542.      */
  543.     public final String getDisplayLanguage() {
  544.         return getDisplayLanguage(getDefault());
  545.     }
  546.  
  547.     /**
  548.      * Returns a name for the locale's language that is appropriate for display to the
  549.      * user.
  550.      * If possible, the name returned will be localized according to inLocale.
  551.      * For example, if the locale is fr_FR and inLocale
  552.      * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
  553.      * inLocale is fr_FR, getDisplayLanguage() will return "anglais".
  554.      * If the name returned cannot be localized according to inLocale,
  555.      * (say, we don't have a Japanese name for Croatian),
  556.      * this function falls back on the default locale, on the English name, and finally
  557.      * on the ISO code as a last-resort value.  If the locale doesn't specify a language,
  558.      * this function returns the empty string.
  559.      */
  560.     public String getDisplayLanguage(Locale inLocale) {
  561.         String  langCode = language;
  562.         if (langCode.length() == 0)
  563.             return "";
  564.  
  565.         Locale  workingLocale = (Locale)inLocale.clone();
  566.         String  result = null;
  567.         int     phase = 0;
  568.         boolean done = false;
  569.  
  570.         if (workingLocale.variant.length() == 0)
  571.             phase = 1;
  572.         if (workingLocale.country.length() == 0)
  573.             phase = 2;
  574.  
  575.         while (!done) {
  576.             try {
  577.                 ResourceBundle bundle = ResourceBundle.getBundle(
  578.                     "java.text.resources.LocaleElements", workingLocale);
  579.                 result = findStringMatch((String[][])bundle.getObject("Languages"),
  580.                                     langCode, langCode);
  581.                 if (result.length() != 0)
  582.                     done = true;
  583.             }
  584.             catch (Exception e) {
  585.                 // just fall through
  586.             }
  587.  
  588.             if (!done) {
  589.                 switch (phase) {
  590.                     case 0:
  591.                         workingLocale.variant = "";
  592.                         break;
  593.  
  594.                     case 1:
  595.                         workingLocale.country = "";
  596.                         break;
  597.  
  598.                     case 2:
  599.                         workingLocale = getDefault();
  600.                         break;
  601.  
  602.                     case 3:
  603.                         workingLocale = new Locale("", "", "");
  604.                         break;
  605.  
  606.                     default:
  607.                         return langCode;
  608.                 }
  609.                 phase++;
  610.             }
  611.         }
  612.         return result;
  613.     }
  614.  
  615.     /**
  616.      * Returns a name for the locale's country that is appropriate for display to the
  617.      * user.
  618.      * If possible, the name returned will be localized for the default locale.
  619.      * For example, if the locale is fr_FR and the default locale
  620.      * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
  621.      * the default locale is fr_FR, getDisplayLanguage() will return "Etats-Unis".
  622.      * If the name returned cannot be localized for the default locale,
  623.      * (say, we don't have a Japanese name for Croatia),
  624.      * this function falls back on the English name, and uses the ISO code as a last-resort
  625.      * value.  If the locale doesn't specify a country, this function returns the empty string.
  626.      */
  627.     public final String getDisplayCountry() {
  628.         return getDisplayCountry(getDefault());
  629.     }
  630.  
  631.     /**
  632.      * Returns a name for the locale's country that is appropriate for display to the
  633.      * user.
  634.      * If possible, the name returned will be localized according to inLocale.
  635.      * For example, if the locale is fr_FR and inLocale
  636.      * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
  637.      * inLocale is fr_FR, getDisplayLanguage() will return "Etats-Unis".
  638.      * If the name returned cannot be localized according to inLocale.
  639.      * (say, we don't have a Japanese name for Croatia),
  640.      * this function falls back on the default locale, on the English name, and finally
  641.      * on the ISO code as a last-resort value.  If the locale doesn't specify a country,
  642.      * this function returns the empty string.
  643.      */
  644.     public String getDisplayCountry(Locale inLocale) {
  645.         String  ctryCode = country;
  646.         if (ctryCode.length() == 0)
  647.             return "";
  648.  
  649.         Locale  workingLocale = (Locale)inLocale.clone();
  650.         String  result = null;
  651.         int     phase = 0;
  652.         boolean done = false;
  653.  
  654.         if (workingLocale.variant.length() == 0)
  655.             phase = 1;
  656.         if (workingLocale.country.length() == 0)
  657.             phase = 2;
  658.  
  659.         while (!done) {
  660.             try {
  661.                 ResourceBundle bundle = ResourceBundle.getBundle(
  662.                     "java.text.resources.LocaleElements", workingLocale);
  663.                 result = findStringMatch((String[][])bundle.getObject("Countries"),
  664.                                     ctryCode, ctryCode);
  665.                 if (result.length() != 0)
  666.                     done = true;
  667.             }
  668.             catch (Exception e) {
  669.                 // just fall through
  670.             }
  671.  
  672.             if (!done) {
  673.                 switch (phase) {
  674.                     case 0:
  675.                         workingLocale.variant = "";
  676.                         break;
  677.  
  678.                     case 1:
  679.                         workingLocale.country = "";
  680.                         break;
  681.  
  682.                     case 2:
  683.                         workingLocale = getDefault();
  684.                         break;
  685.  
  686.                     case 3:
  687.                         workingLocale = new Locale("", "", "");
  688.                         break;
  689.  
  690.                     default:
  691.                         return ctryCode;
  692.                 }
  693.                 phase++;
  694.             }
  695.         }
  696.         return result;
  697.     }
  698.  
  699.     /**
  700.      * Returns a name for the locale's variant code that is appropriate for display to the
  701.      * user.  If possible, the name will be localized for the default locale.  If the locale
  702.      * doesn't specify a variant code, this function returns the empty string.
  703.      */
  704.     public final String getDisplayVariant() {
  705.         return getDisplayVariant(getDefault());
  706.     }
  707.  
  708.     /**
  709.      * Returns a name for the locale's variant code that is appropriate for display to the
  710.      * user.  If possible, the name will be localized for inLocale.  If the locale
  711.      * doesn't specify a variant code, this function returns the empty string.
  712.      */
  713.     public String getDisplayVariant(Locale inLocale) {
  714.         if (variant.length() == 0)
  715.             return "";
  716.  
  717.         ResourceBundle bundle = ResourceBundle.getBundle(
  718.                 "java.text.resources.LocaleElements", inLocale);
  719.  
  720.         String names[] = getDisplayVariantArray(bundle);
  721.  
  722.         // Get the localized patterns for formatting a list, and use
  723.         // them to format the list.
  724.         String[] patterns;
  725.         try {
  726.             patterns = (String[])bundle.getObject("LocaleNamePatterns");
  727.         }
  728.         catch (MissingResourceException e) {
  729.             patterns = null;
  730.         }
  731.         return formatList(patterns, names);
  732.     }
  733.  
  734.     /**
  735.      * Returns a name for the locale that is appropriate for display to the
  736.      * user.  This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
  737.      * and getDisplayVariant() assembled into a single string.  The display name will have
  738.      * one of the following forms:<p><blockquote>
  739.      * language (country, variant)<p>
  740.      * language (country)<p>
  741.      * language (variant)<p>
  742.      * country (variant)<p>
  743.      * language<p>
  744.      * country<p>
  745.      * variant<p></blockquote>
  746.      * depending on which fields are specified in the locale.  If the language, country,
  747.      * and variant fields are all empty, this function returns the empty string.
  748.      */
  749.     public final String getDisplayName() {
  750.         return getDisplayName(getDefault());
  751.     }
  752.  
  753.     /**
  754.      * Returns a name for the locale that is appropriate for display to the
  755.      * user.  This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
  756.      * and getDisplayVariant() assembled into a single string.  The display name will have
  757.      * one of the following forms:<p><blockquote>
  758.      * language (country, variant)<p>
  759.      * language (country)<p>
  760.      * language (variant)<p>
  761.      * country (variant)<p>
  762.      * language<p>
  763.      * country<p>
  764.      * variant<p></blockquote>
  765.      * depending on which fields are specified in the locale.  If the language, country,
  766.      * and variant fields are all empty, this function returns the empty string.
  767.      */
  768.     public String getDisplayName(Locale inLocale) {
  769.         ResourceBundle bundle = ResourceBundle.getBundle(
  770.                 "java.text.resources.LocaleElements", inLocale);
  771.  
  772.         String languageName = getDisplayLanguage(inLocale);
  773.         String countryName = getDisplayCountry(inLocale);
  774.         String[] variantNames = getDisplayVariantArray(bundle);
  775.  
  776.         // Get the localized patterns for formatting a display name.
  777.         String[] patterns;
  778.         try {
  779.             patterns = (String[])bundle.getObject("LocaleNamePatterns");
  780.         }
  781.         catch (MissingResourceException e) {
  782.             patterns = null;
  783.         }
  784.  
  785.         // The display name consists of a main name, followed by qualifiers.
  786.         // Typically, the format is "MainName (Qualifier, Qualifier)" but this
  787.         // depends on what pattern is stored in the display locale.
  788.         String   mainName       = null;
  789.         String[] qualifierNames = null;
  790.  
  791.         // The main name is the language, or if there is no language, the country.
  792.         // If there is neither language nor country (an anomalous situation) then
  793.         // the display name is simply the variant's display name.
  794.         if (languageName.length() != 0) {
  795.             mainName = languageName;
  796.             if (countryName.length() != 0) {
  797.                 qualifierNames = new String[variantNames.length + 1];
  798.                 System.arraycopy(variantNames, 0, qualifierNames, 1, variantNames.length);
  799.                 qualifierNames[0] = countryName;
  800.             }
  801.             else qualifierNames = variantNames;
  802.         }
  803.         else if (countryName.length() != 0) {
  804.             mainName = countryName;
  805.             qualifierNames = variantNames;
  806.         }
  807.         else {
  808.             return formatList(patterns, variantNames);
  809.         }
  810.  
  811.         // Create an array whose first element is the number of remaining
  812.         // elements.  This serves as a selector into a ChoiceFormat pattern from
  813.         // the resource.  The second and third elements are the main name and
  814.         // the qualifier; if there are no qualifiers, the third element is
  815.         // unused by the format pattern.
  816.         Object[] displayNames = {
  817.             new Integer(qualifierNames.length != 0 ? 2 : 1),
  818.             mainName,
  819.             // We could also just call formatList() and have it handle the empty
  820.             // list case, but this is more efficient, and we want it to be
  821.             // efficient since all the language-only locales will not have any
  822.             // qualifiers.
  823.             qualifierNames.length != 0 ? formatList(patterns, qualifierNames) : null
  824.         };
  825.  
  826.         if (patterns != null) {
  827.             return new MessageFormat(patterns[0]).format(displayNames);
  828.         }
  829.         else {
  830.             // If we cannot get the message format pattern, then we use a simple
  831.             // hard-coded pattern.  This should not occur in practice unless the
  832.             // installation is missing some core files (LocaleElements etc.).
  833.             StringBuffer result = new StringBuffer();
  834.             result.append((String)displayNames[1]);
  835.             if (displayNames.length > 2) {
  836.                 result.append(" (");
  837.                 result.append((String)displayNames[2]);
  838.                 result.append(")");
  839.             }
  840.             return result.toString();
  841.         }
  842.     }
  843.  
  844.     /**
  845.      * Overrides Cloneable
  846.      */
  847.     public Object clone()
  848.     {
  849.         try {
  850.             Locale that = (Locale)super.clone();
  851.             return that;
  852.         } catch (CloneNotSupportedException e) {
  853.             throw new InternalError();
  854.         }
  855.     }
  856.  
  857.     /**
  858.      * Override hashCode.
  859.      * Since Locales are often used in hashtables, caches the value
  860.      * for speed.
  861.      */
  862.       // XXX Depending on performance of synchronized, may want to
  863.       // XXX just compute in constructor.
  864.     public synchronized int hashCode() {
  865.         if (hashcode == -1) {
  866.             hashcode =
  867.         language.hashCode() ^
  868.         country.hashCode() ^
  869.         variant.hashCode();
  870.         }
  871.         return hashcode;
  872.     }
  873.  
  874.     // Overrides
  875.  
  876.     /**
  877.      * Returns true if this Locale is equal to another object.  A Locale is
  878.      * deemed equal to another Locale with identical language, country,
  879.      * and variant, and unequal to all other objects.
  880.      *
  881.      * @return true if this Locale is equal to the specified object.
  882.      */
  883.  
  884.     public boolean equals(Object obj) {
  885.         if (this == obj)                      // quick check
  886.             return true;
  887.         if (!(obj instanceof Locale))         // (1) same object?
  888.             return false;
  889.         Locale other = (Locale) obj;
  890.         if (hashCode() != other.hashCode()) return false;       // quick check
  891.         if (language != other.language) return false;
  892.         if (country != other.country) return false;
  893.         if (variant != other.variant) return false;
  894.         return true; // we made it through the guantlet.
  895.         // (1)  We don't check super.equals since it is Object.
  896.         //      Since Locale is final, we don't have to check both directions.
  897.     }
  898.  
  899.     // ================= privates =====================================
  900.  
  901.     // XXX instance and class variables. For now keep these separate, since it is
  902.     // faster to match. Later, make into single string.
  903.  
  904.     /**
  905.      * @serial
  906.      * @see #getLanguage
  907.      */
  908.     private String language = "";
  909.  
  910.     /**
  911.      * @serial
  912.      * @see #getCountry
  913.      */
  914.     private String country = "";
  915.  
  916.     /**
  917.      * @serial
  918.      * @see #getVariant
  919.      */
  920.     private String variant = "";
  921.  
  922.     /**
  923.      * Placeholder for the object's hash code.  Always -1.
  924.      * @serial
  925.      */
  926.     private int hashcode = -1;        // lazy evaluated
  927.  
  928.     private static Locale defaultLocale;
  929.  
  930.     static {
  931.         String language =
  932.             (String) AccessController.doPrivileged(
  933.                         new GetPropertyAction("user.language","EN"));
  934.         String country =
  935.             (String) AccessController.doPrivileged(
  936.                         new GetPropertyAction("user.region",""));
  937.  
  938.         /* The user.region property may be of the form country, country_variant,
  939.          * or _variant.  Since the Locale constructor takes the country value as
  940.          * an unparsed literal, and we don't want to change that behavior, we
  941.          * must premunge it here into country and variant. - liu 7/24/98 */
  942.         String variant = "";
  943.         int i = country.indexOf('_');
  944.         if (i >= 0) {
  945.             variant = country.substring(i+1);
  946.             country = country.substring(0, i);
  947.         }
  948.         defaultLocale = new Locale(language, country, variant);
  949.     }
  950.  
  951.     /**
  952.      * Return an array of the display names of the variant.
  953.      * @param bundle the ResourceBundle to use to get the display names
  954.      * @return an array of display names, possible of zero length.
  955.      */
  956.     private String[] getDisplayVariantArray(ResourceBundle bundle) {
  957.         // Split the variant name into tokens separated by '_'.
  958.         StringTokenizer tokenizer = new StringTokenizer(variant, "_");
  959.         String[] names = new String[tokenizer.countTokens()];
  960.  
  961.         // For each variant token, lookup the display name.  If
  962.         // not found, use the variant name itself.
  963.         for (int i=0; i<names.length; ++i) {
  964.             String token = tokenizer.nextToken();
  965.             try {
  966.                 names[i] = (String)bundle.getObject("%%" + token);
  967.             }
  968.             catch (MissingResourceException e) {
  969.                 names[i] = token;
  970.             }
  971.         }
  972.  
  973.         return names;
  974.     }
  975.  
  976.     /**
  977.      * Format a list with an array of patterns.
  978.      * @param patterns an array of three patterns. The first pattern is not
  979.      * used. The second pattern should create a MessageFormat taking 0-3 arguments
  980.      * and formatting them into a list. The third pattern should take 2 arguments
  981.      * and is used by composeList. If patterns is null, then a the list is
  982.      * formatted by concatenation with the delimiter ','.
  983.      * @param stringList the list of strings to be formatted.
  984.      * @return a string representing the list.
  985.      */
  986.     private static String formatList(String[] patterns, String[] stringList) {
  987.         // If we have no list patterns, compose the list in a simple,
  988.         // non-localized way.
  989.         if (patterns == null) {
  990.             StringBuffer result = new StringBuffer();
  991.             for (int i=0; i<stringList.length; ++i) {
  992.                 if (i>0) result.append(',');
  993.                 result.append(stringList[i]);
  994.             }
  995.             return result.toString();
  996.         }
  997.  
  998.         // Compose the list down to three elements if necessary
  999.         if (stringList.length > 3) {
  1000.             MessageFormat format = new MessageFormat(patterns[2]);
  1001.             stringList = composeList(format, stringList);
  1002.         }
  1003.  
  1004.         // Rebuild the argument list with the list length as the first element
  1005.         Object[] args = new Object[stringList.length + 1];
  1006.         System.arraycopy(stringList, 0, args, 1, stringList.length);
  1007.         args[0] = new Integer(stringList.length);
  1008.  
  1009.         // Format it using the pattern in the resource
  1010.         MessageFormat format = new MessageFormat(patterns[1]);
  1011.         return format.format(args);
  1012.     }
  1013.  
  1014.     /**
  1015.      * Given a list of strings, return a list shortened to three elements.
  1016.      * Shorten it by applying the given format to the first two elements
  1017.      * recursively.
  1018.      * @param format a format which takes two arguments
  1019.      * @param list a list of strings
  1020.      * @return if the list is three elements or shorter, the same list;
  1021.      * otherwise, a new list of three elements.
  1022.      */
  1023.     private static String[] composeList(MessageFormat format, String[] list) {
  1024.         if (list.length <= 3) return list;
  1025.  
  1026.         // Use the given format to compose the first two elements into one
  1027.         String[] listItems = { list[0], list[1] };
  1028.         String newItem = format.format(listItems);
  1029.  
  1030.         // Form a new list one element shorter
  1031.         String[] newList = new String[list.length-1];
  1032.         System.arraycopy(list, 2, newList, 1, newList.length-1);
  1033.         newList[0] = newItem;
  1034.  
  1035.         // Recurse
  1036.         return composeList(format, newList);
  1037.     }
  1038.  
  1039.     /**
  1040.      * @serialData The first three fields are three <code>String</code> objects:
  1041.      * the first is a 2-letter ISO 639 code representing the locale's language,
  1042.      * the second is a 2-letter ISO 3166 code representing the locale's region or
  1043.      * country, and the third is an optional chain of variant codes defined by this
  1044.      * library.  Any of the fields may be the empty string.  The fourth field is an
  1045.      * <code>int</code> whose value is always -1.  This is a sentinel value indicating
  1046.      * the <code>Locale</code>'s hash code must be recomputed.
  1047.      */
  1048.     private void writeObject(ObjectOutputStream out) throws IOException {
  1049.         // hashcode is semantically transient.  We couldn't define it as transient
  1050.         // because versions of this class that DIDN'T declare it as transient have
  1051.         // already shipped.  What we're doing here is making sure that the written-out
  1052.         // version of hashcode is always -1, regardless of what's really stored there
  1053.         // (we hold onto the original value just in case someone might want it again).
  1054.         // Writing -1 ensures that version 1.1 Locales will always recalculate their
  1055.         // hash codes after being streamed back in.  This is necessary because
  1056.         // String.hashCode() calculates its hash code differently in 1.2 than it did
  1057.         // in 1.1.
  1058.         int temp = hashcode;
  1059.         hashcode = -1;
  1060.         out.defaultWriteObject();
  1061.         hashcode = temp;
  1062.     }
  1063.  
  1064.     /**
  1065.      * @serialData The first three fields are three <code>String</code> objects:
  1066.      * the first is a 2-letter ISO 639 code representing the locale's language,
  1067.      * the second is a 2-letter ISO 3166 code representing the locale's region or
  1068.      * country, and the third is an optional chain of variant codes defined by this
  1069.      * library.  Any of the fields may be the empty string.  The fourth field is an
  1070.      * <code>int</code>representing the locale's hash code, but is ignored by
  1071.      * <code>readObject()</code>.  Whatever this field's value, the hash code is
  1072.      * initialized to -1, a sentinel value that indicates the hash code must be
  1073.      * recomputed.
  1074.      */
  1075.     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
  1076.         // hashcode is semantically transient.  We couldn't define it as transient
  1077.         // because versions of this class that DIDN'T deslcare is as transient have
  1078.         // already shipped.  This code makes sure that whatever value for hashcode
  1079.         // was written on the stream, we ignore it and recalculate it on demand.  This
  1080.         // is necessary because String.hashCode() calculates is hash code differently
  1081.         // in version 1.2 than it did in 1.1.
  1082.         in.defaultReadObject();
  1083.         hashcode = -1;
  1084.         language = language.intern();
  1085.         country = country.intern();
  1086.         variant = variant.intern();
  1087.     }
  1088.  
  1089.     /**
  1090.      * List of all 2-letter language codes currently defined in ISO 639.
  1091.      * (Because the Java VM specification turns an array constant into executable code
  1092.      * that generates the array element by element, we keep the array in compressed
  1093.      * form in a single string and build the array from it at run time when requested.)
  1094.      * [We're now also using this table to store a mapping from 2-letter ISO language codes
  1095.      * to 3-letter ISO language codes.  Each group of characters consists of a comma, a
  1096.      * 2-letter code, and a 3-letter code.  We look up a 3-letter code by searching for
  1097.      * a comma followed by a 2-letter code and then getting the three letters following
  1098.      * the 2-letter code.]
  1099.      */
  1100.     private static String[] isoLanguages = null;
  1101.     private static final String compressedIsoLanguages =
  1102.         ",aaaar,ababk,afafr,amamh,arara,asasm,ayaym,azaze,babak,bebel,bgbul,bhbih,bibis,bnben,"
  1103.         + "bobod,brbre,cacat,cocos,csces,cycym,dadan,dedeu,dzdzo,elell,eneng,eoepo,esspa,"
  1104.         + "etest,eueus,fafas,fifin,fjfij,fofao,frfra,fyfry,gagai,gdgdh,glglg,gngrn,guguj,"
  1105.         + "hahau,heheb,hihin,hrhrv,huhun,hyhye,iaina,idind,ieile,ikipk,inind,isisl,itita,"
  1106.         + "iuiku,iwheb,jajpn,jiyid,jwjaw,kakat,kkkaz,klkal,kmkhm,knkan,kokor,kskas,kukur,"
  1107.         + "kykir,lalat,lnlin,lolao,ltlit,lvlav,mgmlg,mimri,mkmkd,mlmal,mnmon,momol,mrmar,"
  1108.         + "msmsa,mtmlt,mymya,nanau,nenep,nlnld,nonor,ococi,omorm,orori,papan,plpol,pspus,"
  1109.         + "ptpor,quque,rmroh,rnrun,roron,rurus,rwkin,sasan,sdsnd,sgsag,shsrp,sisin,skslk,"
  1110.         + "slslv,smsmo,snsna,sosom,sqsqi,srsrp,ssssw,stsot,susun,svswe,swswa,tatat,tetel,"
  1111.         + "tgtgk,ththa,titir,tktuk,tltgl,tntsn,toton,trtur,tstsn,tttat,twtwi,uguig,ukukr,"
  1112.         + "ururd,uzuzb,vivie,vovol,wowol,xhxho,yiyid,yoyor,zazha,zhzho,zuzul";
  1113.  
  1114.     /**
  1115.      * List of all 2-letter country codes currently defined in ISO 3166.
  1116.      * (Because the Java VM specification turns an array constant into executable code
  1117.      * that generates the array element by element, we keep the array in compressed
  1118.      * form in a single string and build the array from it at run time when requested.)
  1119.      * [We're now also using this table to store a mapping from 2-letter ISO country codes
  1120.      * to 3-letter ISO country codes.  Each group of characters consists of a comma, a
  1121.      * 2-letter code, and a 3-letter code.  We look up a 3-letter code by searching for
  1122.      * a comma followed by a 2-letter code and then getting the three letters following
  1123.      * the 2-letter code.]
  1124.      */
  1125.     private static String[] isoCountries = null;
  1126.     private static final String compressedIsoCountries =
  1127.         ",ADAND,AEARE,AFAFG,AGATG,AIAIA,ALALB,AMARM,ANANT,AOAGO,AQATA,ARARG,ASASM,ATAUT,"
  1128.         + "AUAUS,AWABW,AZAZE,BABIH,BBBRB,BDBGD,BEBEL,BFBFA,BGBGR,BHBHR,BIBDI,BJBEN,BMBMU,"
  1129.         + "BNBRN,BOBOL,BRBRA,BSBHS,BTBTN,BVBVT,BWBWA,BYBLR,BZBLZ,CACAN,CCCCK,CFCAF,CGCOG,"
  1130.         + "CHCHE,CICIV,CKCOK,CLCHL,CMCMR,CNCHN,COCOL,CRCRI,CUCUB,CVCPV,CXCXR,CYCYP,CZCZE,"
  1131.         + "DEDEU,DJDJI,DKDNK,DMDMA,DODOM,DZDZA,ECECU,EEEST,EGEGY,EHESH,ERERI,ESESP,ETETH,"
  1132.         + "FIFIN,FJFJI,FKFLK,FMFSM,FOFRO,FRFRA,FXFXX,GAGAB,GBGBR,GDGRD,GEGEO,GFGUF,GHGHA,"
  1133.         + "GIGIB,GLGRL,GMGMB,GNGIN,GPGLP,GQGNQ,GRGRC,GSSGS,GTGTM,GUGUM,GWGNB,GYGUY,HKHKG,"
  1134.         + "HMHMD,HNHND,HRHRV,HTHTI,HUHUN,IDIDN,IEIRL,ILISR,ININD,IOIOT,IQIRQ,IRIRN,ISISL,"
  1135.         + "ITITA,JMJAM,JOJOR,JPJPN,KEKEN,KGKGZ,KHKHM,KIKIR,KMCOM,KNKNA,KPPRK,KRKOR,KWKWT,"
  1136.         + "KYCYM,KZKAZ,LALAO,LBLBN,LCLCA,LILIE,LKLKA,LRLBR,LSLSO,LTLTU,LULUX,LVLVA,LYLBY,"
  1137.         + "MAMAR,MCMCO,MDMDA,MGMDG,MHMHL,MKMKD,MLMLI,MMMMR,MNMNG,MOMAC,MPMNP,MQMTQ,MRMRT,"
  1138.         + "MSMSR,MTMLT,MUMUS,MVMDV,MWMWI,MXMEX,MYMYS,MZMOZ,NANAM,NCNCL,NENER,NFNFK,NGNGA,"
  1139.         + "NINIC,NLNLD,NONOR,NPNPL,NRNRU,NUNIU,NZNZL,OMOMN,PAPAN,PEPER,PFPYF,PGPNG,PHPHL,"
  1140.         + "PKPAK,PLPOL,PMSPM,PNPCN,PRPRI,PTPRT,PWPLW,PYPRY,QAQAT,REREU,ROROM,RURUS,RWRWA,"
  1141.         + "SASAU,SBSLB,SCSYC,SDSDN,SESWE,SGSGP,SHSHN,SISVN,SJSJM,SKSVK,SLSLE,SMSMR,SNSEN,"
  1142.         + "SOSOM,SRSUR,STSTP,SVSLV,SYSYR,SZSWZ,TCTCA,TDTCD,TFATF,TGTGO,THTHA,TJTJK,TKTKL,"
  1143.         + "TMTKM,TNTUN,TOTON,TPTMP,TRTUR,TTTTO,TVTUV,TWTWN,TZTZA,UAUKR,UGUGA,UMUMI,USUSA,"
  1144.         + "UYURY,UZUZB,VAVAT,VCVCT,VEVEN,VGVGB,VIVIR,VNVNM,VUVUT,WFWLF,WSWSM,YEYEM,YTMYT,"
  1145.         + "YUYUG,ZAZAF,ZMZMB,ZRZAR,ZWZWE";
  1146.  
  1147.     /**
  1148.      * Table mapping ISO country codes to the ISO language codes of the languages spoken
  1149.      * in those countries.
  1150.      * (Because the Java VM specification for building arrays and hashtables causes
  1151.      * code that builds the tables element by element to be produces, we compress the data
  1152.      * into a single encoded String, and lazy evaluate the table from it.)
  1153.      */
  1154.     private static Hashtable ctry2LangMapping = null;
  1155.     private static final String compressedCtry2LangMapping =
  1156.         "ADfresAEarenAFpsAGenAIrnALsqAMhyruANnlenAOptAResASensmATdeAUenAWnlenAZazhyru"
  1157.         + "BAsrshhrslmksqBBenBDbnhibhenBEfrnldeBFfrBGbgtrBHarenBIrnfrswBJfrBMenBNmsenzh"
  1158.         + "BOesayquBRptBSenBTdzenneBVnoBWentnBYberuBZenesCAenfrCCenCFfrsgCGfrCHfrdeitrm"
  1159.         + "CIfrCKmienCLesCMenfrCNzhboCOesCResCUesCVptCXenCYeltrenCZcsskDEdeDJarfrsoDKda"
  1160.         + "DMenfrDOesDZarfrECesquEEetruEGarenfrEHarfritERamtiarenitESeseucaglETamaren"
  1161.         + "FIfisvFJenfjhiFKenFMenFOfodaFRfreubrcoFXfrGAfrGBengdcyGDenfrGEkahyruGFfrGHen"
  1162.         + "GIenesGLdaikklGMenwoGNfrGPfrenGQesGRelGTesGUenGWptGYenhiurHKzhenHNesHRhrHTfr"
  1163.         + "HUhuIDinennlIEengaILiwarjiINhienguknksmlmrneorpasatateIOenIQarkutkIRfaarku"
  1164.         + "ISisITitfrdeJMenJOarJPjaKEenswKGkyKHkmKIenKMfrarKNenKPkoKRkoKWarenKYenKZkkru"
  1165.         + "LAlofrLBarenfrLCenfrLIdeLKtasienLRenLSstenLTltruplLUfrdeLVlvltruLYarenit"
  1166.         + "MAarfresMCfrenitMDmorobgMGmgenfrMKmkshtrMLfrMMmyMNmnruMOzhptMQfrMRarfrMSen"
  1167.         + "MTmtenitMUenfrhiMWenMXesMYmsenMZptNAenafdeNEfrhaNFenNGenhayoNIesNLnlfyNOno"
  1168.         + "NPneNRnaenNUenNZenmiOMarenPAesenPEesquayPFfrPGenPHentlesPKurenpspasdPLplPMfren"
  1169.         + "PNenPResenPTptPWenPYesgnQAarenREfrtaROrohuRUruRWenfrrwSAarSBenSCenfrSDarsu"
  1170.         + "SEsvSGzhenmstaSHenSIslSJnoSKskhuplshSLenSMitSNfrSOarenitsoSRnleneshiSTptSVes"
  1171.         + "SYarSZenssTCenTDfrarTFfrTGfrTHthTJtgruuzTKenmiTMtkruTNarTOentoTRtrkuTTenTVen"
  1172.         + "TWzhTZenswUAukruUGenswUMenUSenesUYesUZuzruVAlaitVCenVEesVGenVIenVNvizhfr"
  1173.         + "VUenfrbiWFfrWSensmYEarYTfrmgswYUsrshmkhuZAafenZMenZRfrswZWensn";
  1174.  
  1175.     /*
  1176.      * Locale needs its own, locale insenitive version of toLowerCase to
  1177.      * avoid circularity problems between Locale and String.
  1178.      * The most straightforward algorithm is used. Look at optimizations later.
  1179.      */
  1180.     private String toLowerCase(String str) {
  1181.         char[] buf = str.toCharArray();
  1182.         for (int i = 0; i < buf.length; i++) {
  1183.             buf[i] = Character.toLowerCase( buf[i] );
  1184.         }
  1185.         return new String( buf );
  1186.     }
  1187.  
  1188.     /*
  1189.      * Locale needs its own, locale insensitive version of toUpperCase to
  1190.      * avoid circularity problems between Locale and String.
  1191.      * The most straightforward algorithm is used. Look at optimizations later.
  1192.      */
  1193.     private String toUpperCase(String str) {
  1194.         char[] buf = str.toCharArray();
  1195.         for (int i = 0; i < buf.length; i++) {
  1196.             buf[i] = Character.toUpperCase( buf[i] );
  1197.         }
  1198.         return new String( buf );
  1199.     }
  1200.  
  1201.     private String findStringMatch(String[][] languages,
  1202.                                    String desiredLanguage, String fallbackLanguage)
  1203.     {
  1204.         for (int i = 0; i < languages.length; ++i)
  1205.             if (desiredLanguage.equals(languages[i][0]))
  1206.                 return languages[i][1];
  1207.         if (!fallbackLanguage.equals(desiredLanguage))
  1208.             for (int i = 0; i < languages.length; ++i)
  1209.                 if (fallbackLanguage.equals(languages[i][0]))
  1210.                     return languages[i][1];
  1211.         if (!"EN".equals(desiredLanguage) && "EN".equals(fallbackLanguage))
  1212.             for (int i = 0; i < languages.length; ++i)
  1213.                 if ("EN".equals(languages[i][0]))
  1214.                     return languages[i][1];
  1215.         return "";
  1216.     }
  1217. }
  1218.