Source for java.util.Locale

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