home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / VCAFE.3.0A / Main.bin / Locale.java < prev    next >
Text File  |  1998-09-22  |  39KB  |  967 lines

  1. /*
  2.  * @(#)Locale.java  1.21 97/01/29
  3.  *
  4.  * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
  5.  * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
  6.  *
  7.  * Portions copyright (c) 1996 Sun Microsystems, Inc. All Rights Reserved.
  8.  *
  9.  *   The original version of this source code and documentation is copyrighted
  10.  * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
  11.  * materials are provided under terms of a License Agreement between Taligent
  12.  * and Sun. This technology is protected by multiple US and International
  13.  * patents. This notice and attribution to Taligent may not be removed.
  14.  *   Taligent is a registered trademark of Taligent, Inc.
  15.  *
  16.  * Permission to use, copy, modify, and distribute this software
  17.  * and its documentation for NON-COMMERCIAL purposes and without
  18.  * fee is hereby granted provided that this copyright notice
  19.  * appears in all copies. Please refer to the file "copyright.html"
  20.  * for further important copyright and licensing information.
  21.  *
  22.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  23.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  24.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  25.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  26.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  27.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  28.  *
  29.  */
  30.  
  31. package java.util;
  32. import java.io.Serializable;
  33. import java.text.MessageFormat;
  34.  
  35. /**
  36.  *
  37.  * A <code>Locale</code> object represents a specific geographical, political,
  38.  * or cultural region. An operation that requires a <code>Locale</code> to perform
  39.  * its task is called <em>locale-sensitive</em> and uses the <code>Locale</code>
  40.  * to tailor information for the user. For example, displaying a number
  41.  * is a locale-sensitive operation--the number should be formatted
  42.  * according to the customs/conventions of the user's native country,
  43.  * region, or culture.
  44.  *
  45.  * <P>
  46.  * You create a <code>Locale</code> object using one of the two constructors in
  47.  * this class:
  48.  * <blockquote>
  49.  * <pre>
  50.  * Locale(String language, String country)
  51.  * Locale(String language, String country, String variant)
  52.  * </pre>
  53.  * </blockquote>
  54.  * The first argument to both constructors is a valid <STRONG>ISO
  55.  * Language Code.</STRONG> These codes are the lower-case two-letter
  56.  * codes as defined by ISO-639.
  57.  * You can find a full list of these codes at a number of sites, such as:
  58.  * <BR><a href ="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt">
  59.  * <code>http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt</code></a>
  60.  *
  61.  * <P>
  62.  * The second argument to both constructors is a valid <STRONG>ISO Country
  63.  * Code.</STRONG> These codes are the upper-case two-letter codes
  64.  * as defined by ISO-3166.
  65.  * You can find a full list of these codes at a number of sites, such as:
  66.  * <BR><a href="http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html">
  67.  * <code>http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html</code></a>
  68.  *
  69.  * <P>
  70.  * The second constructor requires a third argument--the <STRONG>Variant.</STRONG>
  71.  * The Variant codes are vendor and browser-specific.
  72.  * For example, use WIN for Windows, MAC for Macintosh, and POSIX for POSIX.
  73.  * Where there are two variants, separate them with an underscore, and
  74.  * put the most important one first. For
  75.  * example, a Traditional Spanish collation might be referenced, with
  76.  * "ES", "ES", "Traditional_WIN".
  77.  *
  78.  * <P>
  79.  * Because a <code>Locale</code> object is just an identifier for a region,
  80.  * no validity check is performed when you construct a <code>Locale</code>.
  81.  * If you want to see whether particular resources are available for the
  82.  * <code>Locale</code> you construct, you must query those resources. For
  83.  * example, ask the <code>NumberFormat</code> for the locales it supports
  84.  * using its <code>getAvailableLocales</code> method.
  85.  * <BR><STRONG>Note:</STRONG> When you ask for a resource for a particular
  86.  * locale, you get back the best available match, not necessarily
  87.  * precisely what you asked for. For more information, look at
  88.  * <a href="java.util.ResourceBundle.html"><code>ResourceBundle</code></a>.
  89.  *
  90.  * <P>
  91.  * The <code>Locale</code> class provides a number of convenient constants
  92.  * that you can use to create <code>Locale</code> objects for commonly used
  93.  * locales. For example, the following creates a <code>Locale</code> object
  94.  * for the United States:
  95.  * <blockquote>
  96.  * <pre>
  97.  * Locale.US
  98.  * </pre>
  99.  * </blockquote>
  100.  *
  101.  * <P>
  102.  * Once you've created a <code>Locale</code> you can query it for information about
  103.  * itself. Use <code>getCountry</code> to get the ISO Country Code and
  104.  * <code>getLanguage</code> to get the ISO Language Code. You can
  105.  * use <code>getDisplayCountry</code> to get the
  106.  * name of the country suitable for displaying to the user. Similarly,
  107.  * you can use <code>getDisplayLanguage</code> to get the name of
  108.  * the language suitable for displaying to the user. Interestingly,
  109.  * the <code>getDisplayXXX</code> methods are themselves locale-sensitive
  110.  * and have two versions: one that uses the default locale and one
  111.  * that uses the locale specified as an argument.
  112.  *
  113.  * <P>
  114.  * The JDK provides a number of classes that perform locale-sensitive
  115.  * operations. For example, the <code>NumberFormat</code> class formats
  116.  * numbers, currency, or percentages in a locale-sensitive manner. Classes
  117.  * such as <code>NumberFormat</code> have a number of convenience methods
  118.  * for creating a default object of that type. For example, the
  119.  * <code>NumberFormat</code> class provides these three convenience methods
  120.  * for creating a default <code>NumberFormat</code> object:
  121.  * <blockquote>
  122.  * <pre>
  123.  * NumberFormat.getInstance()
  124.  * NumberFormat.getCurrencyInstance()
  125.  * NumberFormat.getPercentInstance()
  126.  * </pre>
  127.  * </blockquote>
  128.  * These methods have two variants; one with an explicit locale
  129.  * and one without; the latter using the default locale.
  130.  * <blockquote>
  131.  * <pre>
  132.  * NumberFormat.getInstance(myLocale)
  133.  * NumberFormat.getCurrencyInstance(myLocale)
  134.  * NumberFormat.getPercentInstance(myLocale)
  135.  * </pre>
  136.  * </blockquote>
  137.  * A <code>Locale</code> is the mechanism for identifying the kind of object
  138.  * (<code>NumberFormat</code>) that you would like to get. The locale is
  139.  * <STRONG>just</STRONG> a mechanism for identifying objects,
  140.  * <STRONG>not</STRONG> a container for the objects themselves.
  141.  *
  142.  * <P>
  143.  * Each class that performs locale-sensitive operations allows you
  144.  * to get all the available objects of that type. You can sift
  145.  * through these objects by language, country, or variant,
  146.  * and use the display names to present a menu to the user.
  147.  * For example, you can create a menu of all the collation objects
  148.  * suitable for a given language. Such classes must implement these
  149.  * three class methods:
  150.  * <blockquote>
  151.  * <pre>
  152.  * public static Locale[] getAvailableLocales()
  153.  * public static String getDisplayName(Locale objectLocale,
  154.  *                                     Locale displayLocale)
  155.  * public static final String getDisplayName(Locale objectLocale)
  156.  *     // getDisplayName will throw MissingResourceException if the locale
  157.  *     // is not one of the available locales.
  158.  * </pre>
  159.  * </blockquote>
  160.  *
  161.  * @see         ResourceBundle
  162.  * @see         java.text.Format
  163.  * @see         java.text.NumberFormat
  164.  * @see         java.text.Collation
  165.  * @version     1.21 29 Jan 1997
  166.  * @author      Mark Davis
  167.  */
  168.  
  169. public final class Locale implements Cloneable, Serializable {
  170.  
  171.     /** Useful constant for language.
  172.      */
  173.     static public final Locale ENGLISH = new Locale("en","","");
  174.  
  175.     /** Useful constant for language.
  176.      */
  177.     static public final Locale FRENCH = new Locale("fr","","");
  178.  
  179.     /** Useful constant for language.
  180.      */
  181.     static public final Locale GERMAN = new Locale("de","","");
  182.  
  183.     /** Useful constant for language.
  184.      */
  185.     static public final Locale ITALIAN = new Locale("it","","");
  186.  
  187.     /** Useful constant for language.
  188.      */
  189.     static public final Locale JAPANESE = new Locale("ja","","");
  190.  
  191.     /** Useful constant for language.
  192.      */
  193.     static public final Locale KOREAN = new Locale("ko","","");
  194.  
  195.     /** Useful constant for language.
  196.      */
  197.     static public final Locale CHINESE = new Locale("zh","","");
  198.  
  199.     /** Useful constant for language.
  200.      */
  201.     static public final Locale SIMPLIFIED_CHINESE = new Locale("zh","CN","");
  202.  
  203.     /** Useful constant for language.
  204.      */
  205.     static public final Locale TRADITIONAL_CHINESE = new Locale("zh","TW","");
  206.  
  207.     /** Useful constant for country.
  208.      */
  209.     static public final Locale FRANCE = new Locale("fr","FR","");
  210.  
  211.     /** Useful constant for country.
  212.      */
  213.     static public final Locale GERMANY = new Locale("de","DE","");
  214.  
  215.     /** Useful constant for country.
  216.      */
  217.     static public final Locale ITALY = new Locale("it","IT","");
  218.  
  219.     /** Useful constant for country.
  220.      */
  221.     static public final Locale JAPAN = new Locale("ja","JP","");
  222.  
  223.     /** Useful constant for country.
  224.      */
  225.     static public final Locale KOREA = new Locale("ko","KR","");
  226.  
  227.     /** Useful constant for country.
  228.      */
  229.     static public final Locale CHINA = new Locale("zh","CN","");
  230.  
  231.     /** Useful constant for country.
  232.      */
  233.     static public final Locale PRC = new Locale("zh","CN","");
  234.  
  235.     /** Useful constant for country.
  236.      */
  237.     static public final Locale TAIWAN = new Locale("zh","TW","");
  238.  
  239.     /** Useful constant for country.
  240.      */
  241.     static public final Locale UK = new Locale("en","GB","");
  242.  
  243.     /** Useful constant for country.
  244.      */
  245.     static public final Locale US = new Locale("en","US","");
  246.  
  247.     /** Useful constant for country.
  248.      */
  249.     static public final Locale CANADA = new Locale("en","CA","");
  250.  
  251.     /** Useful constant for country.
  252.      */
  253.     static public final Locale CANADA_FRENCH = new Locale("fr","CA","");
  254.  
  255.     /** serialization ID
  256.      */
  257.     static final long serialVersionUID = 9149081749638150636L;
  258.  
  259.     /**
  260.      * Construct a locale from language, country, variant.
  261.      * NOTE:  ISO 639 is not a stable standard; some of the language codes it defines
  262.      * (specifically iw, ji, and in) have changed.  This constructor accepts both the
  263.      * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
  264.      * API on Locale will return only the OLD codes.
  265.      * @param language lowercase two-letter ISO-639 code.
  266.      * @param country uppercase two-letter ISO-3166 code.
  267.      * @param variant vendor and browser specific code. See class description.
  268.      */
  269.     public Locale(String language, String country, String variant) {
  270.         // we accept both the old and the new ISO codes for the languages whose ISO
  271.         // codes have changed, but we always store the OLD code, for backward compatibility
  272.         language = toLowerCase(language).intern();
  273.         if (language == "he")
  274.             language = "iw";
  275.         else if (language == "yi")
  276.             language = "ji";
  277.         else if (language == "id")
  278.             language = "in";
  279.  
  280.         this.language = language;
  281.         this.country = toUpperCase(country).intern();
  282.         this.variant = toUpperCase(variant).intern();
  283.     }
  284.  
  285.     /**
  286.      * Construct a locale from language, country.
  287.      * NOTE:  ISO 639 is not a stable standard; some of the language codes it defines
  288.      * (specifically iw, ji, and in) have changed.  This constructor accepts both the
  289.      * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
  290.      * API on Locale will return only the OLD codes.
  291.      * @param language lowercase two-letter ISO-639 code.
  292.      * @param country uppercase two-letter ISO-3166 code.
  293.      */
  294.     public Locale(String language, String country) {
  295.         this(language, country, "");
  296.     }
  297.  
  298.     /**
  299.      * Common method of getting the current default Locale.
  300.      * Used for the presentation: menus, dialogs, etc.
  301.      * Generally set once when your applet or application is initialized,
  302.      * then never reset. (If you do reset the default locale, you
  303.      * probably want to reload your GUI, so that the change is reflected
  304.      * in your interface.)
  305.      * <p>More advanced programs will allow users to use different locales
  306.      * for different fields, e.g. in a spreadsheet.
  307.      * <BR>Note that the initial setting will match the host system.
  308.      */
  309.     public static Locale getDefault() {
  310.         return defaultLocale;   // this variable is now initialized at static init time
  311.     }
  312.  
  313.     /**
  314.      * Sets the default.
  315.      * Normally set once at the beginning of applet or application,
  316.      * then never reset. <code>setDefault</code> does not reset the host locale.
  317.      * @param newLocale Locale to set to.
  318.      */
  319.     public static synchronized void setDefault(Locale newLocale) {
  320.     SecurityManager security = System.getSecurityManager();
  321.     if (security != null) {
  322.         security.checkPropertyAccess("user.language");
  323.     }
  324.         defaultLocale = newLocale;
  325.     }
  326.  
  327.     /**
  328.      * Getter for programmatic name of field,
  329.      * an lowercased two-letter ISO-639 code.
  330.      * @see #getDisplayLanguage
  331.      */
  332.     public String getLanguage() {
  333.         return language;
  334.     }
  335.  
  336.     /**
  337.      * Getter for programmatic name of field,
  338.      * an uppercased two-letter ISO-3166 code.
  339.      * @see #getDisplayCountry
  340.      */
  341.     public String getCountry() {
  342.         return country;
  343.     }
  344.  
  345.     /**
  346.      * Getter for programmatic name of field.
  347.      * @see #getDisplayVariant
  348.      */
  349.     public String getVariant() {
  350.         return variant;
  351.     }
  352.  
  353.     /**
  354.      * Getter for the programmatic name of the entire locale,
  355.      * with the language, country and variant separated by underbars.
  356.      * Language is always lower case, and country is always uppcer case.
  357.      * If a field is missing, at most one underbar will occur.
  358.      * Example: "Een, "de_DE", "en_US_WIN", "de_POSIX", "fr_MAC"
  359.      * @see #getDisplayName
  360.      */
  361.     public final String toString() {
  362.         StringBuffer result = new StringBuffer(language);
  363.         if (country.length() != 0) {
  364.             result.append('_');
  365.             result.append(country);
  366.             if (variant.length() != 0) {
  367.                 result.append('_');
  368.                 result.append(variant);
  369.             }
  370.         }
  371.         return result.toString();
  372.     }
  373.  
  374.     /**
  375.      * Getter for the three-letter ISO language abbreviation
  376.      * of the locale.  Returns the empty string if the locale doesn't specify a language.
  377.      * @exception MissingResourceException Throws MissingResourceException if the
  378.      * three-letter language abbreviation is not available for this locale.
  379.      */
  380.     public String getISO3Language() throws MissingResourceException {
  381.         if (language.length() == 0)
  382.             return "";
  383.  
  384.         // the call to getISO2Language() will throw a MissingResourceException if
  385.         // the appropriate locale isn't installed
  386.         getISO2Language();
  387.  
  388.         ResourceBundle resource = ResourceBundle.getBundle
  389.                 ("java.text.resources.LocaleElements", this);
  390.         return resource.getString("ShortLanguage");
  391.     }
  392.  
  393.     /**
  394.      * Getter for the three-letter ISO country abbreviation
  395.      * of the locale.  Returns the empty string if the locale doesn't specify a country.
  396.      * @exception MissingResourceException Throws MissingResourceException if the
  397.      * three-letter language abbreviation is not available for this locale.
  398.      */
  399.     public String getISO3Country() throws MissingResourceException {
  400.         if (country.length() == 0)
  401.             return "";
  402.  
  403.         // the call to getISO2Country() will throw a MissingResourceException if
  404.         // the appropriate locale isn't installed
  405.         getISO2Country();
  406.  
  407.         ResourceBundle resource = ResourceBundle.getBundle
  408.                 ("java.text.resources.LocaleElements", this);
  409.         return resource.getString("ShortCountry");
  410.     }
  411.  
  412.     /**
  413.      * Getter for the two-letter ISO language abbreviation
  414.      * of the locale.  Returns the empty string if the locale doesn't specify a language.
  415.      * @exception MissingResourceException Throws MissingResourceException if the
  416.      * two-letter language abbreviation is not available for this locale.
  417.      */
  418.     /*public*/ String getISO2Language() throws MissingResourceException {
  419.         if (language.length() == 0)
  420.             return "";
  421.  
  422.         ResourceBundle resource = ResourceBundle.getBundle
  423.                 ("java.text.resources.LocaleElements", this);
  424.         String localeID = resource.getString("LocaleString");
  425.         String result = localeID.substring(0, 2);
  426.         if (!result.equals(language))
  427.             throw new MissingResourceException("Requested resource bundle not installed",
  428.                 "LocaleElements", "LocaleString");
  429.         return result;
  430.     }
  431.  
  432.     /**
  433.      * Getter for the two-letter ISO country abbreviation
  434.      * of the locale.  Returns the empty string if the locale doesn't specify a country.
  435.      * @exception MissingResourceException Throws MissingResourceException if the
  436.      * two-letter language abbreviation is not available for this locale.
  437.      */
  438.     /*public*/ String getISO2Country() throws MissingResourceException {
  439.         if (country.length() == 0)
  440.             return "";
  441.  
  442.         ResourceBundle resource = ResourceBundle.getBundle
  443.                 ("java.text.resources.LocaleElements", this);
  444.         String localeID = resource.getString("LocaleString");
  445.         String result = localeID.substring(3, 5);
  446.         if (!result.equals(country))
  447.             throw new MissingResourceException("Requested resource bundle not installed",
  448.                 "LocaleElements", "LocaleString");
  449.         return result;
  450.     }
  451.  
  452.     /**
  453.      * Returns a name for the locale's language that is appropriate for display to the
  454.      * user.  This will be the name the locale's language localized for the default locale,
  455.      * if that data is available.  For example, if the locale is fr_FR and the default locale
  456.      * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
  457.      * the default locale is fr_FR, getDisplayLanguage() will return "anglais".  If the
  458.      * appropriate name isn't available (say, we don't have a Japanese name for Croatian),
  459.      * this function falls back on the English name and uses the ISO code as a last-resort
  460.      * value.  If the locale doesn't specify a language, this function returns the empty string.
  461.      */
  462.     public final String getDisplayLanguage() {
  463.         return getDisplayLanguage(getDefault());
  464.     }
  465.  
  466.     /**
  467.      * Returns a name for the locale's language that is appropriate for display to the
  468.      * user.  This will be the name the locale's language localized for inLocale,
  469.      * if that data is available.  For example, if the locale is fr_FR and inLocale
  470.      * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
  471.      * inLocale is fr_FR, getDisplayLanguage() will return "anglais".  If the
  472.      * appropriate name isn't available (say, we don't have a Japanese name for Croatian),
  473.      * this function falls back on the default locale, on the English name, and finally
  474.      * on the ISO code as a last-resort value.  If the locale doesn't specify a language,
  475.      * this function returns the empty string.
  476.      */
  477.     public String getDisplayLanguage(Locale inLocale) {
  478.         String  langCode = language;
  479.         if (langCode.length() == 0)
  480.             return "";
  481.  
  482.         Locale  workingLocale = (Locale)inLocale.clone();
  483.         String  result = null;
  484.         int     phase = 0;
  485.         boolean done = false;
  486.  
  487.         if (workingLocale.variant.length() == 0)
  488.             phase = 1;
  489.         if (workingLocale.country.length() == 0)
  490.             phase = 2;
  491.  
  492.         while (!done) {
  493.             try {
  494.                 ResourceBundle bundle = ResourceBundle.getBundle(
  495.                     "java.text.resources.LocaleElements", workingLocale);
  496.                 result = findStringMatch((String[][])bundle.getObject("Languages"),
  497.                                     langCode, langCode);
  498.                 if (result.length() != 0)
  499.                     done = true;
  500.             }
  501.             catch (Exception e) {
  502.                 // just fall through
  503.             }
  504.  
  505.             if (!done) {
  506.                 switch (phase) {
  507.                     case 0:
  508.                         workingLocale.variant = "";
  509.                         break;
  510.  
  511.                     case 1:
  512.                         workingLocale.country = "";
  513.                         break;
  514.  
  515.                     case 2:
  516.                         workingLocale = getDefault();
  517.                         break;
  518.  
  519.                     case 3:
  520.                         workingLocale = new Locale("", "", "");
  521.                         break;
  522.  
  523.                     default:
  524.                         return langCode;
  525.                 }
  526.                 phase++;
  527.             }
  528.         }
  529.         return result;
  530.     }
  531.  
  532.     /**
  533.      * Returns a name for the locale's country that is appropriate for display to the
  534.      * user.  This will be the name the locale's country localized for the default locale,
  535.      * if that data is available.  For example, if the locale is fr_FR and the default locale
  536.      * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
  537.      * the default locale is fr_FR, getDisplayLanguage() will return "Etats-Unis".  If the
  538.      * appropriate name isn't available (say, we don't have a Japanese name for Croatia),
  539.      * this function falls back on the English name and uses the ISO code as a last-resort
  540.      * value.  If the locale doesn't specify a country, this function returns the empty string.
  541.      */
  542.     public final String getDisplayCountry() {
  543.         return getDisplayCountry(getDefault());
  544.     }
  545.  
  546.     /**
  547.      * Returns a name for the locale's country that is appropriate for display to the
  548.      * user.  This will be the name the locale's country localized for inLocale,
  549.      * if that data is available.  For example, if the locale is fr_FR and inLocale
  550.      * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
  551.      * inLocale is fr_FR, getDisplayLanguage() will return "Etats-Unis".  If the
  552.      * appropriate name isn't available (say, we don't have a Japanese name for Croatia),
  553.      * this function falls back on the default locale, on the English name, and finally
  554.      * on the ISO code as a last-resort value.  If the locale doesn't specify a country,
  555.      * this function returns the empty string.
  556.      */
  557.     public String getDisplayCountry(Locale inLocale) {
  558.         String  ctryCode = country;
  559.         if (ctryCode.length() == 0)
  560.             return "";
  561.  
  562.         Locale  workingLocale = (Locale)inLocale.clone();
  563.         String  result = null;
  564.         int     phase = 0;
  565.         boolean done = false;
  566.  
  567.         if (workingLocale.variant.length() == 0)
  568.             phase = 1;
  569.         if (workingLocale.country.length() == 0)
  570.             phase = 2;
  571.  
  572.         while (!done) {
  573.             try {
  574.                 ResourceBundle bundle = ResourceBundle.getBundle(
  575.                     "java.text.resources.LocaleElements", workingLocale);
  576.                 result = findStringMatch((String[][])bundle.getObject("Countries"),
  577.                                     ctryCode, ctryCode);
  578.                 if (result.length() != 0)
  579.                     done = true;
  580.             }
  581.             catch (Exception e) {
  582.                 // just fall through
  583.             }
  584.  
  585.             if (!done) {
  586.                 switch (phase) {
  587.                     case 0:
  588.                         workingLocale.variant = "";
  589.                         break;
  590.  
  591.                     case 1:
  592.                         workingLocale.country = "";
  593.                         break;
  594.  
  595.                     case 2:
  596.                         workingLocale = getDefault();
  597.                         break;
  598.  
  599.                     case 3:
  600.                         workingLocale = new Locale("", "", "");
  601.                         break;
  602.  
  603.                     default:
  604.                         return ctryCode;
  605.                 }
  606.                 phase++;
  607.             }
  608.         }
  609.         return result;
  610.     }
  611.  
  612.     /**
  613.      * Returns a name for the locale's variant code that is appropriate for display to the
  614.      * user.  If possible, the name will be localized for the default locale.  If the locale
  615.      * doesn't specify a variant code, this function returns the empty string.
  616.      */
  617.     public final String getDisplayVariant() {
  618.         return getDisplayVariant(getDefault());
  619.     }
  620.  
  621.     /**
  622.      * Returns a name for the locale's variant code that is appropriate for display to the
  623.      * user.  If possible, the name will be localized for inLocale.  If the locale
  624.      * doesn't specify a variant code, this function returns the empty string.
  625.      */
  626.     public String getDisplayVariant(Locale inLocale) {
  627.         if (variant.length() == 0)
  628.             return "";
  629.  
  630.         ResourceBundle bundle = ResourceBundle.getBundle(
  631.                 "java.text.resources.LocaleElements", inLocale);
  632.  
  633.         String names[] = getDisplayVariantArray(bundle);
  634.  
  635.         // Get the localized patterns for formatting a list, and use
  636.         // them to format the list.
  637.         String[] patterns;
  638.         try {
  639.             patterns = (String[])bundle.getObject("LocaleNamePatterns");
  640.         }
  641.         catch (MissingResourceException e) {
  642.             patterns = null;
  643.         }
  644.         return formatList(patterns, names);
  645.    }
  646.  
  647.     /**
  648.      * Returns a name for the locale that is appropriate for display to the
  649.      * user.  This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
  650.      * and getDisplayVariant() assembled into a single string.  The display name will have
  651.      * one of the following forms:<p><blockquote>
  652.      * language (country, variant)<p>
  653.      * language (country)<p>
  654.      * language (variant)<p>
  655.      * country (variant)<p>
  656.      * language<p>
  657.      * country<p>
  658.      * variant<p></blockquote>
  659.      * depending on which fields are specified in the locale.  If the language, country,
  660.      * and variant fields are all empty, this function returns the empty string.
  661.      */
  662.     public final String getDisplayName() {
  663.         return getDisplayName(getDefault());
  664.     }
  665.  
  666.     /**
  667.      * Returns a name for the locale that is appropriate for display to the
  668.      * user.  This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
  669.      * and getDisplayVariant() assembled into a single string.  The display name will have
  670.      * one of the following forms:<p><blockquote>
  671.      * language (country, variant)<p>
  672.      * language (country)<p>
  673.      * language (variant)<p>
  674.      * country (variant)<p>
  675.      * language<p>
  676.      * country<p>
  677.      * variant<p></blockquote>
  678.      * depending on which fields are specified in the locale.  If the language, country,
  679.      * and variant fields are all empty, this function returns the empty string.
  680.      */
  681.     public String getDisplayName(Locale inLocale) {
  682.         ResourceBundle bundle = ResourceBundle.getBundle(
  683.                 "java.text.resources.LocaleElements", inLocale);
  684.  
  685.         String languageName = getDisplayLanguage(inLocale);
  686.         String countryName = getDisplayCountry(inLocale);
  687.         String[] variantNames = getDisplayVariantArray(bundle);
  688.  
  689.         // Get the localized patterns for formatting a display name.
  690.         String[] patterns;
  691.         try {
  692.             patterns = (String[])bundle.getObject("LocaleNamePatterns");
  693.         }
  694.         catch (MissingResourceException e) {
  695.             patterns = null;
  696.         }
  697.  
  698.         // The display name consists of a main name, followed by qualifiers.
  699.         // Typically, the format is "MainName (Qualifier, Qualifier)" but this
  700.         // depends on what pattern is stored in the display locale.
  701.         String   mainName       = null;
  702.         String[] qualifierNames = null;
  703.  
  704.         // The main name is the language, or if there is no language, the country.
  705.         // If there is neither language nor country (an anomalous situation) then
  706.         // the display name is simply the variant's display name.
  707.         if (languageName.length() != 0) {
  708.             mainName = languageName;
  709.             if (countryName.length() != 0) {
  710.                 qualifierNames = new String[variantNames.length + 1];
  711.                 System.arraycopy(variantNames, 0, qualifierNames, 1, variantNames.length);
  712.                 qualifierNames[0] = countryName;
  713.             }
  714.             else qualifierNames = variantNames;
  715.         }
  716.         else if (countryName.length() != 0) {
  717.             mainName = countryName;
  718.             qualifierNames = variantNames;
  719.         }
  720.         else {
  721.             return formatList(patterns, variantNames);
  722.         }
  723.  
  724.         // Create an array whose first element is the number of remaining
  725.         // elements.  This serves as a selector into a ChoiceFormat pattern from
  726.         // the resource.  The second and third elements are the main name and
  727.         // the qualifier; if there are no qualifiers, the third element is
  728.         // unused by the format pattern.
  729.         Object[] displayNames = {
  730.             new Integer(qualifierNames.length != 0 ? 2 : 1),
  731.             mainName,
  732.             // We could also just call formatList() and have it handle the empty
  733.             // list case, but this is more efficient, and we want it to be
  734.             // efficient since all the language-only locales will not have any
  735.             // qualifiers.
  736.             qualifierNames.length != 0 ? formatList(patterns, qualifierNames) : null
  737.         };
  738.  
  739.         if (patterns != null) {
  740.             return new MessageFormat(patterns[0]).format(displayNames);
  741.         }
  742.         else {
  743.             // If we cannot get the message format pattern, then we use a simple
  744.             // hard-coded pattern.  This should not occur in practice unless the
  745.             // installation is missing some core files (LocaleElements etc.).
  746.             StringBuffer result = new StringBuffer();
  747.             result.append((String)displayNames[1]);
  748.             if (displayNames.length > 2) {
  749.                 result.append(" (");
  750.                 result.append((String)displayNames[2]);
  751.                 result.append(")");
  752.             }
  753.             return result.toString();
  754.         }
  755.     }
  756.  
  757.     /**
  758.      * Overrides Cloneable
  759.      */
  760.     public Object clone()
  761.     {
  762.         try {
  763.             Locale that = (Locale)super.clone();
  764.             return that;
  765.         } catch (CloneNotSupportedException e) {
  766.             throw new InternalError();
  767.         }
  768.     }
  769.  
  770.     /**
  771.      * Override hashCode.
  772.      * Since Locales are often used in hashtables, caches the value
  773.      * for speed.
  774.      */
  775.       // XXX Depending on performance of synchronized, may want to
  776.       // XXX just compute in constructor.
  777.     public synchronized int hashCode() {
  778.         if (hashcode == -1) {
  779.             hashcode =
  780.         language.hashCode() ^
  781.         country.hashCode() ^
  782.         variant.hashCode();
  783.         }
  784.         return hashcode;
  785.     }
  786.  
  787.     // Overrides
  788.  
  789.     public boolean equals(Object obj) {
  790.         if (this == obj)                      // quick check
  791.             return true;
  792.         if (!(obj instanceof Locale))         // (1) same object?
  793.             return false;
  794.         Locale other = (Locale) obj;
  795.         if (hashCode() != other.hashCode()) return false;       // quick check
  796.         if (!language.equals(other.language)) return false;
  797.         if (!country.equals(other.country)) return false;
  798.         if (!variant.equals(other.variant)) return false;
  799.         return true; // we made it through the guantlet.
  800.         // (1)  We don't check super.equals since it is Object.
  801.         //      Since Locale is final, we don't have to check both directions.
  802.     }
  803.  
  804.     // ================= privates =====================================
  805.  
  806.     // XXX instance and class variables. For now keep these separate, since it is
  807.     // faster to match. Later, make into single string.
  808.  
  809.     private String language = "";
  810.     private String country = "";
  811.     private String variant = "";
  812.  
  813.     // this field really should be transient, but we made the mistake of letting it
  814.     // ship this way, so now we're stuck with it
  815.     private int hashcode = -1;        // lazy evaluated
  816.  
  817.     private static Locale defaultLocale;
  818.  
  819.     static {
  820.         /* The user.region property may be of the form country, country_variant,
  821.          * or _variant.  Since the Locale constructor takes the country value as
  822.          * an unparsed literal, and we don't want to change that behavior, we
  823.          * must premunge it here into country and variant.  Liu 7/9/98
  824.          */
  825.         String country = System.getProperty("user.region", "");
  826.         String variant = "";
  827.         int i = country.indexOf('_');
  828.         if (i >= 0) {
  829.             variant = country.substring(i+1);
  830.             country = country.substring(0, i);
  831.         }
  832.         defaultLocale = new Locale(System.getProperty("user.language", "EN"),
  833.                                    country, variant);
  834.     }
  835.  
  836.     /*
  837.      * Locale needs its own, locale insenitive version of toLowerCase to
  838.      * avoid circularity problems between Locale and String.
  839.      * The most straightforward algorithm is used. Look at optimizations later.
  840.      */
  841.     private String toLowerCase(String str) {
  842.         char[] buf = str.toCharArray();
  843.         for (int i = 0; i < buf.length; i++) {
  844.             buf[i] = Character.toLowerCase( buf[i] );
  845.         }
  846.         return new String( buf );
  847.     }
  848.  
  849.     /*
  850.      * Locale needs its own, locale insenitive version of toUpperCase to
  851.      * avoid circularity problems between Locale and String.
  852.      * The most straightforward algorithm is used. Look at optimizations later.
  853.      */
  854.     private String toUpperCase(String str) {
  855.         char[] buf = str.toCharArray();
  856.         for (int i = 0; i < buf.length; i++) {
  857.             buf[i] = Character.toUpperCase( buf[i] );
  858.         }
  859.         return new String( buf );
  860.     }
  861.  
  862.     private String findStringMatch(String[][] languages,
  863.                                    String desiredLanguage, String fallbackLanguage)
  864.     {
  865.         for (int i = 0; i < languages.length; ++i)
  866.             if (desiredLanguage.equals(languages[i][0]))
  867.                 return languages[i][1];
  868.         if (!fallbackLanguage.equals(desiredLanguage))
  869.             for (int i = 0; i < languages.length; ++i)
  870.                 if (fallbackLanguage.equals(languages[i][0]))
  871.                     return languages[i][1];
  872.         if (!"EN".equals(desiredLanguage) && "EN".equals(fallbackLanguage))
  873.             for (int i = 0; i < languages.length; ++i)
  874.                 if ("EN".equals(languages[i][0]))
  875.                     return languages[i][1];
  876.         return "";
  877.     }
  878.  
  879.     /**
  880.      * Return an array of the display names of the variant.
  881.      * @param bundle the ResourceBundle to use to get the display names
  882.      * @return an array of display names, possible of zero length.
  883.      */
  884.     private String[] getDisplayVariantArray(ResourceBundle bundle) {
  885.         // Split the variant name into tokens separated by '_'.
  886.         StringTokenizer tokenizer = new StringTokenizer(variant, "_");
  887.         String[] names = new String[tokenizer.countTokens()];
  888.  
  889.         // For each variant token, lookup the display name.  If
  890.         // not found, use the variant name itself.
  891.         for (int i=0; i<names.length; ++i) {
  892.             String token = tokenizer.nextToken();
  893.             try {   
  894.                 names[i] = (String)bundle.getObject("%%" + token);
  895.             }
  896.             catch (MissingResourceException e) {
  897.                 names[i] = token;
  898.             }
  899.         }
  900.  
  901.         return names;
  902.     }
  903.  
  904.     /**
  905.      * Format a list with an array of patterns.
  906.      * @param patterns an array of three patterns. The first pattern is not
  907.      * used. The second pattern should create a MessageFormat taking 0-3 arguments
  908.      * and formatting them into a list. The third pattern should take 2 arguments
  909.      * and is used by composeList. If patterns is null, then a the list is
  910.      * formatted by concatenation with the delimiter ','.
  911.      * @param stringList the list of strings to be formatted.
  912.      * @return a string representing the list.
  913.      */
  914.     private static String formatList(String[] patterns, String[] stringList) {
  915.         // If we have no list patterns, compose the list in a simple,
  916.         // non-localized way.
  917.         if (patterns == null) {
  918.             StringBuffer result = new StringBuffer();
  919.             for (int i=0; i<stringList.length; ++i) {
  920.                 if (i>0) result.append(',');
  921.                 result.append(stringList[i]);
  922.             }
  923.             return result.toString();
  924.         }
  925.  
  926.         // Compose the list down to three elements if necessary
  927.         if (stringList.length > 3) {
  928.             MessageFormat format = new MessageFormat(patterns[2]);
  929.             stringList = composeList(format, stringList);
  930.         }
  931.  
  932.         // Rebuild the argument list with the list length as the first element
  933.         Object[] args = new Object[stringList.length + 1];
  934.         System.arraycopy(stringList, 0, args, 1, stringList.length);
  935.         args[0] = new Integer(stringList.length);
  936.  
  937.         // Format it using the pattern in the resource
  938.         MessageFormat format = new MessageFormat(patterns[1]);
  939.         return format.format(args);
  940.     }
  941.  
  942.     /**
  943.      * Given a list of strings, return a list shortened to three elements.
  944.      * Shorten it by applying the given format to the first two elements
  945.      * recursively.
  946.      * @param format a format which takes two arguments
  947.      * @param list a list of strings
  948.      * @return if the list is three elements or shorter, the same list;
  949.      * otherwise, a new list of three elements.
  950.      */
  951.     private static String[] composeList(MessageFormat format, String[] list) {
  952.         if (list.length <= 3) return list;
  953.  
  954.         // Use the given format to compose the first two elements into one
  955.         String[] listItems = { list[0], list[1] };
  956.         String newItem = format.format(listItems);
  957.  
  958.         // Form a new list one element shorter
  959.         String[] newList = new String[list.length-1];
  960.         System.arraycopy(list, 2, newList, 1, newList.length-1);
  961.         newList[0] = newItem;
  962.  
  963.         // Recurse
  964.         return composeList(format, newList);
  965.     }
  966. }
  967.