home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / util / Locale.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  38.4 KB  |  978 lines

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