Source for java.util.Locale

   1: /* Locale.java -- i18n locales
   2:    Copyright (C) 1998, 1999, 2001, 2002, 2005, 2006  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package java.util;
  40: 
  41: import gnu.classpath.SystemProperties;
  42: import gnu.java.locale.LocaleHelper;
  43: 
  44: import java.io.IOException;
  45: import java.io.ObjectInputStream;
  46: import java.io.ObjectOutputStream;
  47: import java.io.Serializable;
  48: 
  49: import java.util.spi.LocaleNameProvider;
  50: 
  51: /**
  52:  * Locales represent a specific country and culture. Classes which can be
  53:  * passed a Locale object tailor their information for a given locale. For
  54:  * instance, currency number formatting is handled differently for the USA
  55:  * and France.
  56:  *
  57:  * <p>Locales are made up of a language code, a country code, and an optional
  58:  * set of variant strings. Language codes are represented by
  59:  * <a href="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt">
  60:  * ISO 639:1988</a> w/ additions from ISO 639/RA Newsletter No. 1/1989
  61:  * and a decision of the Advisory Committee of ISO/TC39 on August 8, 1997.
  62:  *
  63:  * <p>Country codes are represented by
  64:  * <a href="http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html">
  65:  * ISO 3166</a>. Variant strings are vendor and browser specific. Standard
  66:  * variant strings include "POSIX" for POSIX, "WIN" for MS-Windows, and
  67:  * "MAC" for Macintosh. When there is more than one variant string, they must
  68:  * be separated by an underscore (U+005F).
  69:  *
  70:  * <p>The default locale is determined by the values of the system properties
  71:  * user.language, user.country (or user.region), and user.variant, defaulting
  72:  * to "en_US". Note that the locale does NOT contain the conversion and
  73:  * formatting capabilities (for that, use ResourceBundle and java.text).
  74:  * Rather, it is an immutable tag object for identifying a given locale, which
  75:  * is referenced by these other classes when they must make locale-dependent
  76:  * decisions.
  77:  *
  78:  * @see ResourceBundle
  79:  * @see java.text.Format
  80:  * @see java.text.NumberFormat
  81:  * @see java.text.Collator
  82:  * @author Jochen Hoenicke
  83:  * @author Paul Fisher
  84:  * @author Eric Blake (ebb9@email.byu.edu)
  85:  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
  86:  * @since 1.1
  87:  * @status updated to 1.4
  88:  */
  89: public final class Locale implements Serializable, Cloneable
  90: {
  91:   /** Locale which represents the English language. */
  92:   public static final Locale ENGLISH = getLocale("en");
  93: 
  94:   /** Locale which represents the French language. */
  95:   public static final Locale FRENCH = getLocale("fr");
  96: 
  97:   /** Locale which represents the German language. */
  98:   public static final Locale GERMAN = getLocale("de");
  99: 
 100:   /** Locale which represents the Italian language. */
 101:   public static final Locale ITALIAN = getLocale("it");
 102: 
 103:   /** Locale which represents the Japanese language. */
 104:   public static final Locale JAPANESE = getLocale("ja");
 105: 
 106:   /** Locale which represents the Korean language. */
 107:   public static final Locale KOREAN = getLocale("ko");
 108: 
 109:   /** Locale which represents the Chinese language. */
 110:   public static final Locale CHINESE = getLocale("zh");
 111: 
 112:   /** Locale which represents the Chinese language as used in China. */
 113:   public static final Locale SIMPLIFIED_CHINESE = getLocale("zh", "CN");
 114: 
 115:   /**
 116:    * Locale which represents the Chinese language as used in Taiwan.
 117:    * Same as TAIWAN Locale.
 118:    */
 119:   public static final Locale TRADITIONAL_CHINESE = getLocale("zh", "TW");
 120: 
 121:   /** Locale which represents France. */
 122:   public static final Locale FRANCE = getLocale("fr", "FR");
 123: 
 124:   /** Locale which represents Germany. */
 125:   public static final Locale GERMANY = getLocale("de", "DE");
 126: 
 127:   /** Locale which represents Italy. */
 128:   public static final Locale ITALY = getLocale("it", "IT");
 129: 
 130:   /** Locale which represents Japan. */
 131:   public static final Locale JAPAN = getLocale("ja", "JP");
 132: 
 133:   /** Locale which represents Korea. */
 134:   public static final Locale KOREA = getLocale("ko", "KR");
 135: 
 136:   /**
 137:    * Locale which represents China.
 138:    * Same as SIMPLIFIED_CHINESE Locale.
 139:    */
 140:   public static final Locale CHINA = SIMPLIFIED_CHINESE;
 141: 
 142:   /**
 143:    * Locale which represents the People's Republic of China.
 144:    * Same as CHINA Locale.
 145:    */
 146:   public static final Locale PRC = CHINA;
 147: 
 148:   /**
 149:    * Locale which represents Taiwan.
 150:    * Same as TRADITIONAL_CHINESE Locale.
 151:    */
 152:   public static final Locale TAIWAN = TRADITIONAL_CHINESE;
 153: 
 154:   /** Locale which represents the United Kingdom. */
 155:   public static final Locale UK = getLocale("en", "GB");
 156: 
 157:   /** Locale which represents the United States. */
 158:   public static final Locale US = getLocale("en", "US");
 159: 
 160:   /** Locale which represents the English speaking portion of Canada. */
 161:   public static final Locale CANADA = getLocale("en", "CA");
 162: 
 163:   /** Locale which represents the French speaking portion of Canada. */
 164:   public static final Locale CANADA_FRENCH = getLocale("fr", "CA");
 165: 
 166:   /** The root locale, used as the base case in lookups by
 167:    *  locale-sensitive operations.
 168:    */
 169:   public static final Locale ROOT = new Locale("","","");
 170: 
 171:   /**
 172:    * Compatible with JDK 1.1+.
 173:    */
 174:   private static final long serialVersionUID = 9149081749638150636L;
 175: 
 176:   /**
 177:    * The language code, as returned by getLanguage().
 178:    *
 179:    * @serial the languange, possibly ""
 180:    */
 181:   private final String language;
 182: 
 183:   /**
 184:    * The country code, as returned by getCountry().
 185:    *
 186:    * @serial the country, possibly ""
 187:    */
 188:   private final String country;
 189: 
 190:   /**
 191:    * The variant code, as returned by getVariant().
 192:    *
 193:    * @serial the variant, possibly ""
 194:    */
 195:   private final String variant;
 196: 
 197:   /**
 198:    * This is the cached hashcode. When writing to stream, we write -1.
 199:    *
 200:    * @serial should be -1 in serial streams
 201:    */
 202:   private int hashcode;
 203: 
 204:   /**
 205:    * Array storing all available locales.
 206:    */
 207:   private static transient Locale[] availableLocales;
 208: 
 209:   /**
 210:    * Locale cache. Only created locale objects are stored.
 211:    * Contains all supported locales when getAvailableLocales()
 212:    * got called.
 213:    */
 214:   private static transient HashMap localeMap;
 215:   
 216:   /**
 217:    * The default locale. Except for during bootstrapping, this should never be
 218:    * null. Note the logic in the main constructor, to detect when
 219:    * bootstrapping has completed.
 220:    */
 221:   private static Locale defaultLocale;
 222: 
 223:   static {
 224:     String language = SystemProperties.getProperty("user.language", "en");
 225:     String country  = SystemProperties.getProperty("user.country", "US");
 226:     String region   = SystemProperties.getProperty("user.region", null);
 227:     String variant  = SystemProperties.getProperty("user.variant", "");
 228: 
 229:     defaultLocale = getLocale(language,
 230:                               (region != null) ? region : country,
 231:                               variant);
 232:   }
 233: 
 234:   /**
 235:    * Array storing all the available two-letter ISO639 languages.
 236:    */
 237:   private static transient String[] languageCache;
 238: 
 239:   /**
 240:    * Array storing all the available two-letter ISO3166 country codes.
 241:    */
 242:   private static transient String[] countryCache;
 243: 
 244:   /**
 245:    * Retrieves the locale with the specified language from the cache.
 246:    *
 247:    * @param language the language of the locale to retrieve.
 248:    * @return the locale.
 249:    */ 
 250:   private static Locale getLocale(String language)
 251:   {
 252:     return getLocale(language, "", "");
 253:   }
 254:   
 255:   /**
 256:    * Retrieves the locale with the specified language and country
 257:    * from the cache.
 258:    *
 259:    * @param language the language of the locale to retrieve.
 260:    * @param country the country of the locale to retrieve.
 261:    * @return the locale.
 262:    */ 
 263:   private static Locale getLocale(String language, String country)
 264:   {
 265:     return getLocale(language, country, "");
 266:   }
 267:   
 268:   /**
 269:    * Retrieves the locale with the specified language, country
 270:    * and variant from the cache.
 271:    *
 272:    * @param language the language of the locale to retrieve.
 273:    * @param country the country of the locale to retrieve.
 274:    * @param variant the variant of the locale to retrieve.
 275:    * @return the locale.
 276:    */ 
 277:   private static Locale getLocale(String language, String country, String variant)
 278:   {
 279:     if (localeMap == null)
 280:       localeMap = new HashMap(256);
 281: 
 282:     String name = language + "_" + country + "_" + variant;
 283:     Locale locale = (Locale) localeMap.get(name);
 284: 
 285:     if (locale == null)
 286:       {
 287:     locale = new Locale(language, country, variant);
 288:     localeMap.put(name, locale);
 289:       }
 290: 
 291:     return locale;
 292:   }
 293:   
 294:   /**
 295:    * Convert new iso639 codes to the old ones.
 296:    *
 297:    * @param language the language to check
 298:    * @return the appropriate code
 299:    */
 300:   private String convertLanguage(String language)
 301:   {
 302:     if (language.equals(""))
 303:       return language;
 304:     language = language.toLowerCase();
 305:     int index = "he,id,yi".indexOf(language);
 306:     if (index != -1)
 307:       return "iw,in,ji".substring(index, index + 2);
 308:     return language;
 309:   }
 310: 
 311:   /**
 312:    * Creates a new locale for the given language and country.
 313:    *
 314:    * @param language lowercase two-letter ISO-639 A2 language code
 315:    * @param country uppercase two-letter ISO-3166 A2 contry code
 316:    * @param variant vendor and browser specific
 317:    * @throws NullPointerException if any argument is null
 318:    */
 319:   public Locale(String language, String country, String variant)
 320:   {
 321:     // During bootstrap, we already know the strings being passed in are
 322:     // the correct capitalization, and not null. We can't call
 323:     // String.toUpperCase during this time, since that depends on the
 324:     // default locale.
 325:     if (defaultLocale != null)
 326:       {
 327:         language = convertLanguage(language);
 328:         country = country.toUpperCase();
 329:       }
 330:     this.language = language.intern();
 331:     this.country = country.intern();
 332:     this.variant = variant.intern();
 333:     hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode();
 334:   }
 335: 
 336:   /**
 337:    * Creates a new locale for the given language and country.
 338:    *
 339:    * @param language lowercase two-letter ISO-639 A2 language code
 340:    * @param country uppercase two-letter ISO-3166 A2 country code
 341:    * @throws NullPointerException if either argument is null
 342:    */
 343:   public Locale(String language, String country)
 344:   {
 345:     this(language, country, "");
 346:   }
 347: 
 348:   /**
 349:    * Creates a new locale for a language.
 350:    *
 351:    * @param language lowercase two-letter ISO-639 A2 language code
 352:    * @throws NullPointerException if either argument is null
 353:    * @since 1.4
 354:    */
 355:   public Locale(String language)
 356:   {
 357:     this(language, "", "");
 358:   }
 359: 
 360:   /**
 361:    * Returns the default Locale. The default locale is generally once set
 362:    * on start up and then never changed. Normally you should use this locale
 363:    * for everywhere you need a locale. The initial setting matches the
 364:    * default locale, the user has chosen.
 365:    *
 366:    * @return the default locale for this virtual machine
 367:    */
 368:   public static Locale getDefault()
 369:   {
 370:     return defaultLocale;
 371:   }
 372: 
 373:   /**
 374:    * Changes the default locale. Normally only called on program start up.
 375:    * Note that this doesn't change the locale for other programs. This has
 376:    * a security check,
 377:    * <code>PropertyPermission("user.language", "write")</code>, because of
 378:    * its potential impact to running code.
 379:    *
 380:    * @param newLocale the new default locale
 381:    * @throws NullPointerException if newLocale is null
 382:    * @throws SecurityException if permission is denied
 383:    */
 384:   public static void setDefault(Locale newLocale)
 385:   {
 386:     if (newLocale == null)
 387:       throw new NullPointerException();
 388:     SecurityManager sm = System.getSecurityManager();
 389:     if (sm != null)
 390:       sm.checkPermission(new PropertyPermission("user.language", "write"));
 391:     defaultLocale = newLocale;
 392:   }
 393: 
 394:   /**
 395:    * Returns the list of available locales.
 396:    *
 397:    * @return the installed locales
 398:    */
 399:   public static synchronized Locale[] getAvailableLocales()
 400:   {
 401:     if (availableLocales == null)
 402:       {
 403:         int len = LocaleHelper.getLocaleCount();
 404:         availableLocales = new Locale[len];
 405: 
 406:         for (int i = 0; i < len; i++)
 407:           {
 408:             String language;
 409:             String country = "";
 410:             String variant = "";
 411:             String name = LocaleHelper.getLocaleName(i);
 412: 
 413:             language = name.substring(0, 2);
 414: 
 415:             if (name.length() > 2)
 416:               country = name.substring(3);
 417: 
 418:         int index = country.indexOf("_");
 419:         if (index > 0)
 420:           {
 421:         variant = country.substring(index + 1);
 422:         country = country.substring(0, index - 1);
 423:           }
 424: 
 425:             availableLocales[i] = getLocale(language, country, variant);
 426:           }
 427:       }
 428:     
 429:     return (Locale[]) availableLocales.clone();
 430:   }
 431: 
 432:   /**
 433:    * Returns a list of all 2-letter uppercase country codes as defined
 434:    * in ISO 3166.
 435:    *
 436:    * @return a list of acceptable country codes
 437:    */
 438:   public static String[] getISOCountries()
 439:   {
 440:     if (countryCache == null)
 441:       {
 442:     countryCache = getISOStrings("territories");
 443:       }
 444: 
 445:     return (String[]) countryCache.clone();
 446:   }
 447: 
 448:   /**
 449:    * Returns a list of all 2-letter lowercase language codes as defined
 450:    * in ISO 639 (both old and new variant).
 451:    *
 452:    * @return a list of acceptable language codes
 453:    */
 454:   public static String[] getISOLanguages()
 455:   {
 456:     if (languageCache == null)
 457:       {
 458:     languageCache = getISOStrings("languages");
 459:       }
 460:     return (String[]) languageCache.clone();
 461:   }
 462: 
 463:   /**
 464:    * Returns the set of keys from the specified resource hashtable, filtered
 465:    * so that only two letter strings are returned.
 466:    *
 467:    * @param tableName the name of the table from which to retrieve the keys.
 468:    * @return an array of two-letter strings.
 469:    */
 470:   private static String[] getISOStrings(String tableName)
 471:   {
 472:     int count = 0;
 473:     ResourceBundle bundle =
 474:       ResourceBundle.getBundle("gnu.java.locale.LocaleInformation");
 475:     Enumeration e = bundle.getKeys();
 476:     ArrayList tempList = new ArrayList();
 477: 
 478:     while (e.hasMoreElements())
 479:       {
 480:     String key = (String) e.nextElement();
 481:     
 482:     if (key.startsWith(tableName + "."))
 483:       {
 484:         String str = key.substring(tableName.length() + 1);
 485: 
 486:         if (str.length() == 2
 487:         && Character.isLetter(str.charAt(0))
 488:         && Character.isLetter(str.charAt(1)))
 489:           {
 490:         tempList.add(str);
 491:         ++count;
 492:           }
 493:       }
 494:       }
 495: 
 496:     String[] strings = new String[count];
 497:     
 498:     for (int a = 0; a < count; ++a)
 499:       strings[a] = (String) tempList.get(a);
 500:     
 501:     return strings;
 502:   }
 503: 
 504:   /**
 505:    * Returns the language code of this locale. Some language codes have changed
 506:    * as ISO 639 has evolved; this returns the old name, even if you built
 507:    * the locale with the new one.
 508:    *
 509:    * @return language code portion of this locale, or an empty String
 510:    */
 511:   public String getLanguage()
 512:   {
 513:     return language;
 514:   }
 515: 
 516:   /**
 517:    * Returns the country code of this locale.
 518:    *
 519:    * @return country code portion of this locale, or an empty String
 520:    */
 521:   public String getCountry()
 522:   {
 523:     return country;
 524:   }
 525: 
 526:   /**
 527:    * Returns the variant code of this locale.
 528:    *
 529:    * @return the variant code portion of this locale, or an empty String
 530:    */
 531:   public String getVariant()
 532:   {
 533:     return variant;
 534:   }
 535: 
 536:   /**
 537:    * Gets the string representation of the current locale. This consists of
 538:    * the language, the country, and the variant, separated by an underscore.
 539:    * The variant is listed only if there is a language or country. Examples:
 540:    * "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC".
 541:    *
 542:    * @return the string representation of this Locale
 543:    * @see #getDisplayName()
 544:    */
 545:   public String toString()
 546:   {
 547:     if (language.length() == 0 && country.length() == 0)
 548:       return "";
 549:     else if (country.length() == 0 && variant.length() == 0)
 550:       return language;
 551:     StringBuffer result = new StringBuffer(language);
 552:     result.append('_').append(country);
 553:     if (variant.length() != 0)
 554:       result.append('_').append(variant);
 555:     return result.toString();
 556:   }
 557: 
 558:   /**
 559:    * Returns the three-letter ISO language abbrevation of this locale.
 560:    *
 561:    * @throws MissingResourceException if the three-letter code is not known
 562:    */
 563:   public String getISO3Language()
 564:   {
 565:     // We know all strings are interned so we can use '==' for better performance.
 566:     if (language == "")
 567:       return "";
 568:     int index
 569:       = ("aa,ab,af,am,ar,as,ay,az,ba,be,bg,bh,bi,bn,bo,br,ca,co,cs,cy,da,"
 570:          + "de,dz,el,en,eo,es,et,eu,fa,fi,fj,fo,fr,fy,ga,gd,gl,gn,gu,ha,iw,"
 571:          + "hi,hr,hu,hy,ia,in,ie,ik,in,is,it,iu,iw,ja,ji,jw,ka,kk,kl,km,kn,"
 572:          + "ko,ks,ku,ky,la,ln,lo,lt,lv,mg,mi,mk,ml,mn,mo,mr,ms,mt,my,na,ne,"
 573:          + "nl,no,oc,om,or,pa,pl,ps,pt,qu,rm,rn,ro,ru,rw,sa,sd,sg,sh,si,sk,"
 574:          + "sl,sm,sn,so,sq,sr,ss,st,su,sv,sw,ta,te,tg,th,ti,tk,tl,tn,to,tr,"
 575:          + "ts,tt,tw,ug,uk,ur,uz,vi,vo,wo,xh,ji,yo,za,zh,zu")
 576:       .indexOf(language);
 577: 
 578:     if (index % 3 != 0 || language.length() != 2)
 579:       throw new MissingResourceException
 580:         ("Can't find ISO3 language for " + language,
 581:          "java.util.Locale", language);
 582: 
 583:     // Don't read this aloud. These are the three letter language codes.
 584:     return
 585:       ("aarabkaframharaasmaymazebakbelbulbihbisbenbodbrecatcoscescymdandeu"
 586:        + "dzoellengepospaesteusfasfinfijfaofrafrygaigdhglggrngujhauhebhinhrv"
 587:        + "hunhyeinaindileipkindislitaikuhebjpnyidjawkatkazkalkhmkankorkaskur"
 588:        + "kirlatlinlaolitlavmlgmrimkdmalmonmolmarmsamltmyanaunepnldnorociorm"
 589:        + "oripanpolpusporquerohrunronruskinsansndsagsrpsinslkslvsmosnasomsqi"
 590:        + "srpsswsotsunsweswatamteltgkthatirtuktgltsntonturtsotattwiuigukrurd"
 591:        + "uzbvievolwolxhoyidyorzhazhozul")
 592:       .substring(index, index + 3);
 593:   }
 594: 
 595:   /**
 596:    * Returns the three-letter ISO country abbrevation of the locale.
 597:    *
 598:    * @throws MissingResourceException if the three-letter code is not known
 599:    */
 600:   public String getISO3Country()
 601:   {
 602:     // We know all strings are interned so we can use '==' for better performance.
 603:     if (country == "")
 604:       return "";
 605:     int index
 606:       = ("AD,AE,AF,AG,AI,AL,AM,AN,AO,AQ,AR,AS,AT,AU,AW,AZ,BA,BB,BD,BE,BF,"
 607:          + "BG,BH,BI,BJ,BM,BN,BO,BR,BS,BT,BV,BW,BY,BZ,CA,CC,CF,CG,CH,CI,CK,"
 608:          + "CL,CM,CN,CO,CR,CU,CV,CX,CY,CZ,DE,DJ,DK,DM,DO,DZ,EC,EE,EG,EH,ER,"
 609:          + "ES,ET,FI,FJ,FK,FM,FO,FR,FX,GA,GB,GD,GE,GF,GH,GI,GL,GM,GN,GP,GQ,"
 610:          + "GR,GS,GT,GU,GW,GY,HK,HM,HN,HR,HT,HU,ID,IE,IL,IN,IO,IQ,IR,IS,IT,"
 611:          + "JM,JO,JP,KE,KG,KH,KI,KM,KN,KP,KR,KW,KY,KZ,LA,LB,LC,LI,LK,LR,LS,"
 612:          + "LT,LU,LV,LY,MA,MC,MD,MG,MH,MK,ML,MM,MN,MO,MP,MQ,MR,MS,MT,MU,MV,"
 613:          + "MW,MX,MY,MZ,NA,NC,NE,NF,NG,NI,NL,NO,NP,NR,NU,NZ,OM,PA,PE,PF,PG,"
 614:          + "PH,PK,PL,PM,PN,PR,PT,PW,PY,QA,RE,RO,RU,RW,SA,SB,SC,SD,SE,SG,SH,"
 615:          + "SI,SJ,SK,SL,SM,SN,SO,SR,ST,SV,SY,SZ,TC,TD,TF,TG,TH,TJ,TK,TM,TN,"
 616:          + "TO,TP,TR,TT,TV,TW,TZ,UA,UG,UM,US,UY,UZ,VA,VC,VE,VG,VI,VN,VU,WF,"
 617:          + "WS,YE,YT,YU,ZA,ZM,ZR,ZW")
 618:       .indexOf(country);
 619: 
 620:     if (index % 3 != 0 || country.length() != 2)
 621:       throw new MissingResourceException
 622:         ("Can't find ISO3 country for " + country,
 623:          "java.util.Locale", country);
 624: 
 625:     // Don't read this aloud. These are the three letter country codes.
 626:     return
 627:       ("ANDAREAFGATGAIAALBARMANTAGOATAARGASMAUTAUSABWAZEBIHBRBBGDBELBFABGR"
 628:        + "BHRBDIBENBMUBRNBOLBRABHSBTNBVTBWABLRBLZCANCCKCAFCOGCHECIVCOKCHLCMR"
 629:        + "CHNCOLCRICUBCPVCXRCYPCZEDEUDJIDNKDMADOMDZAECUESTEGYESHERIESPETHFIN"
 630:        + "FJIFLKFSMFROFRAFXXGABGBRGRDGEOGUFGHAGIBGRLGMBGINGLPGNQGRCSGSGTMGUM"
 631:        + "GNBGUYHKGHMDHNDHRVHTIHUNIDNIRLISRINDIOTIRQIRNISLITAJAMJORJPNKENKGZ"
 632:        + "KHMKIRCOMKNAPRKKORKWTCYMKAZLAOLBNLCALIELKALBRLSOLTULUXLVALBYMARMCO"
 633:        + "MDAMDGMHLMKDMLIMMRMNGMACMNPMTQMRTMSRMLTMUSMDVMWIMEXMYSMOZNAMNCLNER"
 634:        + "NFKNGANICNLDNORNPLNRUNIUNZLOMNPANPERPYFPNGPHLPAKPOLSPMPCNPRIPRTPLW"
 635:        + "PRYQATREUROMRUSRWASAUSLBSYCSDNSWESGPSHNSVNSJMSVKSLESMRSENSOMSURSTP"
 636:        + "SLVSYRSWZTCATCDATFTGOTHATJKTKLTKMTUNTONTMPTURTTOTUVTWNTZAUKRUGAUMI"
 637:        + "USAURYUZBVATVCTVENVGBVIRVNMVUTWLFWSMYEMMYTYUGZAFZMBZARZWE")
 638:       .substring(index, index + 3);
 639:   }
 640: 
 641:   /**
 642:    * Gets the country name suitable for display to the user, formatted
 643:    * for the default locale.  This has the same effect as
 644:    * <pre>
 645:    * getDisplayLanguage(Locale.getDefault());
 646:    * </pre>
 647:    *
 648:    * @return the language name of this locale localized to the default locale,
 649:    *         with the ISO code as backup
 650:    */
 651:   public String getDisplayLanguage()
 652:   {
 653:     return getDisplayLanguage(defaultLocale);
 654:   }
 655: 
 656:   /**
 657:    * <p>
 658:    * Gets the name of the language specified by this locale, in a form suitable
 659:    * for display to the user.  If possible, the display name will be localized
 660:    * to the specified locale.  For example, if the locale instance is
 661:    * <code>Locale.GERMANY</code>, and the specified locale is <code>Locale.UK</code>,
 662:    * the result would be 'German'.  Using the German locale would instead give
 663:    * 'Deutsch'.  If the display name can not be localized to the supplied
 664:    * locale, it will fall back on other output in the following order:
 665:    * </p>
 666:    * <ul>
 667:    * <li>the display name in the default locale</li>
 668:    * <li>the display name in English</li>
 669:    * <li>the ISO code</li>
 670:    * </ul>
 671:    * <p>
 672:    * If the language is unspecified by this locale, then the empty string is
 673:    * returned.
 674:    * </p>
 675:    *
 676:    * @param inLocale the locale to use for formatting the display string.
 677:    * @return the language name of this locale localized to the given locale,
 678:    *         with the default locale, English and the ISO code as backups.
 679:    * @throws NullPointerException if the supplied locale is null.
 680:    */
 681:   public String getDisplayLanguage(Locale inLocale)
 682:   {
 683:     if (language.isEmpty())
 684:       return "";
 685:     try
 686:       {
 687:     ResourceBundle res =
 688:           ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
 689:                                    inLocale,
 690:                                    ClassLoader.getSystemClassLoader());
 691: 
 692:         return res.getString("languages." + language);
 693:       }
 694:     catch (MissingResourceException e)
 695:       {
 696:     /* This means runtime support for the locale
 697:      * is not available, so we check providers. */
 698:       }
 699:     for (LocaleNameProvider p :
 700:        ServiceLoader.load(LocaleNameProvider.class))
 701:       {
 702:     for (Locale loc : p.getAvailableLocales())
 703:       {
 704:         if (loc.equals(inLocale))
 705:           {
 706:         String locLang = p.getDisplayLanguage(language,
 707:                               inLocale);
 708:         if (locLang != null)
 709:           return locLang;
 710:         break;
 711:           }
 712:       }
 713:       }
 714:     if (inLocale.equals(Locale.ROOT)) // Base case
 715:       return language;
 716:     return getDisplayLanguage(LocaleHelper.getFallbackLocale(inLocale));
 717:   }
 718: 
 719:   /**
 720:    * Returns the country name of this locale localized to the
 721:    * default locale. If the localized is not found, the ISO code
 722:    * is returned. This has the same effect as
 723:    * <pre>
 724:    * getDisplayCountry(Locale.getDefault());
 725:    * </pre>
 726:    *
 727:    * @return the country name of this locale localized to the given locale,
 728:    *         with the ISO code as backup
 729:    */
 730:   public String getDisplayCountry()
 731:   {
 732:     return getDisplayCountry(defaultLocale);
 733:   }
 734: 
 735:   /**
 736:    * <p>
 737:    * Gets the name of the country specified by this locale, in a form suitable
 738:    * for display to the user.  If possible, the display name will be localized
 739:    * to the specified locale.  For example, if the locale instance is
 740:    * <code>Locale.GERMANY</code>, and the specified locale is <code>Locale.UK</code>,
 741:    * the result would be 'Germany'.  Using the German locale would instead give
 742:    * 'Deutschland'.  If the display name can not be localized to the supplied
 743:    * locale, it will fall back on other output in the following order:
 744:    * </p>
 745:    * <ul>
 746:    * <li>the display name in the default locale</li>
 747:    * <li>the display name in English</li>
 748:    * <li>the ISO code</li>
 749:    * </ul>
 750:    * <p>
 751:    * If the country is unspecified by this locale, then the empty string is
 752:    * returned.
 753:    * </p>
 754:    *
 755:    * @param inLocale the locale to use for formatting the display string.
 756:    * @return the country name of this locale localized to the given locale,
 757:    *         with the default locale, English and the ISO code as backups.
 758:    * @throws NullPointerException if the supplied locale is null.
 759:    */
 760:   public String getDisplayCountry(Locale inLocale)
 761:   {
 762:     if (country.isEmpty())
 763:       return "";
 764:     try
 765:       {
 766:         ResourceBundle res =
 767:           ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
 768:                                    inLocale,
 769:                                    ClassLoader.getSystemClassLoader());
 770:     
 771:         return res.getString("territories." + country);
 772:       }
 773:     catch (MissingResourceException e)
 774:       {
 775:     /* This means runtime support for the locale
 776:      * is not available, so we check providers. */
 777:       }
 778:     for (LocaleNameProvider p :
 779:        ServiceLoader.load(LocaleNameProvider.class))
 780:       {
 781:     for (Locale loc : p.getAvailableLocales())
 782:       {
 783:         if (loc.equals(inLocale))
 784:           {
 785:         String locCountry = p.getDisplayCountry(country,
 786:                             inLocale);
 787:         if (locCountry != null)
 788:           return locCountry;
 789:         break;
 790:           }
 791:       }
 792:       }
 793:     if (inLocale.equals(Locale.ROOT)) // Base case
 794:       return country;
 795:     return getDisplayCountry(LocaleHelper.getFallbackLocale(inLocale));
 796:   }
 797: 
 798:   /**
 799:    * Returns the variant name of this locale localized to the
 800:    * default locale. If the localized is not found, the variant code
 801:    * itself is returned. This has the same effect as
 802:    * <pre>
 803:    * getDisplayVariant(Locale.getDefault());
 804:    * </pre>
 805:    *
 806:    * @return the variant code of this locale localized to the given locale,
 807:    *         with the ISO code as backup
 808:    */
 809:   public String getDisplayVariant()
 810:   {
 811:     return getDisplayVariant(defaultLocale);
 812:   }
 813: 
 814: 
 815:   /**
 816:    * <p>
 817:    * Gets the name of the variant specified by this locale, in a form suitable
 818:    * for display to the user.  If possible, the display name will be localized
 819:    * to the specified locale.  For example, if the locale instance is a revised
 820:    * variant, and the specified locale is <code>Locale.UK</code>, the result
 821:    * would be 'REVISED'.  Using the German locale would instead give
 822:    * 'Revidiert'.  If the display name can not be localized to the supplied
 823:    * locale, it will fall back on other output in the following order:
 824:    * </p>
 825:    * <ul>
 826:    * <li>the display name in the default locale</li>
 827:    * <li>the display name in English</li>
 828:    * <li>the ISO code</li>
 829:    * </ul>
 830:    * <p>
 831:    * If the variant is unspecified by this locale, then the empty string is
 832:    * returned.
 833:    * </p>
 834:    *
 835:    * @param inLocale the locale to use for formatting the display string.
 836:    * @return the variant name of this locale localized to the given locale,
 837:    *         with the default locale, English and the ISO code as backups.
 838:    * @throws NullPointerException if the supplied locale is null.
 839:    */
 840:   public String getDisplayVariant(Locale inLocale)
 841:   {
 842:     if (variant.isEmpty())
 843:       return "";
 844:     try
 845:       {
 846:         ResourceBundle res =
 847:           ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
 848:                                    inLocale,
 849:                                    ClassLoader.getSystemClassLoader());
 850:     
 851:         return res.getString("variants." + variant);
 852:       }
 853:     catch (MissingResourceException e)
 854:       {
 855:     /* This means runtime support for the locale
 856:      * is not available, so we check providers. */
 857:       }
 858:     for (LocaleNameProvider p :
 859:        ServiceLoader.load(LocaleNameProvider.class))
 860:       {
 861:     for (Locale loc : p.getAvailableLocales())
 862:       {
 863:         if (loc.equals(inLocale))
 864:           {
 865:         String locVar = p.getDisplayVariant(variant,
 866:                             inLocale);
 867:         if (locVar != null)
 868:           return locVar;
 869:         break;
 870:           }
 871:       }
 872:       }
 873:     if (inLocale.equals(Locale.ROOT)) // Base case
 874:       return country;
 875:     return getDisplayVariant(LocaleHelper.getFallbackLocale(inLocale));
 876:   }
 877: 
 878:   /**
 879:    * Gets all local components suitable for display to the user, formatted
 880:    * for the default locale. For the language component, getDisplayLanguage
 881:    * is called. For the country component, getDisplayCountry is called.
 882:    * For the variant set component, getDisplayVariant is called.
 883:    *
 884:    * <p>The returned String will be one of the following forms:<br>
 885:    * <pre>
 886:    * language (country, variant)
 887:    * language (country)
 888:    * language (variant)
 889:    * country (variant)
 890:    * language
 891:    * country
 892:    * variant
 893:    * </pre>
 894:    *
 895:    * @return String version of this locale, suitable for display to the user
 896:    */
 897:   public String getDisplayName()
 898:   {
 899:     return getDisplayName(defaultLocale);
 900:   }
 901: 
 902:   /**
 903:    * Gets all local components suitable for display to the user, formatted
 904:    * for a specified locale. For the language component,
 905:    * getDisplayLanguage(Locale) is called. For the country component,
 906:    * getDisplayCountry(Locale) is called. For the variant set component,
 907:    * getDisplayVariant(Locale) is called.
 908:    *
 909:    * <p>The returned String will be one of the following forms:<br>
 910:    * <pre>
 911:    * language (country, variant)
 912:    * language (country)
 913:    * language (variant)
 914:    * country (variant)
 915:    * language
 916:    * country
 917:    * variant
 918:    * </pre>
 919:    *
 920:    * @param locale locale to use for formatting
 921:    * @return String version of this locale, suitable for display to the user
 922:    */
 923:   public String getDisplayName(Locale locale)
 924:   {
 925:     StringBuffer result = new StringBuffer();
 926:     int count = 0;
 927:     String[] delimiters = {"", " (", ","};
 928:     if (language.length() != 0)
 929:       {
 930:         result.append(delimiters[count++]);
 931:         result.append(getDisplayLanguage(locale));
 932:       }
 933:     if (country.length() != 0)
 934:       {
 935:         result.append(delimiters[count++]);
 936:         result.append(getDisplayCountry(locale));
 937:       }
 938:     if (variant.length() != 0)
 939:       {
 940:         result.append(delimiters[count++]);
 941:         result.append(getDisplayVariant(locale));
 942:       }
 943:     if (count > 1)
 944:       result.append(")");
 945:     return result.toString();
 946:   }
 947: 
 948:   /**
 949:    * Does the same as <code>Object.clone()</code> but does not throw
 950:    * a <code>CloneNotSupportedException</code>. Why anyone would
 951:    * use this method is a secret to me, since this class is immutable.
 952:    *
 953:    * @return the clone
 954:    */
 955:   public Object clone()
 956:   {
 957:     // This class is final, so no need to use native super.clone().
 958:     return new Locale(language, country, variant);
 959:   }
 960: 
 961:   /**
 962:    * Return the hash code for this locale. The hashcode is the logical
 963:    * xor of the hash codes of the language, the country and the variant.
 964:    * The hash code is precomputed, since <code>Locale</code>s are often
 965:    * used in hash tables.
 966:    *
 967:    * @return the hashcode
 968:    */
 969:   public int hashCode()
 970:   {
 971:     return hashcode;
 972:   }
 973: 
 974:   /**
 975:    * Compares two locales. To be equal, obj must be a Locale with the same
 976:    * language, country, and variant code.
 977:    *
 978:    * @param obj the other locale
 979:    * @return true if obj is equal to this
 980:    */
 981:   public boolean equals(Object obj)
 982:   {
 983:     if (this == obj)
 984:       return true;
 985:     if (! (obj instanceof Locale))
 986:       return false;
 987:     Locale l = (Locale) obj;
 988: 
 989:     return (language == l.language 
 990:             && country == l.country 
 991:             && variant == l.variant);
 992:   }
 993: 
 994:   /**
 995:    * Write the locale to an object stream.
 996:    *
 997:    * @param s the stream to write to
 998:    * @throws IOException if the write fails
 999:    * @serialData The first three fields are Strings representing language,
1000:    *             country, and variant. The fourth field is a placeholder for 
1001:    *             the cached hashcode, but this is always written as -1, and 
1002:    *             recomputed when reading it back.
1003:    */
1004:   private void writeObject(ObjectOutputStream s)
1005:     throws IOException
1006:   {
1007:     ObjectOutputStream.PutField fields = s.putFields();
1008:     fields.put("hashcode", -1);
1009:     s.defaultWriteObject();
1010:   }
1011: 
1012:   /**
1013:    * Reads a locale from the input stream.
1014:    *
1015:    * @param s the stream to read from
1016:    * @throws IOException if reading fails
1017:    * @throws ClassNotFoundException if reading fails
1018:    * @serialData the hashCode is always invalid and must be recomputed
1019:    */
1020:   private void readObject(ObjectInputStream s)
1021:     throws IOException, ClassNotFoundException
1022:   {
1023:     s.defaultReadObject();
1024:     hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode();
1025:   }
1026: } // class Locale