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