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