Locale.java 26.4 KB
Newer Older
1 2 3
/* Locale.java -- i18n locales
   Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc.

Tom Tromey committed
4
This file is part of GNU Classpath.
5

Tom Tromey committed
6 7 8 9
GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
10

Tom Tromey committed
11 12 13 14
GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.
15

Tom Tromey committed
16 17 18 19
You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.
20

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */
Tom Tromey committed
37 38 39

package java.util;

40 41 42 43 44
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

Tom Tromey committed
45
/**
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
 * Locales represent a specific country and culture. Classes which can be
 * passed a Locale object tailor their information for a given locale. For
 * instance, currency number formatting is handled differently for the USA
 * and France.
 *
 * <p>Locales are made up of a language code, a country code, and an optional
 * set of variant strings. Language codes are represented by
 * <a href="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt">
 * ISO 639:1988</a> w/ additions from ISO 639/RA Newsletter No. 1/1989
 * and a decision of the Advisory Committee of ISO/TC39 on August 8, 1997.
 *
 * <p>Country codes are represented by
 * <a href="http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html">
 * ISO 3166</a>. Variant strings are vendor and browser specific. Standard
 * variant strings include "POSIX" for POSIX, "WIN" for MS-Windows, and
 * "MAC" for Macintosh. When there is more than one variant string, they must
Tom Tromey committed
62
 * be separated by an underscore (U+005F).
63 64 65 66 67 68 69 70
 *
 * <p>The default locale is determined by the values of the system properties
 * user.language, user.region, and user.variant, defaulting to "en". Note that
 * the locale does NOT contain the conversion and formatting capabilities (for
 * that, use ResourceBundle and java.text). Rather, it is an immutable tag
 * object for identifying a given locale, which is referenced by these other
 * classes when they must make locale-dependent decisions.
 *
Tom Tromey committed
71 72 73 74 75 76
 * @see ResourceBundle
 * @see java.text.Format
 * @see java.text.NumberFormat
 * @see java.text.Collator
 * @author Jochen Hoenicke
 * @author Paul Fisher
77 78 79
 * @author Eric Blake <ebb9@email.byu.edu>
 * @since 1.1
 * @status updated to 1.4
Tom Tromey committed
80
 */
