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 String language;
 182: 
 183:   /**
 184:    * The country code, as returned by getCountry().
 185:    *
 186:    * @serial the country, possibly ""
 187:    */
 188:   private String country;
 189: 
 190:   /**
 191:    * The variant code, as returned by getVariant().
 192:    *
 193:    * @serial the variant, possibly ""
 194:    */
 195:   private 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).intern();
 328:         country = country.toUpperCase().intern();
 329:         variant = variant.intern();
 330:       }
 331:     this.language = language;
 332:     this.country = country;
 333:     this.variant = variant;
 334:     hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode();
 335:   }
 336: 
 337:   /**
 338:    * Creates a new locale for the given language and country.
 339:    *
 340:    * @param language lowercase two-letter ISO-639 A2 language code
 341:    * @param country uppercase two-letter ISO-3166 A2 country code
 342:    * @throws NullPointerException if either argument is null
 343:    */
 344:   public Locale(String language, String country)
 345:   {
 346:     this(language, country, "");
 347:   }
 348: 
 349:   /**
 350:    * Creates a new locale for a language.
 351:    *
 352:    * @param language lowercase two-letter ISO-639 A2 language code
 353:    * @throws NullPointerException if either argument is null
 354:    * @since 1.4
 355:    */
 356:   public Locale(String language)
 357:   {
 358:     this(language, "", "");
 359:   }
 360: 
 361:   /**
 362:    * Returns the default Locale. The default locale is generally once set
 363:    * on start up and then never changed. Normally you should use this locale
 364:    * for everywhere you need a locale. The initial setting matches the
 365:    * default locale, the user has chosen.
 366:    *
 367:    * @return the default locale for this virtual machine
 368:    */
 369:   public static Locale getDefault()
 370:   {
 371:     return defaultLocale;
 372:   }
 373: 
 374:   /**
 375:    * Changes the default locale. Normally only called on program start up.
 376:    * Note that this doesn't change the locale for other programs. This has
 377:    * a security check,
 378:    * <code>PropertyPermission("user.language", "write")</code>, because of
 379:    * its potential impact to running code.
 380:    *
 381:    * @param newLocale the new default locale
 382:    * @throws NullPointerException if newLocale is null
 383:    * @throws SecurityException if permission is denied
 384:    */
 385:   public static void setDefault(Locale newLocale)
 386:   {
 387:     if (newLocale == null)
 388:       throw new NullPointerException();
 389:     SecurityManager sm = System.getSecurityManager();
 390:     if (sm != null)
 391:       sm.checkPermission(new PropertyPermission("user.language", "write"));
 392:     defaultLocale = newLocale;
 393:   }
 394: 
 395:   /**
 396:    * Returns the list of available locales.
 397:    *
 398:    * @return the installed locales
 399:    */
 400:   public static synchronized Locale[] getAvailableLocales()
 401:   {
 402:     if (availableLocales == null)
 403:       {
 404:         int len = LocaleHelper.getLocaleCount();
 405:         availableLocales = new Locale[len];
 406: 
 407:         for (int i = 0; i < len; i++)
 408:           {
 409:             String language;
 410:             String country = "";
 411:             String variant = "";
 412:             String name = LocaleHelper.getLocaleName(i);
 413: 
 414:             language = name.substring(0, 2);
 415: 
 416:             if (name.length() > 2)
 417:               country = name.substring(3);
 418: 
 419:         int index = country.indexOf("_");
 420:         if (index > 0)
 421:           {
 422:         variant = country.substring(index + 1);
 423:         country = country.substring(0, index - 1);
 424:           }
 425: 
 426:             availableLocales[i] = getLocale(language, country, variant);
 427:           }
 428:       }
 429:     
 430:     return (Locale[]) availableLocales.clone();
 431:   }
 432: 
 433:   /**
 434:    * Returns a list of all 2-letter uppercase country codes as defined
 435:    * in ISO 3166.
 436:    *
 437:    * @return a list of acceptable country codes
 438:    */
 439:   public static String[] getISOCountries()
 440:   {
 441:     if (countryCache == null)
 442:       {
 443:     countryCache = getISOStrings("territories");
 444:       }
 445: 
 446:     return (String[]) countryCache.clone();
 447:   }
 448: 
 449:   /**
 450:    * Returns a list of all 2-letter lowercase language codes as defined
 451:    * in ISO 639 (both old and new variant).
 452:    *
 453:    * @return a list of acceptable language codes
 454:    */
 455:   public static String[] getISOLanguages()
 456:   {
 457:     if (languageCache == null)
 458:       {
 459:     languageCache = getISOStrings("languages");
 460:       }
 461:     return (String[]) languageCache.clone();
 462:   }
 463: 
 464:   /**
 465:    * Returns the set of keys from the specified resource hashtable, filtered
 466:    * so that only two letter strings are returned.
 467:    *
 468:    * @param tableName the name of the table from which to retrieve the keys.
 469:    * @return an array of two-letter strings.
 470:    */
 471:   private static String[] getISOStrings(String tableName)
 472:   {
 473:     int count = 0;
 474:     ResourceBundle bundle =
 475:       ResourceBundle.getBundle("gnu.java.locale.LocaleInformation");
 476:     Enumeration e = bundle.getKeys();
 477:     ArrayList tempList = new ArrayList();
 478: 
 479:     while (e.hasMoreElements())
 480:       {
 481:     String key = (String) e.nextElement();
 482:     
 483:     if (key.startsWith(tableName + "."))
 484:       {
 485:         String str = key.substring(tableName.length() + 1);
 486: 
 487:         if (str.length() == 2
 488:         && Character.isLetter(str.charAt(0))
 489:         && Character.isLetter(str.charAt(1)))
 490:           {
 491:         tempList.add(str);
 492:         ++count;
 493:           }
 494:       }
 495:       }
 496: 
 497:     String[] strings = new String[count];
 498:     
 499:     for (int a = 0; a < count; ++a)
 500:       strings[a] = (String) tempList.get(a);
 501:     
 502:     return strings;
 503:   }
 504: 
 505:   /**
 506:    * Returns the language code of this locale. Some language codes have changed
 507:    * as ISO 639 has evolved; this returns the old name, even if you built
 508:    * the locale with the new one.
 509:    *
 510:    * @return language code portion of this locale, or an empty String
 511:    */
 512:   public String getLanguage()
 513:   {
 514:     return language;
 515:   }
 516: 
 517:   /**
 518:    * Returns the country code of this locale.
 519:    *
 520:    * @return country code portion of this locale, or an empty String
 521:    */
 522:   public String getCountry()
 523:   {
 524:     return country;
 525:   }
 526: 
 527:   /**
 528:    * Returns the variant code of this locale.
 529:    *
 530:    * @return the variant code portion of this locale, or an empty String
 531:    */
 532:   public String getVariant()
 533:   {
 534:     return variant;
 535:   }
 536: 
 537:   /**
 538:    * Gets the string representation of the current locale. This consists of
 539:    * the language, the country, and the variant, separated by an underscore.
 540:    * The variant is listed only if there is a language or country. Examples:
 541:    * "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC".
 542:    *
 543:    * @return the string representation of this Locale
 544:    * @see #getDisplayName()
 545:    */
 546:   public String toString()
 547:   {
 548:     if (language.length() == 0 && country.length() == 0)
 549:       return "";
 550:     else if (country.length() == 0 && variant.length() == 0)
 551:       return language;
 552:     StringBuffer result = new StringBuffer(language);
 553:     result.append('_').append(country);
 554:     if (variant.length() != 0)
 555:       result.append('_').append(variant);
 556:     return result.toString();
 557:   }
 558: 
 559:   /**
 560:    * Returns the three-letter ISO language abbrevation of this locale.
 561:    *
 562:    * @throws MissingResourceException if the three-letter code is not known
 563:    */
 564:   public String getISO3Language()
 565:   {
 566:     // We know all strings are interned so we can use '==' for better performance.
 567:     if (language == "")
 568:       return "";
 569:     int index
 570:       = ("aa,ab,af,am,ar,as,ay,az,ba,be,bg,bh,bi,bn,bo,br,ca,co,cs,cy,da,"
 571:          + "de,dz,el,en,eo,es,et,eu,fa,fi,fj,fo,fr,fy,ga,gd,gl,gn,gu,ha,iw,"
 572:          + "hi,hr,hu,hy,ia,in,ie,ik,in,is,it,iu,iw,ja,ji,jw,ka,kk,kl,km,kn,"
 573:          + "ko,ks,ku,ky,la,ln,lo,lt,lv,mg,mi,mk,ml,mn,mo,mr,ms,mt,my,na,ne,"
 574:          + "nl,no,oc,om,or,pa,pl,ps,pt,qu,rm,rn,ro,ru,rw,sa,sd,sg,sh,si,sk,"
 575:          + "sl,sm,sn,so,sq,sr,ss,st,su,sv,sw,ta,te,tg,th,ti,tk,tl,tn,to,tr,"
 576:          + "ts,tt,tw,ug,uk,ur,uz,vi,vo,wo,xh,ji,yo,za,zh,zu")
 577:       .indexOf(language);
 578: 
 579:     if (index % 3 != 0 || language.length() != 2)
 580:       throw new MissingResourceException
 581:         ("Can't find ISO3 language for " + language,
 582:          "java.util.Locale", language);
 583: 
 584:     // Don't read this aloud. These are the three letter language codes.
 585:     return
 586:       ("aarabkaframharaasmaymazebakbelbulbihbisbenbodbrecatcoscescymdandeu"
 587:        + "dzoellengepospaesteusfasfinfijfaofrafrygaigdhglggrngujhauhebhinhrv"
 588:        + "hunhyeinaindileipkindislitaikuhebjpnyidjawkatkazkalkhmkankorkaskur"
 589:        + "kirlatlinlaolitlavmlgmrimkdmalmonmolmarmsamltmyanaunepnldnorociorm"
 590:        + "oripanpolpusporquerohrunronruskinsansndsagsrpsinslkslvsmosnasomsqi"
 591:        + "srpsswsotsunsweswatamteltgkthatirtuktgltsntonturtsotattwiuigukrurd"
 592:        + "uzbvievolwolxhoyidyorzhazhozul")
 593:       .substring(index, index + 3);
 594:   }
 595: 
 596:   /**
 597:    * Returns the three-letter ISO country abbrevation of the locale.
 598:    *
 599:    * @throws MissingResourceException if the three-letter code is not known
 600:    */
 601:   public String getISO3Country()
 602:   {
 603:     // We know all strings are interned so we can use '==' for better performance.
 604:     if (country == "")
 605:       return "";
 606:     int index
 607:       = ("AD,AE,AF,AG,AI,AL,AM,AN,AO,AQ,AR,AS,AT,AU,AW,AZ,BA,BB,BD,BE,BF,"
 608:          + "BG,BH,BI,BJ,BM,BN,BO,BR,BS,BT,BV,BW,BY,BZ,CA,CC,CF,CG,CH,CI,CK,"
 609:          + "CL,CM,CN,CO,CR,CU,CV,CX,CY,CZ,DE,DJ,DK,DM,DO,DZ,EC,EE,EG,EH,ER,"
 610:          + "ES,ET,FI,FJ,FK,FM,FO,FR,FX,GA,GB,GD,GE,GF,GH,GI,GL,GM,GN,GP,GQ,"
 611:          + "GR,GS,GT,GU,GW,GY,HK,HM,HN,HR,HT,HU,ID,IE,IL,IN,IO,IQ,IR,IS,IT,"
 612:          + "JM,JO,JP,KE,KG,KH,KI,KM,KN,KP,KR,KW,KY,KZ,LA,LB,LC,LI,LK,LR,LS,"
 613:          + "LT,LU,LV,LY,MA,MC,MD,MG,MH,MK,ML,MM,MN,MO,MP,MQ,MR,MS,MT,MU,MV,"
 614:          + "MW,MX,MY,MZ,NA,NC,NE,NF,NG,NI,NL,NO,NP,NR,NU,NZ,OM,PA,PE,PF,PG,"
 615:          + "PH,PK,PL,PM,PN,PR,PT,PW,PY,QA,RE,RO,RU,RW,SA,SB,SC,SD,SE,SG,SH,"
 616:          + "SI,SJ,SK,SL,SM,SN,SO,SR,ST,SV,SY,SZ,TC,TD,TF,TG,TH,TJ,TK,TM,TN,"
 617:          + "TO,TP,TR,TT,TV,TW,TZ,UA,UG,UM,US,UY,UZ,VA,VC,VE,VG,VI,VN,VU,WF,"
 618:          + "WS,YE,YT,YU,ZA,ZM,ZR,ZW")
 619:       .indexOf(country);
 620: 
 621:     if (index % 3 != 0 || country.length() != 2)
 622:       throw new MissingResourceException
 623:         ("Can't find ISO3 country for " + country,
 624:          "java.util.Locale", country);
 625: 
 626:     // Don't read this aloud. These are the three letter country codes.
 627:     return
 628:       ("ANDAREAFGATGAIAALBARMANTAGOATAARGASMAUTAUSABWAZEBIHBRBBGDBELBFABGR"
 629:        + "BHRBDIBENBMUBRNBOLBRABHSBTNBVTBWABLRBLZCANCCKCAFCOGCHECIVCOKCHLCMR"
 630:        + "CHNCOLCRICUBCPVCXRCYPCZEDEUDJIDNKDMADOMDZAECUESTEGYESHERIESPETHFIN"
 631:        + "FJIFLKFSMFROFRAFXXGABGBRGRDGEOGUFGHAGIBGRLGMBGINGLPGNQGRCSGSGTMGUM"
 632:        + "GNBGUYHKGHMDHNDHRVHTIHUNIDNIRLISRINDIOTIRQIRNISLITAJAMJORJPNKENKGZ"
 633:        + "KHMKIRCOMKNAPRKKORKWTCYMKAZLAOLBNLCALIELKALBRLSOLTULUXLVALBYMARMCO"
 634:        + "MDAMDGMHLMKDMLIMMRMNGMACMNPMTQMRTMSRMLTMUSMDVMWIMEXMYSMOZNAMNCLNER"
 635:        + "NFKNGANICNLDNORNPLNRUNIUNZLOMNPANPERPYFPNGPHLPAKPOLSPMPCNPRIPRTPLW"
 636:        + "PRYQATREUROMRUSRWASAUSLBSYCSDNSWESGPSHNSVNSJMSVKSLESMRSENSOMSURSTP"
 637:        + "SLVSYRSWZTCATCDATFTGOTHATJKTKLTKMTUNTONTMPTURTTOTUVTWNTZAUKRUGAUMI"
 638:        + "USAURYUZBVATVCTVENVGBVIRVNMVUTWLFWSMYEMMYTYUGZAFZMBZARZWE")
 639:       .substring(index, index + 3);
 640:   }
 641: 
 642:   /**
 643:    * Gets the country name suitable for display to the user, formatted
 644:    * for the default locale.  This has the same effect as
 645:    * <pre>
 646:    * getDisplayLanguage(Locale.getDefault());
 647:    * </pre>
 648:    *
 649:    * @return the language name of this locale localized to the default locale,
 650:    *         with the ISO code as backup
 651:    */
 652:   public String getDisplayLanguage()
 653:   {
 654:     return getDisplayLanguage(defaultLocale);
 655:   }
 656: 
 657:   /**
 658:    * <p>
 659:    * Gets the name of the language specified by this locale, in a form suitable
 660:    * for display to the user.  If possible, the display name will be localized
 661:    * to the specified locale.  For example, if the locale instance is
 662:    * <code>Locale.GERMANY</code>, and the specified locale is <code>Locale.UK</code>,
 663:    * the result would be 'German'.  Using the German locale would instead give
 664:    * 'Deutsch'.  If the display name can not be localized to the supplied
 665:    * locale, it will fall back on other output in the following order:
 666:    * </p>
 667:    * <ul>
 668:    * <li>the display name in the default locale</li>
 669:    * <li>the display name in English</li>
 670:    * <li>the ISO code</li>
 671:    * </ul>
 672:    * <p>
 673:    * If the language is unspecified by this locale, then the empty string is
 674:    * returned.
 675:    * </p>
 676:    *
 677:    * @param inLocale the locale to use for formatting the display string.
 678:    * @return the language name of this locale localized to the given locale,
 679:    *         with the default locale, English and the ISO code as backups.
 680:    * @throws NullPointerException if the supplied locale is null.
 681:    */
 682:   public String getDisplayLanguage(Locale inLocale)
 683:   {
 684:     if (language.isEmpty())
 685:       return "";
 686:     try
 687:       {
 688:     ResourceBundle res =
 689:           ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
 690:                                    inLocale,
 691:                                    ClassLoader.getSystemClassLoader());
 692: 
 693:         return res.getString("languages." + language);
 694:       }
 695:     catch (MissingResourceException e)
 696:       {
 697:     /* This means runtime support for the locale
 698:      * is not available, so we check providers. */
 699:       }
 700:     for (LocaleNameProvider p :
 701:        ServiceLoader.load(LocaleNameProvider.class))
 702:       {
 703:     for (Locale loc : p.getAvailableLocales())
 704:       {
 705:         if (loc.equals(inLocale))
 706:           {
 707:         String locLang = p.getDisplayLanguage(language,
 708:                               inLocale);
 709:         if (locLang != null)
 710:           return locLang;
 711:         break;
 712:           }
 713:       }
 714:       }
 715:     if (inLocale.equals(Locale.ROOT)) // Base case
 716:       return language;
 717:     return getDisplayLanguage(LocaleHelper.getFallbackLocale(inLocale));
 718:   }
 719: 
 720:   /**
 721:    * Returns the country name of this locale localized to the
 722:    * default locale. If the localized is not found, the ISO code
 723:    * is returned. This has the same effect as
 724:    * <pre>
 725:    * getDisplayCountry(Locale.getDefault());
 726:    * </pre>
 727:    *
 728:    * @return the country name of this locale localized to the given locale,
 729:    *         with the ISO code as backup
 730:    */
 731:   public String getDisplayCountry()
 732:   {
 733:     return getDisplayCountry(defaultLocale);
 734:   }
 735: 
 736:   /**
 737:    * <p>
 738:    * Gets the name of the country specified by this locale, in a form suitable
 739:    * for display to the user.  If possible, the display name will be localized
 740:    * to the specified locale.  For example, if the locale instance is
 741:    * <code>Locale.GERMANY</code>, and the specified locale is <code>Locale.UK</code>,
 742:    * the result would be 'Germany'.  Using the German locale would instead give
 743:    * 'Deutschland'.  If the display name can not be localized to the supplied
 744:    * locale, it will fall back on other output in the following order:
 745:    * </p>
 746:    * <ul>
 747:    * <li>the display name in the default locale</li>
 748:    * <li>the display name in English</li>
 749:    * <li>the ISO code</li>
 750:    * </ul>
 751:    * <p>
 752:    * If the country is unspecified by this locale, then the empty string is
 753:    * returned.
 754:    * </p>
 755:    *
 756:    * @param inLocale the locale to use for formatting the display string.
 757:    * @return the country name of this locale localized to the given locale,
 758:    *         with the default locale, English and the ISO code as backups.
 759:    * @throws NullPointerException if the supplied locale is null.
 760:    */
 761:   public String getDisplayCountry(Locale inLocale)
 762:   {
 763:     if (country.isEmpty())
 764:       return "";
 765:     try
 766:       {
 767:         ResourceBundle res =
 768:           ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
 769:                                    inLocale,
 770:                                    ClassLoader.getSystemClassLoader());
 771:     
 772:         return res.getString("territories." + country);
 773:       }
 774:     catch (MissingResourceException e)
 775:       {
 776:     /* This means runtime support for the locale
 777:      * is not available, so we check providers. */
 778:       }
 779:     for (LocaleNameProvider p :
 780:        ServiceLoader.load(LocaleNameProvider.class))
 781:       {
 782:     for (Locale loc : p.getAvailableLocales())
 783:       {
 784:         if (loc.equals(inLocale))
 785:           {
 786:         String locCountry = p.getDisplayCountry(country,
 787:                             inLocale);
 788:         if (locCountry != null)
 789:           return locCountry;
 790:         break;
 791:           }
 792:       }
 793:       }
 794:     if (inLocale.equals(Locale.ROOT)) // Base case
 795:       return country;
 796:     return getDisplayCountry(LocaleHelper.getFallbackLocale(inLocale));
 797:   }
 798: 
 799:   /**
 800:    * Returns the variant name of this locale localized to the
 801:    * default locale. If the localized is not found, the variant code
 802:    * itself is returned. This has the same effect as
 803:    * <pre>
 804:    * getDisplayVariant(Locale.getDefault());
 805:    * </pre>
 806:    *
 807:    * @return the variant code of this locale localized to the given locale,
 808:    *         with the ISO code as backup
 809:    */
 810:   public String getDisplayVariant()
 811:   {
 812:     return getDisplayVariant(defaultLocale);
 813:   }
 814: 
 815: 
 816:   /**
 817:    * <p>
 818:    * Gets the name of the variant specified by this locale, in a form suitable
 819:    * for display to the user.  If possible, the display name will be localized
 820:    * to the specified locale.  For example, if the locale instance is a revised
 821:    * variant, and the specified locale is <code>Locale.UK</code>, the result
 822:    * would be 'REVISED'.  Using the German locale would instead give
 823:    * 'Revidiert'.  If the display name can not be localized to the supplied
 824:    * locale, it will fall back on other output in the following order:
 825:    * </p>
 826:    * <ul>
 827:    * <li>the display name in the default locale</li>
 828:    * <li>the display name in English</li>
 829:    * <li>the ISO code</li>
 830:    * </ul>
 831:    * <p>
 832:    * If the variant is unspecified by this locale, then the empty string is
 833:    * returned.
 834:    * </p>
 835:    *
 836:    * @param inLocale the locale to use for formatting the display string.
 837:    * @return the variant name of this locale localized to the given locale,
 838:    *         with the default locale, English and the ISO code as backups.
 839:    * @throws NullPointerException if the supplied locale is null.
 840:    */
 841:   public String getDisplayVariant(Locale inLocale)
 842:   {
 843:     if (variant.isEmpty())
 844:       return "";
 845:     try
 846:       {
 847:         ResourceBundle res =
 848:           ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
 849:                                    inLocale,
 850:                                    ClassLoader.getSystemClassLoader());
 851:     
 852:         return res.getString("variants." + variant);
 853:       }
 854:     catch (MissingResourceException e)
 855:       {
 856:     /* This means runtime support for the locale
 857:      * is not available, so we check providers. */
 858:       }
 859:     for (LocaleNameProvider p :
 860:        ServiceLoader.load(LocaleNameProvider.class))
 861:       {
 862:     for (Locale loc : p.getAvailableLocales())
 863:       {
 864:         if (loc.equals(inLocale))
 865:           {
 866:         String locVar = p.getDisplayVariant(variant,
 867:                             inLocale);
 868:         if (locVar != null)
 869:           return locVar;
 870:         break;
 871:           }
 872:       }
 873:       }
 874:     if (inLocale.equals(Locale.ROOT)) // Base case
 875:       return country;
 876:     return getDisplayVariant(LocaleHelper.getFallbackLocale(inLocale));
 877:   }
 878: 
 879:   /**
 880:    * Gets all local components suitable for display to the user, formatted
 881:    * for the default locale. For the language component, getDisplayLanguage
 882:    * is called. For the country component, getDisplayCountry is called.
 883:    * For the variant set component, getDisplayVariant is called.
 884:    *
 885:    * <p>The returned String will be one of the following forms:<br>
 886:    * <pre>
 887:    * language (country, variant)
 888:    * language (country)
 889:    * language (variant)
 890:    * country (variant)
 891:    * language
 892:    * country
 893:    * variant
 894:    * </pre>
 895:    *
 896:    * @return String version of this locale, suitable for display to the user
 897:    */
 898:   public String getDisplayName()
 899:   {
 900:     return getDisplayName(defaultLocale);
 901:   }
 902: 
 903:   /**
 904:    * Gets all local components suitable for display to the user, formatted
 905:    * for a specified locale. For the language component,
 906:    * getDisplayLanguage(Locale) is called. For the country component,
 907:    * getDisplayCountry(Locale) is called. For the variant set component,
 908:    * getDisplayVariant(Locale) is called.
 909:    *
 910:    * <p>The returned String will be one of the following forms:<br>
 911:    * <pre>
 912:    * language (country, variant)
 913:    * language (country)
 914:    * language (variant)
 915:    * country (variant)
 916:    * language
 917:    * country
 918:    * variant
 919:    * </pre>
 920:    *
 921:    * @param locale locale to use for formatting
 922:    * @return String version of this locale, suitable for display to the user
 923:    */
 924:   public String getDisplayName(Locale locale)
 925:   {
 926:     StringBuffer result = new StringBuffer();
 927:     int count = 0;
 928:     String[] delimiters = {"", " (", ","};
 929:     if (language.length() != 0)
 930:       {
 931:         result.append(delimiters[count++]);
 932:         result.append(getDisplayLanguage(locale));
 933:       }
 934:     if (country.length() != 0)
 935:       {
 936:         result.append(delimiters[count++]);
 937:         result.append(getDisplayCountry(locale));
 938:       }
 939:     if (variant.length() != 0)
 940:       {
 941:         result.append(delimiters[count++]);
 942:         result.append(getDisplayVariant(locale));
 943:       }
 944:     if (count > 1)
 945:       result.append(")");
 946:     return result.toString();
 947:   }
 948: 
 949:   /**
 950:    * Does the same as <code>Object.clone()</code> but does not throw
 951:    * a <code>CloneNotSupportedException</code>. Why anyone would
 952:    * use this method is a secret to me, since this class is immutable.
 953:    *
 954:    * @return the clone
 955:    */
 956:   public Object clone()
 957:   {
 958:     // This class is final, so no need to use native super.clone().
 959:     return new Locale(language, country, variant);
 960:   }
 961: 
 962:   /**
 963:    * Return the hash code for this locale. The hashcode is the logical
 964:    * xor of the hash codes of the language, the country and the variant.
 965:    * The hash code is precomputed, since <code>Locale</code>s are often
 966:    * used in hash tables.
 967:    *
 968:    * @return the hashcode
 969:    */
 970:   public int hashCode()
 971:   {
 972:     return hashcode;
 973:   }
 974: 
 975:   /**
 976:    * Compares two locales. To be equal, obj must be a Locale with the same
 977:    * language, country, and variant code.
 978:    *
 979:    * @param obj the other locale
 980:    * @return true if obj is equal to this
 981:    */
 982:   public boolean equals(Object obj)
 983:   {
 984:     if (this == obj)
 985:       return true;
 986:     if (! (obj instanceof Locale))
 987:       return false;
 988:     Locale l = (Locale) obj;
 989: 
 990:     return (language == l.language 
 991:             && country == l.country 
 992:             && variant == l.variant);
 993:   }
 994: 
 995:   /**
 996:    * Write the locale to an object stream.
 997:    *
 998:    * @param s the stream to write to
 999:    * @throws IOException if the write fails
1000:    * @serialData The first three fields are Strings representing language,
1001:    *             country, and variant. The fourth field is a placeholder for 
1002:    *             the cached hashcode, but this is always written as -1, and 
1003:    *             recomputed when reading it back.
1004:    */
1005:   private void writeObject(ObjectOutputStream s)
1006:     throws IOException
1007:   {
1008:     ObjectOutputStream.PutField fields = s.putFields();
1009:     fields.put("hashcode", -1);
1010:     s.defaultWriteObject();
1011:   }
1012: 
1013:   /**
1014:    * Reads a locale from the input stream.
1015:    *
1016:    * @param s the stream to read from
1017:    * @throws IOException if reading fails
1018:    * @throws ClassNotFoundException if reading fails
1019:    * @serialData the hashCode is always invalid and must be recomputed
1020:    */
1021:   private void readObject(ObjectInputStream s)
1022:     throws IOException, ClassNotFoundException
1023:   {
1024:     s.defaultReadObject();
1025:     language = language.intern();
1026:     country = country.intern();
1027:     variant = variant.intern();
1028:     hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode();
1029:   }
1030: } // class Locale