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