81
public final class Locale implements Serializable, Cloneable
Tom Tromey committed
82
{
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
  /** Locale which represents the English language. */
  public static final Locale ENGLISH = new Locale("en");

  /** Locale which represents the French language. */
  public static final Locale FRENCH = new Locale("fr");

  /** Locale which represents the German language. */
  public static final Locale GERMAN = new Locale("de");

  /** Locale which represents the Italian language. */
  public static final Locale ITALIAN = new Locale("it");

  /** Locale which represents the Japanese language. */
  public static final Locale JAPANESE = new Locale("ja");

  /** Locale which represents the Korean language. */
  public static final Locale KOREAN = new Locale("ko");

  /** Locale which represents the Chinese language. */
  public static final Locale CHINESE = new Locale("zh");

  /** Locale which represents the Chinese language as used in China. */
Tom Tromey committed
105
  public static final Locale SIMPLIFIED_CHINESE = new Locale("zh", "CN");
106

Tom Tromey committed
107 108 109 110 111
  /**
   * Locale which represents the Chinese language as used in Taiwan.
   * Same as TAIWAN Locale.
   */
  public static final Locale TRADITIONAL_CHINESE = new Locale("zh", "TW");
112 113

  /** Locale which represents France. */
Tom Tromey committed
114
  public static final Locale FRANCE = new Locale("fr", "FR");
115 116

  /** Locale which represents Germany. */
Tom Tromey committed
117
  public static final Locale GERMANY = new Locale("de", "DE");
118 119

  /** Locale which represents Italy. */
Tom Tromey committed
120
  public static final Locale ITALY = new Locale("it", "IT");
121 122

  /** Locale which represents Japan. */
Tom Tromey committed
123
  public static final Locale JAPAN = new Locale("ja", "JP");
124 125

  /** Locale which represents Korea. */
Tom Tromey committed
126
  public static final Locale KOREA = new Locale("ko", "KR");
127

Tom Tromey committed
128 129 130 131 132
  /**
   * Locale which represents China.
   * Same as SIMPLIFIED_CHINESE Locale.
   */
  public static final Locale CHINA = SIMPLIFIED_CHINESE;
133

Tom Tromey committed
134 135 136 137 138
  /**
   * Locale which represents the People's Republic of China.
   * Same as CHINA Locale.
   */
  public static final Locale PRC = CHINA;
139

Tom Tromey committed
140 141 142 143 144
  /**
   * Locale which represents Taiwan.
   * Same as TRADITIONAL_CHINESE Locale.
   */
  public static final Locale TAIWAN = TRADITIONAL_CHINESE;
145 146

  /** Locale which represents the United Kingdom. */
Tom Tromey committed
147
  public static final Locale UK = new Locale("en", "GB");
148 149

  /** Locale which represents the United States. */
Tom Tromey committed
150
  public static final Locale US = new Locale("en", "US");
151 152

  /** Locale which represents the English speaking portion of Canada. */
Tom Tromey committed
153
  public static final Locale CANADA = new Locale("en", "CA");
154 155

  /** Locale which represents the French speaking portion of Canada. */
Tom Tromey committed
156 157 158
  public static final Locale CANADA_FRENCH = new Locale("fr", "CA");

  /**
159
   * Compatible with JDK 1.1+.
Tom Tromey committed
160
   */
161
  private static final long serialVersionUID = 9149081749638150636L;
Tom Tromey committed
162 163 164

  /**
   * The language code, as returned by getLanguage().
165 166
   *
   * @serial the languange, possibly ""
Tom Tromey committed
167
   */
Tom Tromey committed
168
  private String language;
169

Tom Tromey committed
170 171
  /**
   * The country code, as returned by getCountry().
172 173
   *
   * @serial the country, possibly ""
Tom Tromey committed
174 175
   */
  private String country;
176

Tom Tromey committed
177 178
  /**
   * The variant code, as returned by getVariant().
179 180
   *
   * @serial the variant, possibly ""
Tom Tromey committed
181
   */
Tom Tromey committed
182
  private String variant;
183

Tom Tromey committed
184
  /**
185 186 187
   * This is the cached hashcode. When writing to stream, we write -1.
   *
   * @serial should be -1 in serial streams
Tom Tromey committed
188 189 190 191
   */
  private int hashcode;

  /**
192 193 194 195 196 197 198 199 200 201 202 203 204 205
   * The default locale. Except for during bootstrapping, this should never be
   * null. Note the logic in the main constructor, to detect when
   * bootstrapping has completed.
   */
  private static Locale defaultLocale =
    new Locale(System.getProperty("user.language", "en"),
               System.getProperty("user.region", ""),
               System.getProperty("user.variant", ""));

  /**
   * Convert new iso639 codes to the old ones.
   *
   * @param language the language to check
   * @return the appropriate code
Tom Tromey committed
206 207 208 209 210 211
   */
  private String convertLanguage(String language)
  {
    if (language.equals(""))
      return language;
    language = language.toLowerCase();
212
    int index = "he,id,yi".indexOf(language);
Tom Tromey committed
213
    if (index != -1)
214
      return "iw,in,ji".substring(index, index + 2);
Tom Tromey committed
215 216 217 218 219
    return language;
  }

  /**
   * Creates a new locale for the given language and country.
220 221 222 223 224
   *
   * @param language lowercase two-letter ISO-639 A2 language code
   * @param country uppercase two-letter ISO-3166 A2 contry code
   * @param variant vendor and browser specific
   * @throws NullPointerException if any argument is null
Tom Tromey committed
225 226 227
   */
  public Locale(String language, String country, String variant)
  {
228 229 230 231 232 233
    // During bootstrap, we already know the strings being passed in are
    // the correct capitalization, and not null. We can't call
    // String.toUpperCase during this time, since that depends on the
    // default locale.
    if (defaultLocale != null)
      {
234 235 236
        language = convertLanguage(language).intern();
        country = country.toUpperCase().intern();
        variant = variant.toUpperCase().intern();
237 238 239 240 241
      }
    this.language = language;
    this.country = country;
    this.variant = variant;
    hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode();
Tom Tromey committed
242
  }
243

Tom Tromey committed
244 245
  /**
   * Creates a new locale for the given language and country.
246 247 248 249
   *
   * @param language lowercase two-letter ISO-639 A2 language code
   * @param country uppercase two-letter ISO-3166 A2 country code
   * @throws NullPointerException if either argument is null
Tom Tromey committed
250 251
   */
  public Locale(String language, String country)
Tom Tromey committed
252
  {
Tom Tromey committed
253
    this(language, country, "");
Tom Tromey committed
254 255
  }

256 257 258 259 260 261 262 263 264 265 266
  /**
   * Creates a new locale for a language.
   *
   * @param language lowercase two-letter ISO-639 A2 language code
   * @throws NullPointerException if either argument is null
   * @since 1.4
   */
  public Locale(String language)
  {
    this(language, "", "");
  }
Tom Tromey committed
267 268

  /**
269 270 271 272 273 274
   * Returns the default Locale. The default locale is generally once set
   * on start up and then never changed. Normally you should use this locale
   * for everywhere you need a locale. The initial setting matches the
   * default locale, the user has chosen.
   *
   * @return the default locale for this virtual machine
Tom Tromey committed
275 276
   */
  public static Locale getDefault()
Tom Tromey committed
277
  {
Tom Tromey committed
278
    return defaultLocale;
Tom Tromey committed
279 280
  }

Tom Tromey committed
281
  /**
282 283 284 285 286 287 288 289 290
   * Changes the default locale. Normally only called on program start up.
   * Note that this doesn't change the locale for other programs. This has
   * a security check,
   * <code>PropertyPermission("user.language", "write")</code>, because of
   * its potential impact to running code.
   *
   * @param newLocale the new default locale
   * @throws NullPointerException if newLocale is null
   * @throws SecurityException if permission is denied
Tom Tromey committed
291 292
   */
  public static void setDefault(Locale newLocale)
293
  {
294 295 296 297 298
    if (newLocale == null)
      throw new NullPointerException();
    SecurityManager sm = System.getSecurityManager();
    if (sm != null)
      sm.checkPermission(new PropertyPermission("user.language", "write"));
Tom Tromey committed
299
    defaultLocale = newLocale;
300
  }
Tom Tromey committed
301

Tom Tromey committed
302 303
  /**
   * Returns the list of available locales.
304 305
   *
   * @return the installed locales
Tom Tromey committed
306 307 308 309 310
   */
  public static Locale[] getAvailableLocales()
  {
    /* I only return those for which localized language
     * or country information exists.
311 312
     * XXX - remove hard coded list, and implement more locales (Sun's JDK 1.4
     * has 148 installed locales!).
Tom Tromey committed
313 314
     */
    return new Locale[]
Tom Tromey committed
315
    {
Tom Tromey committed
316 317 318
      ENGLISH, FRENCH, GERMAN, new Locale("ga", "")
    };
  }
Tom Tromey committed
319

Tom Tromey committed
320 321
  /**
   * Returns a list of all 2-letter uppercase country codes as defined
322 323 324
   * in ISO 3166.
   *
   * @return a list of acceptible country codes
Tom Tromey committed
325 326
   */
  public static String[] getISOCountries()
Tom Tromey committed
327
  {
Tom Tromey committed
328 329
    return new String[]
    {
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
      "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN", "AO", "AQ", "AR", "AS",
      "AT", "AU", "AW", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI",
      "BJ", "BM", "BN", "BO", "BR", "BS", "BT", "BV", "BW", "BY", "BZ", "CA",
      "CC", "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", "CU",
      "CV", "CX", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE",
      "EG", "EH", "ER", "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", "FX",
      "GA", "GB", "GD", "GE", "GF", "GH", "GI", "GL", "GM", "GN", "GP", "GQ",
      "GR", "GS", "GT", "GU", "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU",
      "ID", "IE", "IL", "IN", "IO", "IQ", "IR", "IS", "IT", "JM", "JO", "JP",
      "KE", "KG", "KH", "KI", "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA",
      "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC",
      "MD", "MG", "MH", "MK", "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS",
      "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", "NC", "NE", "NF", "NG",
      "NI", "NL", "NO", "NP", "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG",
      "PH", "PK", "PL", "PM", "PN", "PR", "PT", "PW", "PY", "QA", "RE", "RO",
      "RU", "RW", "SA", "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", "SK",
      "SL", "SM", "SN", "SO", "SR", "ST", "SV", "SY", "SZ", "TC", "TD", "TF",
      "TG", "TH", "TJ", "TK", "TM", "TN", "TO", "TP", "TR", "TT", "TV", "TW",
      "TZ", "UA", "UG", "UM", "US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI",
      "VN", "VU", "WF", "WS", "YE", "YT", "YU", "ZA", "ZM", "ZR", "ZW"
    };
Tom Tromey committed
351 352
  }

Tom Tromey committed
353 354 355
  /**
   * Returns a list of all 2-letter lowercase language codes as defined
   * in ISO 639 (both old and new variant).
356 357
   *
   * @return a list of acceptable language codes
Tom Tromey committed
358 359 360 361 362
   */
  public static String[] getISOLanguages()
  {
    return new String[]
    {
363 364 365 366 367 368 369 370 371 372 373 374 375
      "aa", "ab", "af", "am", "ar", "as", "ay", "az", "ba", "be", "bg", "bh",
      "bi", "bn", "bo", "br", "ca", "co", "cs", "cy", "da", "de", "dz", "el",
      "en", "eo", "es", "et", "eu", "fa", "fi", "fj", "fo", "fr", "fy", "ga",
      "gd", "gl", "gn", "gu", "ha", "he", "hi", "hr", "hu", "hy", "ia", "id",
      "ie", "ik", "in", "is", "it", "iu", "iw", "ja", "ji", "jw", "ka", "kk",
      "kl", "km", "kn", "ko", "ks", "ku", "ky", "la", "ln", "lo", "lt", "lv",
      "mg", "mi", "mk", "ml", "mn", "mo", "mr", "ms", "mt", "my", "na", "ne",
      "nl", "no", "oc", "om", "or", "pa", "pl", "ps", "pt", "qu", "rm", "rn",
      "ro", "ru", "rw", "sa", "sd", "sg", "sh", "si", "sk", "sl", "sm", "sn",
      "so", "sq", "sr", "ss", "st", "su", "sv", "sw", "ta", "te", "tg", "th",
      "ti", "tk", "tl", "tn", "to", "tr", "ts", "tt", "tw", "ug", "uk", "ur",
      "uz", "vi", "vo", "wo", "xh", "yi", "yo", "za", "zh", "zu"
    };
Tom Tromey committed
376 377 378
  }

  /**
379 380 381 382 383
   * Returns the language code of this locale. Some language codes have changed
   * as ISO 639 has evolved; this returns the old name, even if you built
   * the locale with the new one.
   *
   * @return language code portion of this locale, or an empty String
Tom Tromey committed
384 385
   */
  public String getLanguage()
Tom Tromey committed
386 387 388 389
  {
    return language;
  }

Tom Tromey committed
390 391
  /**
   * Returns the country code of this locale.
392 393
   *
   * @return country code portion of this locale, or an empty String
Tom Tromey committed
394 395 396 397 398 399 400 401
   */
  public String getCountry()
  {
    return country;
  }

  /**
   * Returns the variant code of this locale.
402 403
   *
   * @return the variant code portion of this locale, or an empty String
Tom Tromey committed
404 405
   */
  public String getVariant()
Tom Tromey committed
406 407 408 409
  {
    return variant;
  }

Tom Tromey committed
410
  /**
411 412 413 414 415 416 417
   * Gets the string representation of the current locale. This consists of
   * the language, the country, and the variant, separated by an underscore.
   * The variant is listed only if there is a language or country. Examples:
   * "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC".
   *
   * @return the string representation of this Locale
   * @see #getDisplayName()
Tom Tromey committed
418 419
   */
  public final String toString()
Tom Tromey committed
420
  {
421 422
    if (language.length() == 0 && country.length() == 0)
      return "";
423 424
    else if (country.length() == 0 && variant.length() == 0)
      return language;
Tom Tromey committed
425
    StringBuffer result = new StringBuffer(language);
426
    result.append('_').append(country);
Tom Tromey committed
427
    if (variant.length() != 0)
428
      result.append('_').append(variant);
Tom Tromey committed
429 430 431 432 433
    return result.toString();
  }

  /**
   * Returns the three-letter ISO language abbrevation of this locale.
434 435
   *
   * @throws MissingResourceException if the three-letter code is not known
Tom Tromey committed
436
   */
437
  public String getISO3Language()
Tom Tromey committed
438
  {
439
    if (language == "")
440 441 442 443 444 445 446 447 448 449 450 451
      return "";
    int index
      = ("aa,ab,af,am,ar,as,ay,az,ba,be,bg,bh,bi,bn,bo,br,ca,co,cs,cy,da,"
         + "de,dz,el,en,eo,es,et,eu,fa,fi,fj,fo,fr,fy,ga,gd,gl,gn,gu,ha,iw,"
         + "hi,hr,hu,hy,ia,in,ie,ik,in,is,it,iu,iw,ja,ji,jw,ka,kk,kl,km,kn,"
         + "ko,ks,ku,ky,la,ln,lo,lt,lv,mg,mi,mk,ml,mn,mo,mr,ms,mt,my,na,ne,"
         + "nl,no,oc,om,or,pa,pl,ps,pt,qu,rm,rn,ro,ru,rw,sa,sd,sg,sh,si,sk,"
         + "sl,sm,sn,so,sq,sr,ss,st,su,sv,sw,ta,te,tg,th,ti,tk,tl,tn,to,tr,"
         + "ts,tt,tw,ug,uk,ur,uz,vi,vo,wo,xh,ji,yo,za,zh,zu")
      .indexOf(language);

    if (index % 3 != 0 || language.length() != 2)
Tom Tromey committed
452
      throw new MissingResourceException
453 454
        ("Can't find ISO3 language for " + language,
         "java.util.Locale", language);
Tom Tromey committed
455

456
    // Don't read this aloud. These are the three letter language codes.
Tom Tromey committed
457
    return
458 459 460 461 462 463 464 465
      ("aarabkaframharaasmaymazebakbelbulbihbisbenbodbrecatcoscescymdandeu"
       + "dzoellengepospaesteusfasfinfijfaofrafrygaigdhglggrngujhauhebhinhrv"
       + "hunhyeinaindileipkindislitaikuhebjpnyidjawkatkazkalkhmkankorkaskur"
       + "kirlatlinlaolitlavmlgmrimkdmalmonmolmarmsamltmyanaunepnldnorociorm"
       + "oripanpolpusporquerohrunronruskinsansndsagsrpsinslkslvsmosnasomsqi"
       + "srpsswsotsunsweswatamteltgkthatirtuktgltsntonturtsotattwiuigukrurd"
       + "uzbvievolwolxhoyidyorzhazhozul")
      .substring(index, index + 3);
Tom Tromey committed
466 467
  }

Tom Tromey committed
468 469
  /**
   * Returns the three-letter ISO country abbrevation of the locale.
470 471
   *
   * @throws MissingResourceException if the three-letter code is not known
Tom Tromey committed
472
   */
473
  public String getISO3Country()
Tom Tromey committed
474
  {
475
    if (country == "")
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
      return "";
    int index
      = ("AD,AE,AF,AG,AI,AL,AM,AN,AO,AQ,AR,AS,AT,AU,AW,AZ,BA,BB,BD,BE,BF,"
         + "BG,BH,BI,BJ,BM,BN,BO,BR,BS,BT,BV,BW,BY,BZ,CA,CC,CF,CG,CH,CI,CK,"
         + "CL,CM,CN,CO,CR,CU,CV,CX,CY,CZ,DE,DJ,DK,DM,DO,DZ,EC,EE,EG,EH,ER,"
         + "ES,ET,FI,FJ,FK,FM,FO,FR,FX,GA,GB,GD,GE,GF,GH,GI,GL,GM,GN,GP,GQ,"
         + "GR,GS,GT,GU,GW,GY,HK,HM,HN,HR,HT,HU,ID,IE,IL,IN,IO,IQ,IR,IS,IT,"
         + "JM,JO,JP,KE,KG,KH,KI,KM,KN,KP,KR,KW,KY,KZ,LA,LB,LC,LI,LK,LR,LS,"
         + "LT,LU,LV,LY,MA,MC,MD,MG,MH,MK,ML,MM,MN,MO,MP,MQ,MR,MS,MT,MU,MV,"
         + "MW,MX,MY,MZ,NA,NC,NE,NF,NG,NI,NL,NO,NP,NR,NU,NZ,OM,PA,PE,PF,PG,"
         + "PH,PK,PL,PM,PN,PR,PT,PW,PY,QA,RE,RO,RU,RW,SA,SB,SC,SD,SE,SG,SH,"
         + "SI,SJ,SK,SL,SM,SN,SO,SR,ST,SV,SY,SZ,TC,TD,TF,TG,TH,TJ,TK,TM,TN,"
         + "TO,TP,TR,TT,TV,TW,TZ,UA,UG,UM,US,UY,UZ,VA,VC,VE,VG,VI,VN,VU,WF,"
         + "WS,YE,YT,YU,ZA,ZM,ZR,ZW")
      .indexOf(country);

492
    if (index % 3 != 0 || country.length() != 2)
Tom Tromey committed
493
      throw new MissingResourceException
494 495
        ("Can't find ISO3 country for " + country,
         "java.util.Locale", country);
Tom Tromey committed
496

497
    // Don't read this aloud. These are the three letter country codes.
Tom Tromey committed
498
    return
499 500 501 502 503 504 505 506 507 508 509 510
      ("ANDAREAFGATGAIAALBARMANTAGOATAARGASMAUTAUSABWAZEBIHBRBBGDBELBFABGR"
       + "BHRBDIBENBMUBRNBOLBRABHSBTNBVTBWABLRBLZCANCCKCAFCOGCHECIVCOKCHLCMR"
       + "CHNCOLCRICUBCPVCXRCYPCZEDEUDJIDNKDMADOMDZAECUESTEGYESHERIESPETHFIN"
       + "FJIFLKFSMFROFRAFXXGABGBRGRDGEOGUFGHAGIBGRLGMBGINGLPGNQGRCSGSGTMGUM"
       + "GNBGUYHKGHMDHNDHRVHTIHUNIDNIRLISRINDIOTIRQIRNISLITAJAMJORJPNKENKGZ"
       + "KHMKIRCOMKNAPRKKORKWTCYMKAZLAOLBNLCALIELKALBRLSOLTULUXLVALBYMARMCO"
       + "MDAMDGMHLMKDMLIMMRMNGMACMNPMTQMRTMSRMLTMUSMDVMWIMEXMYSMOZNAMNCLNER"
       + "NFKNGANICNLDNORNPLNRUNIUNZLOMNPANPERPYFPNGPHLPAKPOLSPMPCNPRIPRTPLW"
       + "PRYQATREUROMRUSRWASAUSLBSYCSDNSWESGPSHNSVNSJMSVKSLESMRSENSOMSURSTP"
       + "SLVSYRSWZTCATCDATFTGOTHATJKTKLTKMTUNTONTMPTURTTOTUVTWNTZAUKRUGAUMI"
       + "USAURYUZBVATVCTVENVGBVIRVNMVUTWLFWSMYEMMYTYUGZAFZMBZARZWE")
      .substring(index, index + 3);
Tom Tromey committed
511 512
  }

513
  /**
Tom Tromey committed
514 515 516 517 518 519
   * Gets the country name suitable for display to the user, formatted
   * for the default locale.  This has the same effect as
   * <pre>
   * getDisplayLanguage(Locale.getDefault());
   * </pre>
   *
520 521
   * @return the language name of this locale localized to the default locale,
   *         with the ISO code as backup
Tom Tromey committed
522
   */
523
  public final String getDisplayLanguage()
Tom Tromey committed
524
  {
525
    return getDisplayLanguage(defaultLocale);
Tom Tromey committed
526 527
  }

528
  /**
Tom Tromey committed
529 530
   * Gets the language name suitable for display to the user, formatted
   * for a specified locale.
531
   *
Tom Tromey committed
532
   * @param locale locale to use for formatting
533 534
   * @return the language name of this locale localized to the given locale,
   *         with the ISO code as backup
Tom Tromey committed
535 536
   */
  public String getDisplayLanguage(Locale locale)
Tom Tromey committed
537
  {
Tom Tromey committed
538 539
    try
      {
540 541 542
        ResourceBundle bundle
          = ResourceBundle.getBundle("gnu.java.locale.iso639", locale);
        return bundle.getString(language);
Tom Tromey committed
543 544 545
      }
    catch (MissingResourceException ex)
      {
546
        return language;
Tom Tromey committed
547 548 549
      }
  }

550
  /**
Tom Tromey committed
551
   * Returns the country name of this locale localized to the
552 553
   * default locale. If the localized is not found, the ISO code
   * is returned. This has the same effect as
Tom Tromey committed
554 555 556
   * <pre>
   * getDisplayCountry(Locale.getDefault());
   * </pre>
557 558 559
   *
   * @return the country name of this locale localized to the given locale,
   *         with the ISO code as backup
Tom Tromey committed
560
   */
561
  public final String getDisplayCountry()
Tom Tromey committed
562
  {
563
    return getDisplayCountry(defaultLocale);
Tom Tromey committed
564 565
  }

566
  /**
Tom Tromey committed
567 568 569 570
   * Gets the country name suitable for display to the user, formatted
   * for a specified locale.
   *
   * @param locale locale to use for formatting
571 572 573
   * @return the country name of this locale localized to the given locale,
   *         with the ISO code as backup
   */
Tom Tromey committed
574
  public String getDisplayCountry(Locale locale)
Tom Tromey committed
575
  {
Tom Tromey committed
576
    try
Tom Tromey committed
577
      {
578 579 580
        ResourceBundle bundle =
          ResourceBundle.getBundle("gnu.java.locale.iso3166", locale);
        return bundle.getString(country);
Tom Tromey committed
581 582 583
      }
    catch (MissingResourceException ex)
      {
584
        return country;
Tom Tromey committed
585
      }
Tom Tromey committed
586 587
  }

588
  /**
Tom Tromey committed
589
   * Returns the variant name of this locale localized to the
590 591
   * default locale. If the localized is not found, the variant code
   * itself is returned. This has the same effect as
Tom Tromey committed
592 593 594
   * <pre>
   * getDisplayVariant(Locale.getDefault());
   * </pre>
595 596 597
   *
   * @return the variant code of this locale localized to the given locale,
   *         with the ISO code as backup
Tom Tromey committed
598
   */
599
  public final String getDisplayVariant()
Tom Tromey committed
600
  {
601
    return getDisplayVariant(defaultLocale);
Tom Tromey committed
602 603
  }

604
  /**
Tom Tromey committed
605
   * Returns the variant name of this locale localized to the
606
   * given locale. If the localized is not found, the variant code
Tom Tromey committed
607
   * itself is returned.
608 609 610 611
   *
   * @param locale locale to use for formatting
   * @return the variant code of this locale localized to the given locale,
   *         with the ISO code as backup
Tom Tromey committed
612 613 614
   */
  public String getDisplayVariant(Locale locale)
  {
615
    // XXX - load a bundle?
Tom Tromey committed
616 617 618 619 620
    return variant;
  }

  /**
   * Gets all local components suitable for display to the user, formatted
621 622
   * for the default locale. For the language component, getDisplayLanguage
   * is called. For the country component, getDisplayCountry is called.
Tom Tromey committed
623
   * For the variant set component, getDisplayVariant is called.
624 625
   *
   * <p>The returned String will be one of the following forms:<br>
Tom Tromey committed
626 627 628 629 630 631 632 633 634
   * <pre>
   * language (country, variant)
   * language (country)
   * language (variant)
   * country (variant)
   * language
   * country
   * variant
   * </pre>
635 636
   *
   * @return String version of this locale, suitable for display to the user
Tom Tromey committed
637
   */
638
  public final String getDisplayName()
Tom Tromey committed
639
  {
640
    return getDisplayName(defaultLocale);
Tom Tromey committed
641 642 643 644
  }

  /**
   * Gets all local components suitable for display to the user, formatted
645 646 647
   * for a specified locale. For the language component,
   * getDisplayLanguage(Locale) is called. For the country component,
   * getDisplayCountry(Locale) is called. For the variant set component,
Tom Tromey committed
648
   * getDisplayVariant(Locale) is called.
649 650
   *
   * <p>The returned String will be one of the following forms:<br>
Tom Tromey committed
651 652 653 654 655 656 657 658 659 660 661
   * <pre>
   * language (country, variant)
   * language (country)
   * language (variant)
   * country (variant)
   * language
   * country
   * variant
   * </pre>
   *
   * @param locale locale to use for formatting
662
   * @return String version of this locale, suitable for display to the user
Tom Tromey committed
663 664 665 666 667 668 669 670
   */
  public String getDisplayName(Locale locale)
  {
    StringBuffer result = new StringBuffer();
    int count = 0;
    String[] delimiters = {"", " (", ","};
    if (language.length() != 0)
      {
671 672
        result.append(delimiters[count++]);
        result.append(getDisplayLanguage(locale));
Tom Tromey committed
673 674 675
      }
    if (country.length() != 0)
      {
676 677
        result.append(delimiters[count++]);
        result.append(getDisplayCountry(locale));
Tom Tromey committed
678 679 680
      }
    if (variant.length() != 0)
      {
681 682
        result.append(delimiters[count++]);
        result.append(getDisplayVariant(locale));
Tom Tromey committed
683 684 685
      }
    if (count > 1)
      result.append(")");
Tom Tromey committed
686 687
    return result.toString();
  }
688

Tom Tromey committed
689 690
  /**
   * Does the same as <code>Object.clone()</code> but does not throw
691 692 693 694
   * a <code>CloneNotSupportedException</code>. Why anyone would
   * use this method is a secret to me, since this class is immutable.
   *
   * @return the clone
Tom Tromey committed
695 696 697
   */
  public Object clone()
  {
698 699
    // This class is final, so no need to use native super.clone().
    return new Locale(language, country, variant);
Tom Tromey committed
700 701 702
  }

  /**
703
   * Return the hash code for this locale. The hashcode is the logical
Tom Tromey committed
704 705 706
   * xor of the hash codes of the language, the country and the variant.
   * The hash code is precomputed, since <code>Locale</code>s are often
   * used in hash tables.
707 708
   *
   * @return the hashcode
Tom Tromey committed
709 710 711 712 713 714 715 716 717
   */
  public synchronized int hashCode()
  {
    // This method is synchronized because writeObject() might reset
    // the hashcode.
    return hashcode;
  }

  /**
718 719 720 721 722
   * Compares two locales. To be equal, obj must be a Locale with the same
   * language, country, and variant code.
   *
   * @param obj the other locale
   * @return true if obj is equal to this
Tom Tromey committed
723 724 725
   */
  public boolean equals(Object obj)
  {
726 727
    if (this == obj)
      return true;
728
    if (! (obj instanceof Locale))
Tom Tromey committed
729 730
      return false;
    Locale l = (Locale) obj;
731

732 733 734 735 736 737 738
    // ??? We might also want to add:
    //        hashCode() == l.hashCode()
    // But this is a synchronized method.  Is the overhead worth it?
    // Measure this to make a decision.
    return (language == l.language
            && country == l.country
            && variant == l.variant);
Tom Tromey committed
739 740 741
  }

  /**
742 743 744 745 746 747
   * Write the locale to an object stream.
   *
   * @param output the stream to write to
   * @throws IOException if the write fails
   * @serialData the hashcode should always be written as -1, and recomputed
   *      when reading it back
748
   */
749 750
  private synchronized void writeObject(ObjectOutputStream output)
    throws IOException
751
  {
752
    // Synchronized so that hashCode() doesn't get wrong value.
753 754 755 756 757 758 759
    int tmpHashcode = hashcode;
    hashcode = -1;
    output.defaultWriteObject();
    hashcode = tmpHashcode;
  }

  /**
760 761 762 763 764 765
   * Reads a locale from the input stream.
   *
   * @param input the stream to read from
   * @throws IOException if reading fails
   * @throws ClassNotFoundException if reading fails
   * @serialData the hashCode is always invalid and must be recomputed
766
   */
767 768
  private void readObject(ObjectInputStream input)
    throws IOException, ClassNotFoundException
769 770 771 772
  {
    input.defaultReadObject();
    hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode();
  }
773
} // class Locale