001    /* Locale.java -- i18n locales
002       Copyright (C) 1998, 1999, 2001, 2002, 2005, 2006  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package java.util;
040    
041    import gnu.classpath.SystemProperties;
042    
043    import gnu.java.lang.CPStringBuilder;
044    
045    import gnu.java.locale.LocaleHelper;
046    
047    import java.io.IOException;
048    import java.io.ObjectInputStream;
049    import java.io.ObjectOutputStream;
050    import java.io.Serializable;
051    
052    import java.util.spi.LocaleNameProvider;
053    
054    /**
055     * Locales represent a specific country and culture. Classes which can be
056     * passed a Locale object tailor their information for a given locale. For
057     * instance, currency number formatting is handled differently for the USA
058     * and France.
059     *
060     * <p>Locales are made up of a language code, a country code, and an optional
061     * set of variant strings. Language codes are represented by
062     * <a href="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt">
063     * ISO 639:1988</a> w/ additions from ISO 639/RA Newsletter No. 1/1989
064     * and a decision of the Advisory Committee of ISO/TC39 on August 8, 1997.
065     *
066     * <p>Country codes are represented by
067     * <a href="http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html">
068     * ISO 3166</a>. Variant strings are vendor and browser specific. Standard
069     * variant strings include "POSIX" for POSIX, "WIN" for MS-Windows, and
070     * "MAC" for Macintosh. When there is more than one variant string, they must
071     * be separated by an underscore (U+005F).
072     *
073     * <p>The default locale is determined by the values of the system properties
074     * user.language, user.country (or user.region), and user.variant, defaulting
075     * to "en_US". Note that the locale does NOT contain the conversion and
076     * formatting capabilities (for that, use ResourceBundle and java.text).
077     * Rather, it is an immutable tag object for identifying a given locale, which
078     * is referenced by these other classes when they must make locale-dependent
079     * decisions.
080     *
081     * @see ResourceBundle
082     * @see java.text.Format
083     * @see java.text.NumberFormat
084     * @see java.text.Collator
085     * @author Jochen Hoenicke
086     * @author Paul Fisher
087     * @author Eric Blake (ebb9@email.byu.edu)
088     * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
089     * @since 1.1
090     * @status updated to 1.4
091     */
092    public final class Locale implements Serializable, Cloneable
093    {
094      /** Locale which represents the English language. */
095      public static final Locale ENGLISH = getLocale("en");
096    
097      /** Locale which represents the French language. */
098      public static final Locale FRENCH = getLocale("fr");
099    
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