1/* GENERATED SOURCE. DO NOT MODIFY. */ 2/* 3 ******************************************************************************* 4 * Copyright (C) 2004-2014, International Business Machines Corporation and 5 * others. All Rights Reserved. 6 ******************************************************************************* 7*/ 8package android.icu.util; 9 10import java.text.ParseException; 11import java.util.ArrayList; 12import java.util.Arrays; 13import java.util.BitSet; 14import java.util.Date; 15import java.util.HashMap; 16import java.util.List; 17import java.util.Map; 18import java.util.MissingResourceException; 19import java.util.ResourceBundle; 20 21import android.icu.impl.Utility; 22import android.icu.text.BreakIterator; 23import android.icu.text.Collator; 24import android.icu.text.DateFormat; 25import android.icu.text.NumberFormat; 26import android.icu.text.SimpleDateFormat; 27 28/** 29 * This convenience class provides a mechanism for bundling together different 30 * globalization preferences. It includes: 31 * <ul> 32 * <li>A list of locales/languages in preference order</li> 33 * <li>A territory</li> 34 * <li>A currency</li> 35 * <li>A timezone</li> 36 * <li>A calendar</li> 37 * <li>A collator (for language-sensitive sorting, searching, and matching).</li> 38 * <li>Explicit overrides for date/time formats, etc.</li> 39 * </ul> 40 * The class will heuristically compute implicit, heuristic values for the above 41 * based on available data if explicit values are not supplied. These implicit 42 * values can be presented to users for confirmation, or replacement if the 43 * values are incorrect. 44 * <p> 45 * To reset any explicit field so that it will get heuristic values, pass in 46 * null. For example, myPreferences.setLocale(null); 47 * <p> 48 * All of the heuristics can be customized by subclasses, by overriding 49 * getTerritory(), guessCollator(), etc. 50 * <p> 51 * The class also supplies display names for languages, scripts, territories, 52 * currencies, timezones, etc. These are computed according to the 53 * locale/language preference list. Thus, if the preference is Breton; French; 54 * English, then the display name for a language will be returned in Breton if 55 * available, otherwise in French if available, otherwise in English. 56 * <p> 57 * The codes used to reference territory, currency, etc. are as defined elsewhere 58 * in ICU, and are taken from CLDR (which reflects RFC 3066bis usage, ISO 4217, 59 * and the TZ Timezone database identifiers). 60 * <p> 61 * <b>This is at a prototype stage, and has not incorporated all the design 62 * changes that we would like yet; further feedback is welcome.</b></p> 63 * Note: 64 * <ul> 65 * <li>to get the display name for the first day of the week, use the calendar + 66 * display names.</li> 67 * <li>to get the work days, ask the calendar (when that is available).</li> 68 * <li>to get papersize / measurement system/bidi-orientation, ask the locale 69 * (when that is available there)</li> 70 * <li>to get the field order in a date, and whether a time is 24hour or not, 71 * ask the DateFormat (when that is available there)</li> 72 * <li>it will support HOST locale when it becomes available (it is a special 73 * locale that will ask the services to use the host platform's values).</li> 74 * </ul> 75 * 76 * @hide Only a subset of ICU is exposed in Android 77 * @hide draft / provisional / internal are hidden on Android 78 */ 79 80//TODO: 81// - Add Holidays 82// - Add convenience to get/take Locale as well as ULocale. 83// - Add Lenient datetime formatting when that is available. 84// - Should this be serializable? 85// - Other utilities? 86 87public class GlobalizationPreferences implements Freezable<GlobalizationPreferences> { 88 89 /** 90 * Default constructor 91 * @hide draft / provisional / internal are hidden on Android 92 */ 93 public GlobalizationPreferences(){} 94 /** 95 * Number Format type 96 * @hide draft / provisional / internal are hidden on Android 97 */ 98 public static final int 99 NF_NUMBER = 0, // NumberFormat.NUMBERSTYLE 100 NF_CURRENCY = 1, // NumberFormat.CURRENCYSTYLE 101 NF_PERCENT = 2, // NumberFormat.PERCENTSTYLE 102 NF_SCIENTIFIC = 3, // NumberFormat.SCIENTIFICSTYLE 103 NF_INTEGER = 4; // NumberFormat.INTEGERSTYLE 104 105 private static final int NF_LIMIT = NF_INTEGER + 1; 106 107 /** 108 * Date Format type 109 * @hide draft / provisional / internal are hidden on Android 110 */ 111 public static final int 112 DF_FULL = DateFormat.FULL, // 0 113 DF_LONG = DateFormat.LONG, // 1 114 DF_MEDIUM = DateFormat.MEDIUM, // 2 115 DF_SHORT = DateFormat.SHORT, // 3 116 DF_NONE = 4; 117 118 private static final int DF_LIMIT = DF_NONE + 1; 119 120 /** 121 * For selecting a choice of display names 122 * @hide draft / provisional / internal are hidden on Android 123 */ 124 public static final int 125 ID_LOCALE = 0, 126 ID_LANGUAGE = 1, 127 ID_SCRIPT = 2, 128 ID_TERRITORY = 3, 129 ID_VARIANT = 4, 130 ID_KEYWORD = 5, 131 ID_KEYWORD_VALUE = 6, 132 ID_CURRENCY = 7, 133 ID_CURRENCY_SYMBOL = 8, 134 ID_TIMEZONE = 9; 135 136 //private static final int ID_LIMIT = ID_TIMEZONE + 1; 137 138 /** 139 * Break iterator type 140 * @hide draft / provisional / internal are hidden on Android 141 */ 142 public static final int 143 BI_CHARACTER = BreakIterator.KIND_CHARACTER, // 0 144 BI_WORD = BreakIterator.KIND_WORD, // 1 145 BI_LINE = BreakIterator.KIND_LINE, // 2 146 BI_SENTENCE = BreakIterator.KIND_SENTENCE, // 3 147 BI_TITLE = BreakIterator.KIND_TITLE; // 4 148 149 private static final int BI_LIMIT = BI_TITLE + 1; 150 151 /** 152 * Sets the language/locale priority list. If other information is 153 * not (yet) available, this is used to to produce a default value 154 * for the appropriate territory, currency, timezone, etc. The 155 * user should be given the opportunity to correct those defaults 156 * in case they are incorrect. 157 * 158 * @param inputLocales list of locales in priority order, eg {"be", "fr"} 159 * for Breton first, then French if that fails. 160 * @return this, for chaining 161 * @hide draft / provisional / internal are hidden on Android 162 */ 163 public GlobalizationPreferences setLocales(List<ULocale> inputLocales) { 164 if (isFrozen()) { 165 throw new UnsupportedOperationException("Attempt to modify immutable object"); 166 } 167 locales = processLocales(inputLocales); 168 return this; 169 } 170 171 /** 172 * Get a copy of the language/locale priority list 173 * 174 * @return a copy of the language/locale priority list. 175 * @hide draft / provisional / internal are hidden on Android 176 */ 177 public List<ULocale> getLocales() { 178 List<ULocale> result; 179 if (locales == null) { 180 result = guessLocales(); 181 } else { 182 result = new ArrayList<ULocale>(); 183 result.addAll(locales); 184 } 185 return result; 186 } 187 188 /** 189 * Convenience function for getting the locales in priority order 190 * @param index The index (0..n) of the desired item. 191 * @return desired item. null if index is out of range 192 * @hide draft / provisional / internal are hidden on Android 193 */ 194 public ULocale getLocale(int index) { 195 List<ULocale> lcls = locales; 196 if (lcls == null) { 197 lcls = guessLocales(); 198 } 199 if (index >= 0 && index < lcls.size()) { 200 return lcls.get(index); 201 } 202 return null; 203 } 204 205 /** 206 * Convenience routine for setting the language/locale priority 207 * list from an array. 208 * 209 * @see #setLocales(List locales) 210 * @param uLocales list of locales in an array 211 * @return this, for chaining 212 * @hide draft / provisional / internal are hidden on Android 213 */ 214 public GlobalizationPreferences setLocales(ULocale[] uLocales) { 215 if (isFrozen()) { 216 throw new UnsupportedOperationException("Attempt to modify immutable object"); 217 } 218 return setLocales(Arrays.asList(uLocales)); 219 } 220 221 /** 222 * Convenience routine for setting the language/locale priority 223 * list from a single locale/language. 224 * 225 * @see #setLocales(List locales) 226 * @param uLocale single locale 227 * @return this, for chaining 228 * @hide draft / provisional / internal are hidden on Android 229 */ 230 public GlobalizationPreferences setLocale(ULocale uLocale) { 231 if (isFrozen()) { 232 throw new UnsupportedOperationException("Attempt to modify immutable object"); 233 } 234 return setLocales(new ULocale[]{uLocale}); 235 } 236 237 /** 238 * Convenience routine for setting the locale priority list from 239 * an Accept-Language string. 240 * @see #setLocales(List locales) 241 * @param acceptLanguageString Accept-Language list, as defined by 242 * Section 14.4 of the RFC 2616 (HTTP 1.1) 243 * @return this, for chaining 244 * @hide draft / provisional / internal are hidden on Android 245 */ 246 public GlobalizationPreferences setLocales(String acceptLanguageString) { 247 if (isFrozen()) { 248 throw new UnsupportedOperationException("Attempt to modify immutable object"); 249 } 250 ULocale[] acceptLocales = null; 251 try { 252 acceptLocales = ULocale.parseAcceptLanguage(acceptLanguageString, true); 253 } catch (ParseException pe) { 254 //TODO: revisit after 3.8 255 throw new IllegalArgumentException("Invalid Accept-Language string"); 256 } 257 return setLocales(acceptLocales); 258 } 259 260 /** 261 * Convenience function to get a ResourceBundle instance using 262 * the specified base name based on the language/locale priority list 263 * stored in this object. 264 * 265 * @param baseName the base name of the resource bundle, a fully qualified 266 * class name 267 * @return a resource bundle for the given base name and locale based on the 268 * language/locale priority list stored in this object 269 * @hide draft / provisional / internal are hidden on Android 270 */ 271 public ResourceBundle getResourceBundle(String baseName) { 272 return getResourceBundle(baseName, null); 273 } 274 275 /** 276 * Convenience function to get a ResourceBundle instance using 277 * the specified base name and class loader based on the language/locale 278 * priority list stored in this object. 279 * 280 * @param baseName the base name of the resource bundle, a fully qualified 281 * class name 282 * @param loader the class object from which to load the resource bundle 283 * @return a resource bundle for the given base name and locale based on the 284 * language/locale priority list stored in this object 285 * @hide draft / provisional / internal are hidden on Android 286 */ 287 public ResourceBundle getResourceBundle(String baseName, ClassLoader loader) { 288 UResourceBundle urb = null; 289 UResourceBundle candidate = null; 290 String actualLocaleName = null; 291 List<ULocale> fallbacks = getLocales(); 292 for (int i = 0; i < fallbacks.size(); i++) { 293 String localeName = (fallbacks.get(i)).toString(); 294 if (actualLocaleName != null && localeName.equals(actualLocaleName)) { 295 // Actual locale name in the previous round may exactly matches 296 // with the next fallback locale 297 urb = candidate; 298 break; 299 } 300 try { 301 if (loader == null) { 302 candidate = UResourceBundle.getBundleInstance(baseName, localeName); 303 } 304 else { 305 candidate = UResourceBundle.getBundleInstance(baseName, localeName, loader); 306 } 307 if (candidate != null) { 308 actualLocaleName = candidate.getULocale().getName(); 309 if (actualLocaleName.equals(localeName)) { 310 urb = candidate; 311 break; 312 } 313 if (urb == null) { 314 // Preserve the available bundle as the last resort 315 urb = candidate; 316 } 317 } 318 } catch (MissingResourceException mre) { 319 actualLocaleName = null; 320 continue; 321 } 322 } 323 if (urb == null) { 324 throw new MissingResourceException("Can't find bundle for base name " 325 + baseName, baseName, ""); 326 } 327 return urb; 328 } 329 330 /** 331 * Sets the territory, which is a valid territory according to for 332 * RFC 3066 (or successor). If not otherwise set, default 333 * currency and timezone values will be set from this. The user 334 * should be given the opportunity to correct those defaults in 335 * case they are incorrect. 336 * 337 * @param territory code 338 * @return this, for chaining 339 * @hide draft / provisional / internal are hidden on Android 340 */ 341 public GlobalizationPreferences setTerritory(String territory) { 342 if (isFrozen()) { 343 throw new UnsupportedOperationException("Attempt to modify immutable object"); 344 } 345 this.territory = territory; // immutable, so don't need to clone 346 return this; 347 } 348 349 /** 350 * Gets the territory setting. If it wasn't explicitly set, it is 351 * computed from the general locale setting. 352 * 353 * @return territory code, explicit or implicit. 354 * @hide draft / provisional / internal are hidden on Android 355 */ 356 public String getTerritory() { 357 if (territory == null) { 358 return guessTerritory(); 359 } 360 return territory; // immutable, so don't need to clone 361 } 362 363 /** 364 * Sets the currency code. If this has not been set, uses default for territory. 365 * 366 * @param currency Valid ISO 4217 currency code. 367 * @return this, for chaining 368 * @hide draft / provisional / internal are hidden on Android 369 */ 370 public GlobalizationPreferences setCurrency(Currency currency) { 371 if (isFrozen()) { 372 throw new UnsupportedOperationException("Attempt to modify immutable object"); 373 } 374 this.currency = currency; // immutable, so don't need to clone 375 return this; 376 } 377 378 /** 379 * Get a copy of the currency computed according to the settings. 380 * 381 * @return currency code, explicit or implicit. 382 * @hide draft / provisional / internal are hidden on Android 383 */ 384 public Currency getCurrency() { 385 if (currency == null) { 386 return guessCurrency(); 387 } 388 return currency; // immutable, so don't have to clone 389 } 390 391 /** 392 * Sets the calendar. If this has not been set, uses default for territory. 393 * 394 * @param calendar arbitrary calendar 395 * @return this, for chaining 396 * @hide draft / provisional / internal are hidden on Android 397 */ 398 public GlobalizationPreferences setCalendar(Calendar calendar) { 399 if (isFrozen()) { 400 throw new UnsupportedOperationException("Attempt to modify immutable object"); 401 } 402 this.calendar = (Calendar) calendar.clone(); // clone for safety 403 return this; 404 } 405 406 /** 407 * Get a copy of the calendar according to the settings. 408 * 409 * @return calendar explicit or implicit. 410 * @hide draft / provisional / internal are hidden on Android 411 */ 412 public Calendar getCalendar() { 413 if (calendar == null) { 414 return guessCalendar(); 415 } 416 Calendar temp = (Calendar) calendar.clone(); // clone for safety 417 temp.setTimeZone(getTimeZone()); 418 temp.setTimeInMillis(System.currentTimeMillis()); 419 return temp; 420 } 421 422 /** 423 * Sets the timezone ID. If this has not been set, uses default for territory. 424 * 425 * @param timezone a valid TZID (see UTS#35). 426 * @return this, for chaining 427 * @hide draft / provisional / internal are hidden on Android 428 */ 429 public GlobalizationPreferences setTimeZone(TimeZone timezone) { 430 if (isFrozen()) { 431 throw new UnsupportedOperationException("Attempt to modify immutable object"); 432 } 433 this.timezone = (TimeZone) timezone.clone(); // clone for safety; 434 return this; 435 } 436 437 /** 438 * Get the timezone. It was either explicitly set, or is 439 * heuristically computed from other settings. 440 * 441 * @return timezone, either implicitly or explicitly set 442 * @hide draft / provisional / internal are hidden on Android 443 */ 444 public TimeZone getTimeZone() { 445 if (timezone == null) { 446 return guessTimeZone(); 447 } 448 return timezone.cloneAsThawed(); // clone for safety 449 } 450 451 /** 452 * Get a copy of the collator according to the settings. 453 * 454 * @return collator explicit or implicit. 455 * @hide draft / provisional / internal are hidden on Android 456 */ 457 public Collator getCollator() { 458 if (collator == null) { 459 return guessCollator(); 460 } 461 try { 462 return (Collator) collator.clone(); // clone for safety 463 } catch (CloneNotSupportedException e) { 464 throw new ICUCloneNotSupportedException("Error in cloning collator", e); 465 } 466 } 467 468 /** 469 * Explicitly set the collator for this object. 470 * @param collator The collator object to be passed. 471 * @return this, for chaining 472 * @hide draft / provisional / internal are hidden on Android 473 */ 474 public GlobalizationPreferences setCollator(Collator collator) { 475 if (isFrozen()) { 476 throw new UnsupportedOperationException("Attempt to modify immutable object"); 477 } 478 try { 479 this.collator = (Collator) collator.clone(); // clone for safety 480 } catch (CloneNotSupportedException e) { 481 throw new ICUCloneNotSupportedException("Error in cloning collator", e); 482 } 483 return this; 484 } 485 486 /** 487 * Get a copy of the break iterator for the specified type according to the 488 * settings. 489 * 490 * @param type break type - BI_CHARACTER or BI_WORD, BI_LINE, BI_SENTENCE, BI_TITLE 491 * @return break iterator explicit or implicit 492 * @hide draft / provisional / internal are hidden on Android 493 */ 494 public BreakIterator getBreakIterator(int type) { 495 if (type < BI_CHARACTER || type >= BI_LIMIT) { 496 throw new IllegalArgumentException("Illegal break iterator type"); 497 } 498 if (breakIterators == null || breakIterators[type] == null) { 499 return guessBreakIterator(type); 500 } 501 return (BreakIterator) breakIterators[type].clone(); // clone for safety 502 } 503 504 /** 505 * Explicitly set the break iterator for this object. 506 * 507 * @param type break type - BI_CHARACTER or BI_WORD, BI_LINE, BI_SENTENCE, BI_TITLE 508 * @param iterator a break iterator 509 * @return this, for chaining 510 * @hide draft / provisional / internal are hidden on Android 511 */ 512 public GlobalizationPreferences setBreakIterator(int type, BreakIterator iterator) { 513 if (type < BI_CHARACTER || type >= BI_LIMIT) { 514 throw new IllegalArgumentException("Illegal break iterator type"); 515 } 516 if (isFrozen()) { 517 throw new UnsupportedOperationException("Attempt to modify immutable object"); 518 } 519 if (breakIterators == null) 520 breakIterators = new BreakIterator[BI_LIMIT]; 521 breakIterators[type] = (BreakIterator) iterator.clone(); // clone for safety 522 return this; 523 } 524 525 /** 526 * Get the display name for an ID: language, script, territory, currency, timezone... 527 * Uses the language priority list to do so. 528 * 529 * @param id language code, script code, ... 530 * @param type specifies the type of the ID: ID_LANGUAGE, etc. 531 * @return the display name 532 * @hide draft / provisional / internal are hidden on Android 533 */ 534 public String getDisplayName(String id, int type) { 535 String result = id; 536 for (ULocale locale : getLocales()) { 537 if (!isAvailableLocale(locale, TYPE_GENERIC)) { 538 continue; 539 } 540 switch (type) { 541 case ID_LOCALE: 542 result = ULocale.getDisplayName(id, locale); 543 break; 544 case ID_LANGUAGE: 545 result = ULocale.getDisplayLanguage(id, locale); 546 break; 547 case ID_SCRIPT: 548 result = ULocale.getDisplayScript("und-" + id, locale); 549 break; 550 case ID_TERRITORY: 551 result = ULocale.getDisplayCountry("und-" + id, locale); 552 break; 553 case ID_VARIANT: 554 // TODO fix variant parsing 555 result = ULocale.getDisplayVariant("und-QQ-" + id, locale); 556 break; 557 case ID_KEYWORD: 558 result = ULocale.getDisplayKeyword(id, locale); 559 break; 560 case ID_KEYWORD_VALUE: 561 String[] parts = new String[2]; 562 Utility.split(id,'=',parts); 563 result = ULocale.getDisplayKeywordValue("und@"+id, parts[0], locale); 564 // TODO fix to tell when successful 565 if (result.equals(parts[1])) { 566 continue; 567 } 568 break; 569 case ID_CURRENCY_SYMBOL: 570 case ID_CURRENCY: 571 Currency temp = new Currency(id); 572 result =temp.getName(locale, type==ID_CURRENCY 573 ? Currency.LONG_NAME 574 : Currency.SYMBOL_NAME, new boolean[1]); 575 // TODO: have method that doesn't take parameter. Add 576 // function to determine whether string is choice 577 // format. 578 // TODO: have method that doesn't require us 579 // to create a currency 580 break; 581 case ID_TIMEZONE: 582 SimpleDateFormat dtf = new SimpleDateFormat("vvvv",locale); 583 dtf.setTimeZone(TimeZone.getFrozenTimeZone(id)); 584 result = dtf.format(new Date()); 585 // TODO, have method that doesn't require us to create a timezone 586 // fix other hacks 587 // hack for couldn't match 588 589 boolean isBadStr = false; 590 // Matcher badTimeZone = Pattern.compile("[A-Z]{2}|.*\\s\\([A-Z]{2}\\)").matcher(""); 591 // badtzstr = badTimeZone.reset(result).matches(); 592 String teststr = result; 593 int sidx = result.indexOf('('); 594 int eidx = result.indexOf(')'); 595 if (sidx != -1 && eidx != -1 && (eidx - sidx) == 3) { 596 teststr = result.substring(sidx+1, eidx); 597 } 598 if (teststr.length() == 2) { 599 isBadStr = true; 600 for (int i = 0; i < 2; i++) { 601 char c = teststr.charAt(i); 602 if (c < 'A' || 'Z' < c) { 603 isBadStr = false; 604 break; 605 } 606 } 607 } 608 if (isBadStr) { 609 continue; 610 } 611 break; 612 default: 613 throw new IllegalArgumentException("Unknown type: " + type); 614 } 615 616 // TODO need better way of seeing if we fell back to root!! 617 // This will not work at all for lots of stuff 618 if (!id.equals(result)) { 619 return result; 620 } 621 } 622 return result; 623 } 624 625 /** 626 * Set an explicit date format. Overrides the locale priority list for 627 * a particular combination of dateStyle and timeStyle. DF_NONE should 628 * be used if for the style, where only the date or time format individually 629 * is being set. 630 * 631 * @param dateStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE 632 * @param timeStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE 633 * @param format The date format 634 * @return this, for chaining 635 * @hide draft / provisional / internal are hidden on Android 636 */ 637 public GlobalizationPreferences setDateFormat(int dateStyle, int timeStyle, DateFormat format) { 638 if (isFrozen()) { 639 throw new UnsupportedOperationException("Attempt to modify immutable object"); 640 } 641 if (dateFormats == null) { 642 dateFormats = new DateFormat[DF_LIMIT][DF_LIMIT]; 643 } 644 dateFormats[dateStyle][timeStyle] = (DateFormat) format.clone(); // for safety 645 return this; 646 } 647 648 /** 649 * Gets a date format according to the current settings. If there 650 * is an explicit (non-null) date/time format set, a copy of that 651 * is returned. Otherwise, the language priority list is used. 652 * DF_NONE should be used for the style, where only the date or 653 * time format individually is being gotten. 654 * 655 * @param dateStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE 656 * @param timeStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE 657 * @return a DateFormat, according to the above description 658 * @hide draft / provisional / internal are hidden on Android 659 */ 660 public DateFormat getDateFormat(int dateStyle, int timeStyle) { 661 if (dateStyle == DF_NONE && timeStyle == DF_NONE 662 || dateStyle < 0 || dateStyle >= DF_LIMIT 663 || timeStyle < 0 || timeStyle >= DF_LIMIT) { 664 throw new IllegalArgumentException("Illegal date format style arguments"); 665 } 666 DateFormat result = null; 667 if (dateFormats != null) { 668 result = dateFormats[dateStyle][timeStyle]; 669 } 670 if (result != null) { 671 result = (DateFormat) result.clone(); // clone for safety 672 // Not sure overriding configuration is what we really want... 673 result.setTimeZone(getTimeZone()); 674 } else { 675 result = guessDateFormat(dateStyle, timeStyle); 676 } 677 return result; 678 } 679 680 /** 681 * Gets a number format according to the current settings. If 682 * there is an explicit (non-null) number format set, a copy of 683 * that is returned. Otherwise, the language priority list is 684 * used. 685 * 686 * @param style NF_NUMBER, NF_CURRENCY, NF_PERCENT, NF_SCIENTIFIC, NF_INTEGER 687 * @hide draft / provisional / internal are hidden on Android 688 */ 689 public NumberFormat getNumberFormat(int style) { 690 if (style < 0 || style >= NF_LIMIT) { 691 throw new IllegalArgumentException("Illegal number format type"); 692 } 693 NumberFormat result = null; 694 if (numberFormats != null) { 695 result = numberFormats[style]; 696 } 697 if (result != null) { 698 result = (NumberFormat) result.clone(); // clone for safety (later optimize) 699 } else { 700 result = guessNumberFormat(style); 701 } 702 return result; 703 } 704 705 /** 706 * Sets a number format explicitly. Overrides the general locale settings. 707 * 708 * @param style NF_NUMBER, NF_CURRENCY, NF_PERCENT, NF_SCIENTIFIC, NF_INTEGER 709 * @param format The number format 710 * @return this, for chaining 711 * @hide draft / provisional / internal are hidden on Android 712 */ 713 public GlobalizationPreferences setNumberFormat(int style, NumberFormat format) { 714 if (isFrozen()) { 715 throw new UnsupportedOperationException("Attempt to modify immutable object"); 716 } 717 if (numberFormats == null) { 718 numberFormats = new NumberFormat[NF_LIMIT]; 719 } 720 numberFormats[style] = (NumberFormat) format.clone(); // for safety 721 return this; 722 } 723 724 /** 725 * Restore the object to the initial state. 726 * 727 * @return this, for chaining 728 * @hide draft / provisional / internal are hidden on Android 729 */ 730 public GlobalizationPreferences reset() { 731 if (isFrozen()) { 732 throw new UnsupportedOperationException("Attempt to modify immutable object"); 733 } 734 locales = null; 735 territory = null; 736 calendar = null; 737 collator = null; 738 breakIterators = null; 739 timezone = null; 740 currency = null; 741 dateFormats = null; 742 numberFormats = null; 743 implicitLocales = null; 744 return this; 745 } 746 747 /** 748 * Process a language/locale priority list specified via <code>setLocales</code>. 749 * The input locale list may be expanded or re-ordered to represent the prioritized 750 * language/locale order actually used by this object by the algorithm explained 751 * below. 752 * <br> 753 * <br> 754 * <b>Step 1</b>: Move later occurrence of more specific locale before earlier 755 * occurrence of less specific locale. 756 * <br> 757 * Before: en, fr_FR, en_US, en_GB 758 * <br> 759 * After: en_US, en_GB, en, fr_FR 760 * <br> 761 * <br> 762 * <b>Step 2</b>: Append a fallback locale to each locale. 763 * <br> 764 * Before: en_US, en_GB, en, fr_FR 765 * <br> 766 * After: en_US, en, en_GB, en, en, fr_FR, fr 767 * <br> 768 * <br> 769 * <b>Step 3</b>: Remove earlier occurrence of duplicated locale entries. 770 * <br> 771 * Before: en_US, en, en_GB, en, en, fr_FR, fr 772 * <br> 773 * After: en_US, en_GB, en, fr_FR, fr 774 * <br> 775 * <br> 776 * The final locale list is used to produce a default value for the appropriate territory, 777 * currency, timezone, etc. The list also represents the lookup order used in 778 * <code>getResourceBundle</code> for this object. A subclass may override this method 779 * to customize the algorithm used for populating the locale list. 780 * 781 * @param inputLocales The list of input locales 782 * @hide draft / provisional / internal are hidden on Android 783 */ 784 protected List<ULocale> processLocales(List<ULocale> inputLocales) { 785 List<ULocale> result = new ArrayList<ULocale>(); 786 /* 787 * Step 1: Relocate later occurrence of more specific locale 788 * before earlier occurrence of less specific locale. 789 * 790 * Example: 791 * Before - en_US, fr_FR, zh, en_US_Boston, zh_TW, zh_Hant, fr_CA 792 * After - en_US_Boston, en_US, fr_FR, zh_TW, zh_Hant, zh, fr_CA 793 */ 794 for (int i = 0; i < inputLocales.size(); i++) { 795 ULocale uloc = inputLocales.get(i); 796 797 String language = uloc.getLanguage(); 798 String script = uloc.getScript(); 799 String country = uloc.getCountry(); 800 String variant = uloc.getVariant(); 801 802 boolean bInserted = false; 803 for (int j = 0; j < result.size(); j++) { 804 // Check if this locale is more specific 805 // than existing locale entries already inserted 806 // in the destination list 807 ULocale u = result.get(j); 808 if (!u.getLanguage().equals(language)) { 809 continue; 810 } 811 String s = u.getScript(); 812 String c = u.getCountry(); 813 String v = u.getVariant(); 814 if (!s.equals(script)) { 815 if (s.length() == 0 && c.length() == 0 && v.length() == 0) { 816 result.add(j, uloc); 817 bInserted = true; 818 break; 819 } else if (s.length() == 0 && c.equals(country)) { 820 // We want to see zh_Hant_HK before zh_HK 821 result.add(j, uloc); 822 bInserted = true; 823 break; 824 } else if (script.length() == 0 && country.length() > 0 && c.length() == 0) { 825 // We want to see zh_HK before zh_Hant 826 result.add(j, uloc); 827 bInserted = true; 828 break; 829 } 830 continue; 831 } 832 if (!c.equals(country)) { 833 if (c.length() == 0 && v.length() == 0) { 834 result.add(j, uloc); 835 bInserted = true; 836 break; 837 } 838 } 839 if (!v.equals(variant) && v.length() == 0) { 840 result.add(j, uloc); 841 bInserted = true; 842 break; 843 } 844 } 845 if (!bInserted) { 846 // Add this locale at the end of the list 847 result.add(uloc); 848 } 849 } 850 851 // TODO: Locale aliases might be resolved here 852 // For example, zh_Hant_TW = zh_TW 853 854 /* 855 * Step 2: Append fallback locales for each entry 856 * 857 * Example: 858 * Before - en_US_Boston, en_US, fr_FR, zh_TW, zh_Hant, zh, fr_CA 859 * After - en_US_Boston, en_US, en, en_US, en, fr_FR, fr, 860 * zh_TW, zn, zh_Hant, zh, zh, fr_CA, fr 861 */ 862 int index = 0; 863 while (index < result.size()) { 864 ULocale uloc = result.get(index); 865 while (true) { 866 uloc = uloc.getFallback(); 867 if (uloc.getLanguage().length() == 0) { 868 break; 869 } 870 index++; 871 result.add(index, uloc); 872 } 873 index++; 874 } 875 876 /* 877 * Step 3: Remove earlier occurrence of duplicated locales 878 * 879 * Example: 880 * Before - en_US_Boston, en_US, en, en_US, en, fr_FR, fr, 881 * zh_TW, zn, zh_Hant, zh, zh, fr_CA, fr 882 * After - en_US_Boston, en_US, en, fr_FR, zh_TW, zh_Hant, 883 * zh, fr_CA, fr 884 */ 885 index = 0; 886 while (index < result.size() - 1) { 887 ULocale uloc = result.get(index); 888 boolean bRemoved = false; 889 for (int i = index + 1; i < result.size(); i++) { 890 if (uloc.equals(result.get(i))) { 891 // Remove earlier one 892 result.remove(index); 893 bRemoved = true; 894 break; 895 } 896 } 897 if (!bRemoved) { 898 index++; 899 } 900 } 901 return result; 902 } 903 904 905 /** 906 * This function can be overridden by subclasses to use different heuristics. 907 * <b>It MUST return a 'safe' value, 908 * one whose modification will not affect this object.</b> 909 * 910 * @param dateStyle 911 * @param timeStyle 912 * @hide draft / provisional / internal are hidden on Android 913 */ 914 protected DateFormat guessDateFormat(int dateStyle, int timeStyle) { 915 DateFormat result; 916 ULocale dfLocale = getAvailableLocale(TYPE_DATEFORMAT); 917 if (dfLocale == null) { 918 dfLocale = ULocale.ROOT; 919 } 920 if (timeStyle == DF_NONE) { 921 result = DateFormat.getDateInstance(getCalendar(), dateStyle, dfLocale); 922 } else if (dateStyle == DF_NONE) { 923 result = DateFormat.getTimeInstance(getCalendar(), timeStyle, dfLocale); 924 } else { 925 result = DateFormat.getDateTimeInstance(getCalendar(), dateStyle, timeStyle, dfLocale); 926 } 927 return result; 928 } 929 930 /** 931 * This function can be overridden by subclasses to use different heuristics. 932 * <b>It MUST return a 'safe' value, 933 * one whose modification will not affect this object.</b> 934 * 935 * @param style 936 * @hide draft / provisional / internal are hidden on Android 937 */ 938 protected NumberFormat guessNumberFormat(int style) { 939 NumberFormat result; 940 ULocale nfLocale = getAvailableLocale(TYPE_NUMBERFORMAT); 941 if (nfLocale == null) { 942 nfLocale = ULocale.ROOT; 943 } 944 switch (style) { 945 case NF_NUMBER: 946 result = NumberFormat.getInstance(nfLocale); 947 break; 948 case NF_SCIENTIFIC: 949 result = NumberFormat.getScientificInstance(nfLocale); 950 break; 951 case NF_INTEGER: 952 result = NumberFormat.getIntegerInstance(nfLocale); 953 break; 954 case NF_PERCENT: 955 result = NumberFormat.getPercentInstance(nfLocale); 956 break; 957 case NF_CURRENCY: 958 result = NumberFormat.getCurrencyInstance(nfLocale); 959 result.setCurrency(getCurrency()); 960 break; 961 default: 962 throw new IllegalArgumentException("Unknown number format style"); 963 } 964 return result; 965 } 966 967 /** 968 * This function can be overridden by subclasses to use different heuristics. 969 * 970 * @hide draft / provisional / internal are hidden on Android 971 */ 972 protected String guessTerritory() { 973 String result; 974 // pass through locales to see if there is a territory. 975 for (ULocale locale : getLocales()) { 976 result = locale.getCountry(); 977 if (result.length() != 0) { 978 return result; 979 } 980 } 981 // if not, guess from the first language tag, or maybe from 982 // intersection of languages, eg nl + fr => BE 983 // TODO: fix using real data 984 // for now, just use fixed values 985 ULocale firstLocale = getLocale(0); 986 String language = firstLocale.getLanguage(); 987 String script = firstLocale.getScript(); 988 result = null; 989 if (script.length() != 0) { 990 result = language_territory_hack_map.get(language + "_" + script); 991 } 992 if (result == null) { 993 result = language_territory_hack_map.get(language); 994 } 995 if (result == null) { 996 result = "US"; // need *some* default 997 } 998 return result; 999 } 1000 1001 /** 1002 * This function can be overridden by subclasses to use different heuristics 1003 * 1004 * @hide draft / provisional / internal are hidden on Android 1005 */ 1006 protected Currency guessCurrency() { 1007 return Currency.getInstance(new ULocale("und-" + getTerritory())); 1008 } 1009 1010 /** 1011 * This function can be overridden by subclasses to use different heuristics 1012 * <b>It MUST return a 'safe' value, 1013 * one whose modification will not affect this object.</b> 1014 * 1015 * @hide draft / provisional / internal are hidden on Android 1016 */ 1017 protected List<ULocale> guessLocales() { 1018 if (implicitLocales == null) { 1019 List<ULocale> result = new ArrayList<ULocale>(1); 1020 result.add(ULocale.getDefault()); 1021 implicitLocales = processLocales(result); 1022 } 1023 return implicitLocales; 1024 } 1025 1026 /** 1027 * This function can be overridden by subclasses to use different heuristics. 1028 * <b>It MUST return a 'safe' value, 1029 * one whose modification will not affect this object.</b> 1030 * 1031 * @hide draft / provisional / internal are hidden on Android 1032 */ 1033 protected Collator guessCollator() { 1034 ULocale collLocale = getAvailableLocale(TYPE_COLLATOR); 1035 if (collLocale == null) { 1036 collLocale = ULocale.ROOT; 1037 } 1038 return Collator.getInstance(collLocale); 1039 } 1040 1041 /** 1042 * This function can be overridden by subclasses to use different heuristics. 1043 * <b>It MUST return a 'safe' value, 1044 * one whose modification will not affect this object.</b> 1045 * 1046 * @param type 1047 * @hide draft / provisional / internal are hidden on Android 1048 */ 1049 protected BreakIterator guessBreakIterator(int type) { 1050 BreakIterator bitr = null; 1051 ULocale brkLocale = getAvailableLocale(TYPE_BREAKITERATOR); 1052 if (brkLocale == null) { 1053 brkLocale = ULocale.ROOT; 1054 } 1055 switch (type) { 1056 case BI_CHARACTER: 1057 bitr = BreakIterator.getCharacterInstance(brkLocale); 1058 break; 1059 case BI_TITLE: 1060 bitr = BreakIterator.getTitleInstance(brkLocale); 1061 break; 1062 case BI_WORD: 1063 bitr = BreakIterator.getWordInstance(brkLocale); 1064 break; 1065 case BI_LINE: 1066 bitr = BreakIterator.getLineInstance(brkLocale); 1067 break; 1068 case BI_SENTENCE: 1069 bitr = BreakIterator.getSentenceInstance(brkLocale); 1070 break; 1071 default: 1072 throw new IllegalArgumentException("Unknown break iterator type"); 1073 } 1074 return bitr; 1075 } 1076 1077 /** 1078 * This function can be overridden by subclasses to use different heuristics. 1079 * <b>It MUST return a 'safe' value, 1080 * one whose modification will not affect this object.</b> 1081 * 1082 * @hide draft / provisional / internal are hidden on Android 1083 */ 1084 protected TimeZone guessTimeZone() { 1085 // TODO fix using real data 1086 // for single-zone countries, pick that zone 1087 // for others, pick the most populous zone 1088 // for now, just use fixed value 1089 // NOTE: in a few cases can do better by looking at language. 1090 // Eg haw+US should go to Pacific/Honolulu 1091 // fr+CA should go to America/Montreal 1092 String timezoneString = territory_tzid_hack_map.get(getTerritory()); 1093 if (timezoneString == null) { 1094 String[] attempt = TimeZone.getAvailableIDs(getTerritory()); 1095 if (attempt.length == 0) { 1096 timezoneString = "Etc/GMT"; // gotta do something 1097 } else { 1098 int i; 1099 // this all needs to be fixed to use real data. But for now, do slightly better by skipping cruft 1100 for (i = 0; i < attempt.length; ++i) { 1101 if (attempt[i].indexOf("/") >= 0) break; 1102 } 1103 if (i > attempt.length) i = 0; 1104 timezoneString = attempt[i]; 1105 } 1106 } 1107 return TimeZone.getTimeZone(timezoneString); 1108 } 1109 1110 /** 1111 * This function can be overridden by subclasses to use different heuristics. 1112 * <b>It MUST return a 'safe' value, 1113 * one whose modification will not affect this object.</b> 1114 * 1115 * @hide draft / provisional / internal are hidden on Android 1116 */ 1117 protected Calendar guessCalendar() { 1118 ULocale calLocale = getAvailableLocale(TYPE_CALENDAR); 1119 if (calLocale == null) { 1120 calLocale = ULocale.US; 1121 } 1122 return Calendar.getInstance(getTimeZone(), calLocale); 1123 } 1124 1125 // PRIVATES 1126 1127 private List<ULocale> locales; 1128 private String territory; 1129 private Currency currency; 1130 private TimeZone timezone; 1131 private Calendar calendar; 1132 private Collator collator; 1133 private BreakIterator[] breakIterators; 1134 private DateFormat[][] dateFormats; 1135 private NumberFormat[] numberFormats; 1136 private List<ULocale> implicitLocales; 1137 1138 { 1139 reset(); 1140 } 1141 1142 1143 private ULocale getAvailableLocale(int type) { 1144 List<ULocale> locs = getLocales(); 1145 ULocale result = null; 1146 for (int i = 0; i < locs.size(); i++) { 1147 ULocale l = locs.get(i); 1148 if (isAvailableLocale(l, type)) { 1149 result = l; 1150 break; 1151 } 1152 } 1153 return result; 1154 } 1155 1156 private boolean isAvailableLocale(ULocale loc, int type) { 1157 BitSet bits = available_locales.get(loc); 1158 if (bits != null && bits.get(type)) { 1159 return true; 1160 } 1161 return false; 1162 } 1163 1164 /* 1165 * Available locales for service types 1166 */ 1167 private static final HashMap<ULocale, BitSet> available_locales = new HashMap<ULocale, BitSet>(); 1168 private static final int 1169 TYPE_GENERIC = 0, 1170 TYPE_CALENDAR = 1, 1171 TYPE_DATEFORMAT= 2, 1172 TYPE_NUMBERFORMAT = 3, 1173 TYPE_COLLATOR = 4, 1174 TYPE_BREAKITERATOR = 5, 1175 TYPE_LIMIT = TYPE_BREAKITERATOR + 1; 1176 1177 static { 1178 BitSet bits; 1179 ULocale[] allLocales = ULocale.getAvailableLocales(); 1180 for (int i = 0; i < allLocales.length; i++) { 1181 bits = new BitSet(TYPE_LIMIT); 1182 available_locales.put(allLocales[i], bits); 1183 bits.set(TYPE_GENERIC); 1184 } 1185 1186 ULocale[] calLocales = Calendar.getAvailableULocales(); 1187 for (int i = 0; i < calLocales.length; i++) { 1188 bits = available_locales.get(calLocales[i]); 1189 if (bits == null) { 1190 bits = new BitSet(TYPE_LIMIT); 1191 available_locales.put(allLocales[i], bits); 1192 } 1193 bits.set(TYPE_CALENDAR); 1194 } 1195 1196 ULocale[] dateLocales = DateFormat.getAvailableULocales(); 1197 for (int i = 0; i < dateLocales.length; i++) { 1198 bits = available_locales.get(dateLocales[i]); 1199 if (bits == null) { 1200 bits = new BitSet(TYPE_LIMIT); 1201 available_locales.put(allLocales[i], bits); 1202 } 1203 bits.set(TYPE_DATEFORMAT); 1204 } 1205 1206 ULocale[] numLocales = NumberFormat.getAvailableULocales(); 1207 for (int i = 0; i < numLocales.length; i++) { 1208 bits = available_locales.get(numLocales[i]); 1209 if (bits == null) { 1210 bits = new BitSet(TYPE_LIMIT); 1211 available_locales.put(allLocales[i], bits); 1212 } 1213 bits.set(TYPE_NUMBERFORMAT); 1214 } 1215 1216 ULocale[] collLocales = Collator.getAvailableULocales(); 1217 for (int i = 0; i < collLocales.length; i++) { 1218 bits = available_locales.get(collLocales[i]); 1219 if (bits == null) { 1220 bits = new BitSet(TYPE_LIMIT); 1221 available_locales.put(allLocales[i], bits); 1222 } 1223 bits.set(TYPE_COLLATOR); 1224 } 1225 1226 ULocale[] brkLocales = BreakIterator.getAvailableULocales(); 1227 for (int i = 0; i < brkLocales.length; i++) { 1228 bits = available_locales.get(brkLocales[i]); 1229 bits.set(TYPE_BREAKITERATOR); 1230 } 1231 } 1232 1233 /** WARNING: All of this data is temporary, until we start importing from CLDR!!! 1234 * 1235 */ 1236 private static final Map<String, String> language_territory_hack_map = new HashMap<String, String>(); 1237 private static final String[][] language_territory_hack = { 1238 {"af", "ZA"}, 1239 {"am", "ET"}, 1240 {"ar", "SA"}, 1241 {"as", "IN"}, 1242 {"ay", "PE"}, 1243 {"az", "AZ"}, 1244 {"bal", "PK"}, 1245 {"be", "BY"}, 1246 {"bg", "BG"}, 1247 {"bn", "IN"}, 1248 {"bs", "BA"}, 1249 {"ca", "ES"}, 1250 {"ch", "MP"}, 1251 {"cpe", "SL"}, 1252 {"cs", "CZ"}, 1253 {"cy", "GB"}, 1254 {"da", "DK"}, 1255 {"de", "DE"}, 1256 {"dv", "MV"}, 1257 {"dz", "BT"}, 1258 {"el", "GR"}, 1259 {"en", "US"}, 1260 {"es", "ES"}, 1261 {"et", "EE"}, 1262 {"eu", "ES"}, 1263 {"fa", "IR"}, 1264 {"fi", "FI"}, 1265 {"fil", "PH"}, 1266 {"fj", "FJ"}, 1267 {"fo", "FO"}, 1268 {"fr", "FR"}, 1269 {"ga", "IE"}, 1270 {"gd", "GB"}, 1271 {"gl", "ES"}, 1272 {"gn", "PY"}, 1273 {"gu", "IN"}, 1274 {"gv", "GB"}, 1275 {"ha", "NG"}, 1276 {"he", "IL"}, 1277 {"hi", "IN"}, 1278 {"ho", "PG"}, 1279 {"hr", "HR"}, 1280 {"ht", "HT"}, 1281 {"hu", "HU"}, 1282 {"hy", "AM"}, 1283 {"id", "ID"}, 1284 {"is", "IS"}, 1285 {"it", "IT"}, 1286 {"ja", "JP"}, 1287 {"ka", "GE"}, 1288 {"kk", "KZ"}, 1289 {"kl", "GL"}, 1290 {"km", "KH"}, 1291 {"kn", "IN"}, 1292 {"ko", "KR"}, 1293 {"kok", "IN"}, 1294 {"ks", "IN"}, 1295 {"ku", "TR"}, 1296 {"ky", "KG"}, 1297 {"la", "VA"}, 1298 {"lb", "LU"}, 1299 {"ln", "CG"}, 1300 {"lo", "LA"}, 1301 {"lt", "LT"}, 1302 {"lv", "LV"}, 1303 {"mai", "IN"}, 1304 {"men", "GN"}, 1305 {"mg", "MG"}, 1306 {"mh", "MH"}, 1307 {"mk", "MK"}, 1308 {"ml", "IN"}, 1309 {"mn", "MN"}, 1310 {"mni", "IN"}, 1311 {"mo", "MD"}, 1312 {"mr", "IN"}, 1313 {"ms", "MY"}, 1314 {"mt", "MT"}, 1315 {"my", "MM"}, 1316 {"na", "NR"}, 1317 {"nb", "NO"}, 1318 {"nd", "ZA"}, 1319 {"ne", "NP"}, 1320 {"niu", "NU"}, 1321 {"nl", "NL"}, 1322 {"nn", "NO"}, 1323 {"no", "NO"}, 1324 {"nr", "ZA"}, 1325 {"nso", "ZA"}, 1326 {"ny", "MW"}, 1327 {"om", "KE"}, 1328 {"or", "IN"}, 1329 {"pa", "IN"}, 1330 {"pau", "PW"}, 1331 {"pl", "PL"}, 1332 {"ps", "PK"}, 1333 {"pt", "BR"}, 1334 {"qu", "PE"}, 1335 {"rn", "BI"}, 1336 {"ro", "RO"}, 1337 {"ru", "RU"}, 1338 {"rw", "RW"}, 1339 {"sd", "IN"}, 1340 {"sg", "CF"}, 1341 {"si", "LK"}, 1342 {"sk", "SK"}, 1343 {"sl", "SI"}, 1344 {"sm", "WS"}, 1345 {"so", "DJ"}, 1346 {"sq", "CS"}, 1347 {"sr", "CS"}, 1348 {"ss", "ZA"}, 1349 {"st", "ZA"}, 1350 {"sv", "SE"}, 1351 {"sw", "KE"}, 1352 {"ta", "IN"}, 1353 {"te", "IN"}, 1354 {"tem", "SL"}, 1355 {"tet", "TL"}, 1356 {"th", "TH"}, 1357 {"ti", "ET"}, 1358 {"tg", "TJ"}, 1359 {"tk", "TM"}, 1360 {"tkl", "TK"}, 1361 {"tvl", "TV"}, 1362 {"tl", "PH"}, 1363 {"tn", "ZA"}, 1364 {"to", "TO"}, 1365 {"tpi", "PG"}, 1366 {"tr", "TR"}, 1367 {"ts", "ZA"}, 1368 {"uk", "UA"}, 1369 {"ur", "IN"}, 1370 {"uz", "UZ"}, 1371 {"ve", "ZA"}, 1372 {"vi", "VN"}, 1373 {"wo", "SN"}, 1374 {"xh", "ZA"}, 1375 {"zh", "CN"}, 1376 {"zh_Hant", "TW"}, 1377 {"zu", "ZA"}, 1378 {"aa", "ET"}, 1379 {"byn", "ER"}, 1380 {"eo", "DE"}, 1381 {"gez", "ET"}, 1382 {"haw", "US"}, 1383 {"iu", "CA"}, 1384 {"kw", "GB"}, 1385 {"sa", "IN"}, 1386 {"sh", "HR"}, 1387 {"sid", "ET"}, 1388 {"syr", "SY"}, 1389 {"tig", "ER"}, 1390 {"tt", "RU"}, 1391 {"wal", "ET"}, }; 1392 static { 1393 for (int i = 0; i < language_territory_hack.length; ++i) { 1394 language_territory_hack_map.put(language_territory_hack[i][0],language_territory_hack[i][1]); 1395 } 1396 } 1397 1398 static final Map<String, String> territory_tzid_hack_map = new HashMap<String, String>(); 1399 static final String[][] territory_tzid_hack = { 1400 {"AQ", "Antarctica/McMurdo"}, 1401 {"AR", "America/Buenos_Aires"}, 1402 {"AU", "Australia/Sydney"}, 1403 {"BR", "America/Sao_Paulo"}, 1404 {"CA", "America/Toronto"}, 1405 {"CD", "Africa/Kinshasa"}, 1406 {"CL", "America/Santiago"}, 1407 {"CN", "Asia/Shanghai"}, 1408 {"EC", "America/Guayaquil"}, 1409 {"ES", "Europe/Madrid"}, 1410 {"GB", "Europe/London"}, 1411 {"GL", "America/Godthab"}, 1412 {"ID", "Asia/Jakarta"}, 1413 {"ML", "Africa/Bamako"}, 1414 {"MX", "America/Mexico_City"}, 1415 {"MY", "Asia/Kuala_Lumpur"}, 1416 {"NZ", "Pacific/Auckland"}, 1417 {"PT", "Europe/Lisbon"}, 1418 {"RU", "Europe/Moscow"}, 1419 {"UA", "Europe/Kiev"}, 1420 {"US", "America/New_York"}, 1421 {"UZ", "Asia/Tashkent"}, 1422 {"PF", "Pacific/Tahiti"}, 1423 {"FM", "Pacific/Kosrae"}, 1424 {"KI", "Pacific/Tarawa"}, 1425 {"KZ", "Asia/Almaty"}, 1426 {"MH", "Pacific/Majuro"}, 1427 {"MN", "Asia/Ulaanbaatar"}, 1428 {"SJ", "Arctic/Longyearbyen"}, 1429 {"UM", "Pacific/Midway"}, 1430 }; 1431 static { 1432 for (int i = 0; i < territory_tzid_hack.length; ++i) { 1433 territory_tzid_hack_map.put(territory_tzid_hack[i][0],territory_tzid_hack[i][1]); 1434 } 1435 } 1436 1437 // Freezable implementation 1438 1439 private volatile boolean frozen; 1440 1441 /** 1442 * @hide draft / provisional / internal are hidden on Android 1443 */ 1444 public boolean isFrozen() { 1445 return frozen; 1446 } 1447 1448 /** 1449 * @hide draft / provisional / internal are hidden on Android 1450 */ 1451 public GlobalizationPreferences freeze() { 1452 frozen = true; 1453 return this; 1454 } 1455 1456 /** 1457 * @hide draft / provisional / internal are hidden on Android 1458 */ 1459 public GlobalizationPreferences cloneAsThawed() { 1460 try { 1461 GlobalizationPreferences result = (GlobalizationPreferences) clone(); 1462 result.frozen = false; 1463 return result; 1464 } catch (CloneNotSupportedException e) { 1465 // will always work 1466 return null; 1467 } 1468 } 1469} 1470 1471