• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package java.util;
19 
20 // BEGIN android-changed
21 // import java.io.File;
22 import java.io.IOException;
23 import java.io.ObjectInputStream;
24 import java.io.ObjectOutputStream;
25 import java.io.ObjectStreamField;
26 import java.io.Serializable;
27 import java.security.AccessController;
28 import java.security.PrivilegedAction;
29 // import java.util.zip.ZipEntry;
30 // import java.util.zip.ZipFile;
31 
32 import org.apache.harmony.luni.util.PriviAction;
33 import org.apache.harmony.luni.util.Util;
34 
35 import com.ibm.icu4jni.util.Resources;
36 // END android-changed
37 
38 /**
39  * {@code Locale} represents a language/country/variant combination. It is an identifier
40  * which dictates particular conventions for the presentation of information.
41  * The language codes are two letter lowercase codes as defined by ISO-639. The
42  * country codes are three letter uppercase codes as defined by ISO-3166. The
43  * variant codes are unspecified.
44  *
45  * @see ResourceBundle
46  */
47 public final class Locale implements Cloneable, Serializable {
48 
49     private static final long serialVersionUID = 9149081749638150636L;
50 
51     private static volatile Locale[] availableLocales;
52 
53     // Initialize a default which is used during static
54     // initialization of the default for the platform.
55     private static Locale defaultLocale = new Locale();
56 
57     /**
58      * Locale constant for en_CA.
59      */
60     public static final Locale CANADA = new Locale("en", "CA"); //$NON-NLS-1$ //$NON-NLS-2$
61 
62     /**
63      * Locale constant for fr_CA.
64      */
65     public static final Locale CANADA_FRENCH = new Locale("fr", "CA"); //$NON-NLS-1$ //$NON-NLS-2$
66 
67     /**
68      * Locale constant for zh_CN.
69      */
70     public static final Locale CHINA = new Locale("zh", "CN"); //$NON-NLS-1$ //$NON-NLS-2$
71 
72     /**
73      * Locale constant for zh.
74      */
75     public static final Locale CHINESE = new Locale("zh", ""); //$NON-NLS-1$//$NON-NLS-2$
76 
77     /**
78      * Locale constant for en.
79      */
80     public static final Locale ENGLISH = new Locale("en", ""); //$NON-NLS-1$ //$NON-NLS-2$
81 
82     /**
83      * Locale constant for fr_FR.
84      */
85     public static final Locale FRANCE = new Locale("fr", "FR"); //$NON-NLS-1$//$NON-NLS-2$
86 
87     /**
88      * Locale constant for fr.
89      */
90     public static final Locale FRENCH = new Locale("fr", ""); //$NON-NLS-1$//$NON-NLS-2$
91 
92     /**
93      * Locale constant for de.
94      */
95     public static final Locale GERMAN = new Locale("de", ""); //$NON-NLS-1$ //$NON-NLS-2$
96 
97     /**
98      * Locale constant for de_DE.
99      */
100     public static final Locale GERMANY = new Locale("de", "DE"); //$NON-NLS-1$ //$NON-NLS-2$
101 
102     /**
103      * Locale constant for it.
104      */
105     public static final Locale ITALIAN = new Locale("it", ""); //$NON-NLS-1$ //$NON-NLS-2$
106 
107     /**
108      * Locale constant for it_IT.
109      */
110     public static final Locale ITALY = new Locale("it", "IT"); //$NON-NLS-1$ //$NON-NLS-2$
111 
112     /**
113      * Locale constant for ja_JP.
114      */
115     public static final Locale JAPAN = new Locale("ja", "JP"); //$NON-NLS-1$//$NON-NLS-2$
116 
117     /**
118      * Locale constant for ja.
119      */
120     public static final Locale JAPANESE = new Locale("ja", ""); //$NON-NLS-1$//$NON-NLS-2$
121 
122     /**
123      * Locale constant for ko_KR.
124      */
125     public static final Locale KOREA = new Locale("ko", "KR"); //$NON-NLS-1$//$NON-NLS-2$
126 
127     /**
128      * Locale constant for ko.
129      */
130     public static final Locale KOREAN = new Locale("ko", ""); //$NON-NLS-1$//$NON-NLS-2$
131 
132     /**
133      * Locale constant for zh_CN.
134      */
135     public static final Locale PRC = new Locale("zh", "CN"); //$NON-NLS-1$//$NON-NLS-2$
136 
137     /**
138      * Locale constant for zh_CN.
139      */
140     public static final Locale SIMPLIFIED_CHINESE = new Locale("zh", "CN"); //$NON-NLS-1$//$NON-NLS-2$
141 
142     /**
143      * Locale constant for zh_TW.
144      */
145     public static final Locale TAIWAN = new Locale("zh", "TW"); //$NON-NLS-1$ //$NON-NLS-2$
146 
147     /**
148      * Locale constant for zh_TW.
149      */
150     public static final Locale TRADITIONAL_CHINESE = new Locale("zh", "TW"); //$NON-NLS-1$ //$NON-NLS-2$
151 
152     /**
153      * Locale constant for en_GB.
154      */
155     public static final Locale UK = new Locale("en", "GB"); //$NON-NLS-1$ //$NON-NLS-2$
156 
157     /**
158      * Locale constant for en_US.
159      */
160     public static final Locale US = new Locale("en", "US"); //$NON-NLS-1$//$NON-NLS-2$
161 
162     private static final PropertyPermission setLocalePermission = new PropertyPermission(
163             "user.language", "write"); //$NON-NLS-1$//$NON-NLS-2$
164 
165     static {
166         String language = AccessController
167                 .doPrivileged(new PriviAction<String>("user.language", "en")); //$NON-NLS-1$ //$NON-NLS-2$
168         // BEGIN android-changed
169         String region = AccessController.doPrivileged(new PriviAction<String>(
170                 "user.region", "US")); //$NON-NLS-1$ //$NON-NLS-2$
171         // END android-changed
172         String variant = AccessController.doPrivileged(new PriviAction<String>(
173                 "user.variant", "")); //$NON-NLS-1$ //$NON-NLS-2$
174         defaultLocale = new Locale(language, region, variant);
175     }
176 
177     private transient String countryCode;
178     private transient String languageCode;
179     private transient String variantCode;
180 
181     // BEGIN android-removed
182     // private transient ULocale uLocale;
183     // END android-removed
184 
185 	/**
186 	 * Constructs a default which is used during static initialization of the
187 	 * default for the platform.
188 	 */
Locale()189 	private Locale() {
190 		languageCode = "en"; //$NON-NLS-1$
191 		countryCode = "US"; //$NON-NLS-1$
192 		variantCode = ""; //$NON-NLS-1$
193 	}
194 
195     /**
196      * Constructs a new {@code Locale} using the specified language.
197      *
198      * @param language
199      *            the language this {@code Locale} represents.
200      */
Locale(String language)201     public Locale(String language) {
202         this(language, "", ""); //$NON-NLS-1$//$NON-NLS-2$
203     }
204 
205     /**
206      * Constructs a new {@code Locale} using the specified language and country codes.
207      *
208      * @param language
209      *            the language this {@code Locale} represents.
210      * @param country
211      *            the country this {@code Locale} represents.
212      */
Locale(String language, String country)213     public Locale(String language, String country) {
214         this(language, country, ""); //$NON-NLS-1$
215     }
216 
217     /**
218      * Constructs a new {@code Locale} using the specified language, country, and
219      * variant codes.
220      *
221      * @param language
222      *            the language this {@code Locale} represents.
223      * @param country
224      *            the country this {@code Locale} represents.
225      * @param variant
226      *            the variant this {@code Locale} represents.
227      * @throws NullPointerException
228      *             if {@code language}, {@code country}, or
229      *             {@code variant} is {@code null}.
230      */
Locale(String language, String country, String variant)231     public Locale(String language, String country, String variant) {
232         if (language == null || country == null || variant == null) {
233             throw new NullPointerException();
234         }
235         if(language.length() == 0 && country.length() == 0){
236             languageCode = "";
237             countryCode = "";
238             variantCode = variant;
239             return;
240         }
241         // BEGIN android-changed
242         // this.uLocale = new ULocale(language, country, variant);
243         // languageCode = uLocale.getLanguage();
244         languageCode = Util.toASCIILowerCase(language);
245         // END android-changed
246         // Map new language codes to the obsolete language
247         // codes so the correct resource bundles will be used.
248         if (languageCode.equals("he")) {//$NON-NLS-1$
249             languageCode = "iw"; //$NON-NLS-1$
250         } else if (languageCode.equals("id")) {//$NON-NLS-1$
251             languageCode = "in"; //$NON-NLS-1$
252         } else if (languageCode.equals("yi")) {//$NON-NLS-1$
253             languageCode = "ji"; //$NON-NLS-1$
254         }
255 
256         // countryCode is defined in ASCII character set
257         // BEGIN android-changed
258         // countryCode = country.length()!=0?uLocale.getCountry():"";
259         countryCode = Util.toASCIIUpperCase(country);
260         // END android-changed
261 
262         // Work around for be compatible with RI
263         variantCode = variant;
264     }
265 
266     /**
267      * Returns a new {@code Locale} with the same language, country and variant codes as
268      * this {@code Locale}.
269      *
270      * @return a shallow copy of this {@code Locale}.
271      * @see java.lang.Cloneable
272      */
273     @Override
clone()274     public Object clone() {
275         try {
276             return super.clone();
277         } catch (CloneNotSupportedException e) {
278             return null;
279         }
280     }
281 
282     /**
283      * Compares the specified object to this {@code Locale} and returns whether they are
284      * equal. The object must be an instance of {@code Locale} and have the same
285      * language, country and variant.
286      *
287      * @param object
288      *            the object to compare with this object.
289      * @return {@code true} if the specified object is equal to this {@code Locale},
290      *         {@code false} otherwise.
291      * @see #hashCode
292      */
293     @Override
equals(Object object)294     public boolean equals(Object object) {
295         if (object == this) {
296             return true;
297         }
298         if (object instanceof Locale) {
299             Locale o = (Locale) object;
300             return languageCode.equals(o.languageCode)
301                     && countryCode.equals(o.countryCode)
302                     && variantCode.equals(o.variantCode);
303         }
304         return false;
305     }
306 
307     // BEGIN android-removed
308     // static Locale[] find(String prefix) {
309     //     int last = prefix.lastIndexOf('/');
310     //     final String thePackage = prefix.substring(0, last + 1);
311     //     int length = prefix.length();
312     //     final String classPrefix = prefix.substring(last + 1, length);
313     //     Set<String> result = new HashSet<String>();
314     //     StringTokenizer paths = new StringTokenizer(System.getProperty(
315     //             "org.apache.harmony.boot.class.path", ""), System.getProperty( //$NON-NLS-1$ //$NON-NLS-2$
316     //             "path.separator", ";")); //$NON-NLS-1$//$NON-NLS-2$
317     //     while (paths.hasMoreTokens()) {
318     //         String nextToken = paths.nextToken();
319     //         File directory = new File(nextToken);
320     //         if (directory.exists()) {
321     //             if (directory.isDirectory()) {
322     //                 String path;
323     //                 try {
324     //                     path = directory.getCanonicalPath();
325     //                 } catch (IOException e) {
326     //                     continue;
327     //                 }
328     //                 File newDir;
329     //                 if (path.charAt(path.length() - 1) == File.separatorChar) {
330     //                     newDir = new File(path + thePackage);
331     //                 } else {
332     //                     newDir = new File(path + File.separatorChar
333     //                             + thePackage);
334     //                 }
335     //                 if (newDir.isDirectory()) {
336     //                     String[] list = newDir.list();
337     //                     for (int i = 0; i < list.length; i++) {
338     //                         String name = list[i];
339     //                         if (name.startsWith(classPrefix)
340     //                                 && name.endsWith(".class")) { //$NON-NLS-1$
341     //                             result
342     //                                     .add(name.substring(0,
343     //                                             name.length() - 6));
344     //                         }
345     //                     }
346     //                 }
347     //
348     //             } else {
349     //                 // Handle ZIP/JAR files.
350     //                 try {
351     //                     ZipFile zip = new ZipFile(directory);
352     //                     Enumeration<? extends ZipEntry> entries = zip.entries();
353     //                     while (entries.hasMoreElements()) {
354     //                         ZipEntry e = entries.nextElement();
355     //                         String name = e.getName();
356     //                         if (name.startsWith(prefix)
357     //                                 && name.endsWith(".class")) {//$NON-NLS-1$
358     //                             result.add(name.substring(last + 1, name
359     //                                     .length() - 6));
360     //                         }
361     //                     }
362     //                     zip.close();
363     //                 } catch (IOException e) {
364     //                     // Empty
365     //                 }
366     //             }
367     //         }
368     //     }
369     //     Locale[] locales = new Locale[result.size()];
370     //     int i = 0;
371     //     for (String name : result) {
372     //         int index = name.indexOf('_');
373     //         int nextIndex = name.indexOf('_', index + 1);
374     //         if (nextIndex == -1) {
375     //             locales[i++] = new Locale(name.substring(index + 1, name
376     //                     .length()), ""); //$NON-NLS-1$
377     //         } else {
378     //             String language = name.substring(index + 1, nextIndex);
379     //             String variant;
380     //             if ((index = name.indexOf('_', nextIndex + 1)) == -1) {
381     //                 variant = ""; //$NON-NLS-1$
382     //                 index = name.length();
383     //             } else {
384     //                 variant = name.substring(index + 1, name.length());
385     //             }
386     //             String country = name.substring(nextIndex + 1, index);
387     //             locales[i++] = new Locale(language, country, variant);
388     //         }
389     //     }
390     //     return locales;
391     // }
392     // END android-removed
393 
394     // BEGIN android-added
find()395     static Locale[] find() {
396         String[] locales = Resources.getAvailableLocales();
397         ArrayList<Locale> temp = new ArrayList<Locale>();
398         for (int i = 0; i < locales.length; i++) {
399             String s = locales[i];
400             int first = s.indexOf('_');
401             int second = s.indexOf('_', first + 1);
402 
403             if (first == -1) {
404                 // Language only
405                 temp.add(new Locale(s));
406             } else if (second == -1) {
407                 // Language and country
408                 temp.add(new Locale(s.substring(0, first), s.substring(first + 1)));
409             } else {
410                 // Language and country and variant
411                 temp.add(new Locale(s.substring(0, first), s.substring(first + 1, second), s.substring(second + 1)));
412             }
413         }
414         Locale[] result = new Locale[temp.size()];
415         return temp.toArray(result);
416     }
417     // END android-added
418 
419 	/**
420      * Gets the list of installed {@code Locale}. At least a {@code Locale} that is equal to
421      * {@code Locale.US} must be contained in this array.
422      *
423      * @return an array of {@code Locale}s.
424 	 */
getAvailableLocales()425 	public static Locale[] getAvailableLocales() {
426         // BEGIN android-changed
427         // ULocale[] ulocales =  ULocale.getAvailableLocales();
428         // Locale[] locales = new Locale[ulocales.length];
429         // for (int i = 0; i < locales.length; i++) {
430         //     locales[i] = ulocales[i].toLocale();
431         // }
432         // return locales;
433         if (availableLocales == null) {
434             availableLocales = find();
435         }
436         return availableLocales.clone();
437         // END android-changed
438 	}
439 
440     /**
441      * Gets the country code for this {@code Locale} or an empty string of no country
442      * was set.
443      *
444      * @return a country code.
445      */
getCountry()446     public String getCountry() {
447         return countryCode;
448     }
449 
450     /**
451      * Gets the default {@code Locale}.
452      *
453      * @return the default {@code Locale}.
454      */
getDefault()455     public static Locale getDefault() {
456         return defaultLocale;
457     }
458 
459     /**
460      * Gets the full country name in the default {@code Locale} for the country code of
461      * this {@code Locale}. If there is no matching country name, the country code is
462      * returned.
463      *
464      * @return a country name.
465      */
getDisplayCountry()466     public final String getDisplayCountry() {
467         return getDisplayCountry(getDefault());
468     }
469 
470     /**
471      * Gets the full country name in the specified {@code Locale} for the country code
472      * of this {@code Locale}. If there is no matching country name, the country code is
473      * returned.
474      *
475      * @param locale
476      *            the {@code Locale} for which the display name is retrieved.
477      * @return a country name.
478      */
getDisplayCountry(Locale locale)479 	public String getDisplayCountry(Locale locale) {
480         // BEGIN android-changed
481 		// return ULocale.forLocale(this).getDisplayCountry(ULocale.forLocale(locale));
482         if (countryCode.length() == 0) {
483             return countryCode;
484         }
485         try {
486             // First try the specified locale
487             ResourceBundle bundle = getBundle("Country", locale); //$NON-NLS-1$
488             String result = bundle.getString(this.toString());
489             if (result != null) {
490                 return result;
491             }
492             // Now use the default locale
493             if (locale != Locale.getDefault()) {
494                 bundle = getBundle("Country", Locale.getDefault()); //$NON-NLS-1$
495             }
496             return bundle.getString(countryCode);
497         } catch (MissingResourceException e) {
498             return countryCode;
499         }
500         // END android-changed
501 	}
502 
503     /**
504      * Gets the full language name in the default {@code Locale} for the language code
505      * of this {@code Locale}. If there is no matching language name, the language code
506      * is returned.
507      *
508      * @return a language name.
509      */
getDisplayLanguage()510     public final String getDisplayLanguage() {
511         return getDisplayLanguage(getDefault());
512     }
513 
514     /**
515      * Gets the full language name in the specified {@code Locale} for the language code
516      * of this {@code Locale}. If there is no matching language name, the language code
517      * is returned.
518      *
519      * @param locale
520      *            the {@code Locale} for which the display name is retrieved.
521      * @return a language name.
522      */
getDisplayLanguage(Locale locale)523 	public String getDisplayLanguage(Locale locale) {
524         // BEGIN android-changed
525         // return ULocale.forLocale(this).getDisplayLanguage(ULocale.forLocale(locale));
526         if (languageCode.length() == 0) {
527             return languageCode;
528         }
529         try {
530             // First try the specified locale
531             ResourceBundle bundle = getBundle("Language", locale); //$NON-NLS-1$
532             String result = bundle.getString(this.toString());
533             if (result != null) {
534                 return result;
535             }
536             // Now use the default locale
537             if (locale != Locale.getDefault()) {
538                 bundle = getBundle("Language", Locale.getDefault()); //$NON-NLS-1$
539             }
540             return bundle.getString(languageCode);
541         } catch (MissingResourceException e) {
542             return languageCode;
543         }
544         // END android-changed
545 	}
546 
547     /**
548      * Gets the full language, country, and variant names in the default {@code Locale}
549      * for the codes of this {@code Locale}.
550      *
551      * @return a {@code Locale} name.
552      */
getDisplayName()553     public final String getDisplayName() {
554         return getDisplayName(getDefault());
555     }
556 
557     /**
558      * Gets the full language, country, and variant names in the specified
559      * Locale for the codes of this {@code Locale}.
560      *
561      * @param locale
562      *            the {@code Locale} for which the display name is retrieved.
563      * @return a {@code Locale} name.
564      */
getDisplayName(Locale locale)565     public String getDisplayName(Locale locale) {
566         int count = 0;
567         StringBuilder buffer = new StringBuilder();
568         if (languageCode.length() > 0) {
569             buffer.append(getDisplayLanguage(locale));
570             count++;
571         }
572         if (countryCode.length() > 0) {
573             if (count == 1) {
574                 buffer.append(" ("); //$NON-NLS-1$
575             }
576             buffer.append(getDisplayCountry(locale));
577             count++;
578         }
579         if (variantCode.length() > 0) {
580             if (count == 1) {
581                 buffer.append(" ("); //$NON-NLS-1$
582             } else if (count == 2) {
583                 buffer.append(","); //$NON-NLS-1$
584             }
585             buffer.append(getDisplayVariant(locale));
586             count++;
587         }
588         if (count > 1) {
589             buffer.append(")"); //$NON-NLS-1$
590         }
591         return buffer.toString();
592     }
593 
594     /**
595      * Gets the full variant name in the default {@code Locale} for the variant code of
596      * this {@code Locale}. If there is no matching variant name, the variant code is
597      * returned.
598      *
599      * @return a variant name.
600      */
getDisplayVariant()601     public final String getDisplayVariant() {
602         return getDisplayVariant(getDefault());
603     }
604 
605     /**
606      * Gets the full variant name in the specified {@code Locale} for the variant code
607      * of this {@code Locale}. If there is no matching variant name, the variant code is
608      * returned.
609      *
610      * @param locale
611      *            the {@code Locale} for which the display name is retrieved.
612      * @return a variant name.
613      */
getDisplayVariant(Locale locale)614 	public String getDisplayVariant(Locale locale) {
615         // BEGIN android-changed
616         // return ULocale.forLocale(this).getDisplayVariant(ULocale.forLocale(locale));
617         if (variantCode.length() == 0) {
618             return variantCode;
619         }
620         try {
621             // First try the specified locale
622             ResourceBundle bundle = getBundle("Variant", locale); //$NON-NLS-1$
623             String result = bundle.getString(this.toString());
624             if (result != null) {
625                 return result;
626             }
627             // Now use the default locale
628             if (locale != Locale.getDefault()) {
629                 bundle = getBundle("Variant", Locale.getDefault()); //$NON-NLS-1$
630             }
631             return bundle.getString(variantCode);
632         } catch (MissingResourceException e) {
633             return variantCode;
634         }
635         // END android-changed
636 	}
637 
638     /**
639      * Gets the three letter ISO country code which corresponds to the country
640      * code for this {@code Locale}.
641      *
642      * @return a three letter ISO language code.
643      * @throws MissingResourceException
644      *                if there is no matching three letter ISO country code.
645      */
getISO3Country()646 	public String getISO3Country() throws MissingResourceException {
647         // BEGIN android-changed
648         // return ULocale.forLocale(this).getISO3Country();
649         if (countryCode.length() == 0) {
650             return ""; //$NON-NLS-1$
651         }
652         ResourceBundle bundle = getBundle("ISO3Countries", this); //$NON-NLS-1$
653         return bundle.getString(this.toString());
654         // END android-changed
655 	}
656 
657     /**
658      * Gets the three letter ISO language code which corresponds to the language
659      * code for this {@code Locale}.
660      *
661      * @return a three letter ISO language code.
662      * @throws MissingResourceException
663      *                if there is no matching three letter ISO language code.
664      */
getISO3Language()665 	public String getISO3Language() throws MissingResourceException {
666         // BEGIN android-changed
667         // return ULocale.forLocale(this).getISO3Language();
668         if (languageCode.length() == 0) {
669             return ""; //$NON-NLS-1$
670         }
671         ResourceBundle bundle = getBundle("ISO3Languages", this); //$NON-NLS-1$
672         return bundle.getString(this.toString());
673         // END android-changed
674 	}
675 
676     /**
677      * Gets the list of two letter ISO country codes which can be used as the
678      * country code for a {@code Locale}.
679      *
680      * @return an array of strings.
681      */
getISOCountries()682     public static String[] getISOCountries() {
683         // BEGIN android-changed
684         // return ULocale.getISOCountries();
685         return Resources.getISOCountries();
686         // END android-changed
687     }
688 
689     /**
690      * Gets the list of two letter ISO language codes which can be used as the
691      * language code for a {@code Locale}.
692      *
693      * @return an array of strings.
694      */
getISOLanguages()695 	public static String[] getISOLanguages() {
696         // BEGIN android-changed
697         // return ULocale.getISOLanguages();
698         return Resources.getISOLanguages();
699         // END android-changed
700 	}
701 
702     /**
703      * Gets the language code for this {@code Locale} or the empty string of no language
704      * was set.
705      *
706      * @return a language code.
707      */
getLanguage()708     public String getLanguage() {
709         return languageCode;
710     }
711 
712     /**
713      * Gets the variant code for this {@code Locale} or an empty {@code String} of no variant
714      * was set.
715      *
716      * @return a variant code.
717      */
getVariant()718     public String getVariant() {
719         return variantCode;
720     }
721 
722     /**
723      * Returns an integer hash code for the receiver. Objects which are equal
724      * return the same value for this method.
725      *
726      * @return the receiver's hash.
727      * @see #equals
728      */
729     @Override
hashCode()730     public synchronized int hashCode() {
731         return countryCode.hashCode() + languageCode.hashCode()
732                 + variantCode.hashCode();
733     }
734 
735     /**
736      * Sets the default {@code Locale} to the specified {@code Locale}.
737      *
738      * @param locale
739      *            the new default {@code Locale}.
740      * @throws SecurityException
741      *                if there is a {@code SecurityManager} in place which does not allow this
742      *                operation.
743      */
setDefault(Locale locale)744     public synchronized static void setDefault(Locale locale) {
745         if (locale != null) {
746             SecurityManager security = System.getSecurityManager();
747             if (security != null) {
748                 security.checkPermission(setLocalePermission);
749             }
750             defaultLocale = locale;
751         } else {
752             throw new NullPointerException();
753         }
754     }
755 
756     /**
757      * Returns the string representation of this {@code Locale}. It consists of the
758      * language followed by the country and at the end the variant. They are
759      * separated by underscores. If the language is missing the string begins
760      * with an underscore. If the country is missing there are 2 underscores
761      * between the language and the variant. the variant alone canot be defined
762      * without a language and/or a country (in this case this method would
763      * return the empty string).
764      *
765      * Examples: "en", "en_US", "_US", "en__POSIX", "en_US_POSIX"
766      *
767      * @return the string representation of this {@code Locale}.
768      */
769     @Override
toString()770     public final String toString() {
771         StringBuilder result = new StringBuilder();
772         result.append(languageCode);
773         if (countryCode.length() > 0) {
774             result.append('_');
775             result.append(countryCode);
776         }
777         if (variantCode.length() > 0 && result.length() > 0) {
778             if (0 == countryCode.length()) {
779                 result.append("__"); //$NON-NLS-1$
780             } else {
781                 result.append('_');
782             }
783             result.append(variantCode);
784         }
785         return result.toString();
786     }
787 
788     // BEGIN android-added
getBundle(final String clName, final Locale locale)789     static ResourceBundle getBundle(final String clName, final Locale locale) {
790         return AccessController.doPrivileged(new PrivilegedAction<ResourceBundle>() {
791             public ResourceBundle run() {
792                 return ResourceBundle.getBundle("org.apache.harmony.luni.internal.locale." //$NON-NLS-1$
793                         + clName, locale);
794             }
795         });
796     }
797     // END android-added
798 
799     private static final ObjectStreamField[] serialPersistentFields = {
800             new ObjectStreamField("country", String.class), //$NON-NLS-1$
801             new ObjectStreamField("hashcode", Integer.TYPE), //$NON-NLS-1$
802             new ObjectStreamField("language", String.class), //$NON-NLS-1$
803             new ObjectStreamField("variant", String.class) }; //$NON-NLS-1$
804 
805     private void writeObject(ObjectOutputStream stream) throws IOException {
806         ObjectOutputStream.PutField fields = stream.putFields();
807         fields.put("country", countryCode); //$NON-NLS-1$
808         fields.put("hashcode", -1); //$NON-NLS-1$
809         fields.put("language", languageCode); //$NON-NLS-1$
810         fields.put("variant", variantCode); //$NON-NLS-1$
811         stream.writeFields();
812     }
813 
814     private void readObject(ObjectInputStream stream) throws IOException,
815             ClassNotFoundException {
816         ObjectInputStream.GetField fields = stream.readFields();
817         countryCode = (String) fields.get("country", ""); //$NON-NLS-1$//$NON-NLS-2$
818         languageCode = (String) fields.get("language", ""); //$NON-NLS-1$//$NON-NLS-2$
819         variantCode = (String) fields.get("variant", ""); //$NON-NLS-1$//$NON-NLS-2$
820     }
821 }
822