1/* GENERATED SOURCE. DO NOT MODIFY. */ 2// © 2016 and later: Unicode, Inc. and others. 3// License & terms of use: http://www.unicode.org/copyright.html#License 4/* 5 ******************************************************************************* 6 * Copyright (C) 2009-2016, International Business Machines Corporation and 7 * others. All Rights Reserved. 8 ******************************************************************************* 9 */ 10package android.icu.impl; 11 12import java.util.ArrayList; 13import java.util.Collections; 14import java.util.Comparator; 15import java.util.HashMap; 16import java.util.HashSet; 17import java.util.Iterator; 18import java.util.List; 19import java.util.Locale; 20import java.util.Map; 21import java.util.Map.Entry; 22import java.util.MissingResourceException; 23import java.util.Set; 24 25import android.icu.impl.CurrencyData.CurrencyDisplayInfo; 26import android.icu.impl.locale.AsciiUtil; 27import android.icu.lang.UCharacter; 28import android.icu.lang.UScript; 29import android.icu.text.BreakIterator; 30import android.icu.text.CaseMap; 31import android.icu.text.DisplayContext; 32import android.icu.text.DisplayContext.Type; 33import android.icu.text.LocaleDisplayNames; 34import android.icu.util.ULocale; 35import android.icu.util.UResourceBundle; 36 37/** 38 * @hide Only a subset of ICU is exposed in Android 39 */ 40public class LocaleDisplayNamesImpl extends LocaleDisplayNames { 41 private final ULocale locale; 42 private final DialectHandling dialectHandling; 43 private final DisplayContext capitalization; 44 private final DisplayContext nameLength; 45 private final DisplayContext substituteHandling; 46 private final DataTable langData; 47 private final DataTable regionData; 48 // Compiled SimpleFormatter patterns. 49 private final String separatorFormat; 50 private final String format; 51 private final String keyTypeFormat; 52 private final char formatOpenParen; 53 private final char formatReplaceOpenParen; 54 private final char formatCloseParen; 55 private final char formatReplaceCloseParen; 56 private final CurrencyDisplayInfo currencyDisplayInfo; 57 58 private static final Cache cache = new Cache(); 59 60 /** 61 * Capitalization context usage types for locale display names 62 */ 63 private enum CapitalizationContextUsage { 64 LANGUAGE, 65 SCRIPT, 66 TERRITORY, 67 VARIANT, 68 KEY, 69 KEYVALUE 70 } 71 /** 72 * Capitalization transforms. For each usage type, indicates whether to titlecase for 73 * the context specified in capitalization (which we know at construction time). 74 */ 75 private boolean[] capitalizationUsage = null; 76 /** 77 * Map from resource key to CapitalizationContextUsage value 78 */ 79 private static final Map<String, CapitalizationContextUsage> contextUsageTypeMap; 80 static { 81 contextUsageTypeMap=new HashMap<String, CapitalizationContextUsage>(); 82 contextUsageTypeMap.put("languages", CapitalizationContextUsage.LANGUAGE); 83 contextUsageTypeMap.put("script", CapitalizationContextUsage.SCRIPT); 84 contextUsageTypeMap.put("territory", CapitalizationContextUsage.TERRITORY); 85 contextUsageTypeMap.put("variant", CapitalizationContextUsage.VARIANT); 86 contextUsageTypeMap.put("key", CapitalizationContextUsage.KEY); 87 contextUsageTypeMap.put("keyValue", CapitalizationContextUsage.KEYVALUE); 88 } 89 /** 90 * BreakIterator to use for capitalization 91 */ 92 private transient BreakIterator capitalizationBrkIter = null; 93 94 private static final CaseMap.Title TO_TITLE_WHOLE_STRING_NO_LOWERCASE = 95 CaseMap.toTitle().wholeString().noLowercase(); 96 97 private static String toTitleWholeStringNoLowercase(ULocale locale, String s) { 98 return TO_TITLE_WHOLE_STRING_NO_LOWERCASE.apply( 99 locale.toLocale(), null, s, new StringBuilder(), null).toString(); 100 } 101 102 public static LocaleDisplayNames getInstance(ULocale locale, DialectHandling dialectHandling) { 103 synchronized (cache) { 104 return cache.get(locale, dialectHandling); 105 } 106 } 107 108 public static LocaleDisplayNames getInstance(ULocale locale, DisplayContext... contexts) { 109 synchronized (cache) { 110 return cache.get(locale, contexts); 111 } 112 } 113 114 private final class CapitalizationContextSink extends UResource.Sink { 115 boolean hasCapitalizationUsage = false; 116 117 @Override 118 public void put(UResource.Key key, UResource.Value value, boolean noFallback) { 119 UResource.Table contextsTable = value.getTable(); 120 for (int i = 0; contextsTable.getKeyAndValue(i, key, value); ++i) { 121 122 CapitalizationContextUsage usage = contextUsageTypeMap.get(key.toString()); 123 if (usage == null) { continue; }; 124 125 int[] intVector = value.getIntVector(); 126 if (intVector.length < 2) { continue; } 127 128 int titlecaseInt = (capitalization == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU) 129 ? intVector[0] : intVector[1]; 130 if (titlecaseInt == 0) { continue; } 131 132 capitalizationUsage[usage.ordinal()] = true; 133 hasCapitalizationUsage = true; 134 } 135 } 136 } 137 138 public LocaleDisplayNamesImpl(ULocale locale, DialectHandling dialectHandling) { 139 this(locale, (dialectHandling==DialectHandling.STANDARD_NAMES)? DisplayContext.STANDARD_NAMES: DisplayContext.DIALECT_NAMES, 140 DisplayContext.CAPITALIZATION_NONE); 141 } 142 143 public LocaleDisplayNamesImpl(ULocale locale, DisplayContext... contexts) { 144 DialectHandling dialectHandling = DialectHandling.STANDARD_NAMES; 145 DisplayContext capitalization = DisplayContext.CAPITALIZATION_NONE; 146 DisplayContext nameLength = DisplayContext.LENGTH_FULL; 147 DisplayContext substituteHandling = DisplayContext.SUBSTITUTE; 148 for (DisplayContext contextItem : contexts) { 149 switch (contextItem.type()) { 150 case DIALECT_HANDLING: 151 dialectHandling = (contextItem.value()==DisplayContext.STANDARD_NAMES.value())? 152 DialectHandling.STANDARD_NAMES: DialectHandling.DIALECT_NAMES; 153 break; 154 case CAPITALIZATION: 155 capitalization = contextItem; 156 break; 157 case DISPLAY_LENGTH: 158 nameLength = contextItem; 159 break; 160 case SUBSTITUTE_HANDLING: 161 substituteHandling = contextItem; 162 break; 163 default: 164 break; 165 } 166 } 167 168 this.dialectHandling = dialectHandling; 169 this.capitalization = capitalization; 170 this.nameLength = nameLength; 171 this.substituteHandling = substituteHandling; 172 this.langData = LangDataTables.impl.get(locale, substituteHandling == DisplayContext.NO_SUBSTITUTE); 173 this.regionData = RegionDataTables.impl.get(locale, substituteHandling == DisplayContext.NO_SUBSTITUTE); 174 this.locale = ULocale.ROOT.equals(langData.getLocale()) ? regionData.getLocale() : 175 langData.getLocale(); 176 177 // Note, by going through DataTable, this uses table lookup rather than straight lookup. 178 // That should get us the same data, I think. This way we don't have to explicitly 179 // load the bundle again. Using direct lookup didn't seem to make an appreciable 180 // difference in performance. 181 String sep = langData.get("localeDisplayPattern", "separator"); 182 if (sep == null || "separator".equals(sep)) { 183 sep = "{0}, {1}"; 184 } 185 StringBuilder sb = new StringBuilder(); 186 this.separatorFormat = SimpleFormatterImpl.compileToStringMinMaxArguments(sep, sb, 2, 2); 187 188 String pattern = langData.get("localeDisplayPattern", "pattern"); 189 if (pattern == null || "pattern".equals(pattern)) { 190 pattern = "{0} ({1})"; 191 } 192 this.format = SimpleFormatterImpl.compileToStringMinMaxArguments(pattern, sb, 2, 2); 193 if (pattern.contains("(")) { 194 formatOpenParen = '('; 195 formatCloseParen = ')'; 196 formatReplaceOpenParen = '['; 197 formatReplaceCloseParen = ']'; 198 } else { 199 formatOpenParen = '('; 200 formatCloseParen = ')'; 201 formatReplaceOpenParen = '['; 202 formatReplaceCloseParen = ']'; 203 } 204 205 String keyTypePattern = langData.get("localeDisplayPattern", "keyTypePattern"); 206 if (keyTypePattern == null || "keyTypePattern".equals(keyTypePattern)) { 207 keyTypePattern = "{0}={1}"; 208 } 209 this.keyTypeFormat = SimpleFormatterImpl.compileToStringMinMaxArguments( 210 keyTypePattern, sb, 2, 2); 211 212 // Get values from the contextTransforms data if we need them 213 // Also check whether we will need a break iterator (depends on the data) 214 boolean needBrkIter = false; 215 if (capitalization == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU || 216 capitalization == DisplayContext.CAPITALIZATION_FOR_STANDALONE) { 217 capitalizationUsage = new boolean[CapitalizationContextUsage.values().length]; // initialized to all false 218 ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale); 219 CapitalizationContextSink sink = new CapitalizationContextSink(); 220 try { 221 rb.getAllItemsWithFallback("contextTransforms", sink); 222 } 223 catch (MissingResourceException e) { 224 // Silently ignore. Not every locale has contextTransforms. 225 } 226 needBrkIter = sink.hasCapitalizationUsage; 227 } 228 // Get a sentence break iterator if we will need it 229 if (needBrkIter || capitalization == DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) { 230 capitalizationBrkIter = BreakIterator.getSentenceInstance(locale); 231 } 232 233 this.currencyDisplayInfo = CurrencyData.provider.getInstance(locale, false); 234 } 235 236 @Override 237 public ULocale getLocale() { 238 return locale; 239 } 240 241 @Override 242 public DialectHandling getDialectHandling() { 243 return dialectHandling; 244 } 245 246 @Override 247 public DisplayContext getContext(DisplayContext.Type type) { 248 DisplayContext result; 249 switch (type) { 250 case DIALECT_HANDLING: 251 result = (dialectHandling==DialectHandling.STANDARD_NAMES)? DisplayContext.STANDARD_NAMES: DisplayContext.DIALECT_NAMES; 252 break; 253 case CAPITALIZATION: 254 result = capitalization; 255 break; 256 case DISPLAY_LENGTH: 257 result = nameLength; 258 break; 259 case SUBSTITUTE_HANDLING: 260 result = substituteHandling; 261 break; 262 default: 263 result = DisplayContext.STANDARD_NAMES; // hmm, we should do something else here 264 break; 265 } 266 return result; 267 } 268 269 private String adjustForUsageAndContext(CapitalizationContextUsage usage, String name) { 270 if (name != null && name.length() > 0 && UCharacter.isLowerCase(name.codePointAt(0)) && 271 (capitalization==DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || 272 (capitalizationUsage != null && capitalizationUsage[usage.ordinal()]) )) { 273 // Note, won't have capitalizationUsage != null && capitalizationUsage[usage.ordinal()] 274 // unless capitalization is CAPITALIZATION_FOR_UI_LIST_OR_MENU or CAPITALIZATION_FOR_STANDALONE 275 synchronized (this) { 276 if (capitalizationBrkIter == null) { 277 // should only happen when deserializing, etc. 278 capitalizationBrkIter = BreakIterator.getSentenceInstance(locale); 279 } 280 return UCharacter.toTitleCase(locale, name, capitalizationBrkIter, 281 UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT); 282 } 283 } 284 return name; 285 } 286 287 @Override 288 public String localeDisplayName(ULocale locale) { 289 return localeDisplayNameInternal(locale); 290 } 291 292 @Override 293 public String localeDisplayName(Locale locale) { 294 return localeDisplayNameInternal(ULocale.forLocale(locale)); 295 } 296 297 @Override 298 public String localeDisplayName(String localeId) { 299 return localeDisplayNameInternal(new ULocale(localeId)); 300 } 301 302 // TODO: implement use of capitalization 303 private String localeDisplayNameInternal(ULocale locale) { 304 // lang 305 // lang (script, country, variant, keyword=value, ...) 306 // script, country, variant, keyword=value, ... 307 308 String resultName = null; 309 310 String lang = locale.getLanguage(); 311 312 // Empty basename indicates root locale (keywords are ignored for this). 313 // Our data uses 'root' to access display names for the root locale in the 314 // "Languages" table. 315 if (locale.getBaseName().length() == 0) { 316 lang = "root"; 317 } 318 String script = locale.getScript(); 319 String country = locale.getCountry(); 320 String variant = locale.getVariant(); 321 322 boolean hasScript = script.length() > 0; 323 boolean hasCountry = country.length() > 0; 324 boolean hasVariant = variant.length() > 0; 325 326 // always have a value for lang 327 if (dialectHandling == DialectHandling.DIALECT_NAMES) { 328 do { // loop construct is so we can break early out of search 329 if (hasScript && hasCountry) { 330 String langScriptCountry = lang + '_' + script + '_' + country; 331 String result = localeIdName(langScriptCountry); 332 if (result != null && !result.equals(langScriptCountry)) { 333 resultName = result; 334 hasScript = false; 335 hasCountry = false; 336 break; 337 } 338 } 339 if (hasScript) { 340 String langScript = lang + '_' + script; 341 String result = localeIdName(langScript); 342 if (result != null && !result.equals(langScript)) { 343 resultName = result; 344 hasScript = false; 345 break; 346 } 347 } 348 if (hasCountry) { 349 String langCountry = lang + '_' + country; 350 String result = localeIdName(langCountry); 351 if (result != null && !result.equals(langCountry)) { 352 resultName = result; 353 hasCountry = false; 354 break; 355 } 356 } 357 } while (false); 358 } 359 360 if (resultName == null) { 361 String result = localeIdName(lang); 362 if (result == null) { return null; } 363 resultName = result 364 .replace(formatOpenParen, formatReplaceOpenParen) 365 .replace(formatCloseParen, formatReplaceCloseParen); 366 } 367 368 StringBuilder buf = new StringBuilder(); 369 if (hasScript) { 370 // first element, don't need appendWithSep 371 String result = scriptDisplayNameInContext(script, true); 372 if (result == null) { return null; } 373 buf.append(result 374 .replace(formatOpenParen, formatReplaceOpenParen) 375 .replace(formatCloseParen, formatReplaceCloseParen)); 376 } 377 if (hasCountry) { 378 String result = regionDisplayName(country, true); 379 if (result == null) { return null; } 380 appendWithSep(result 381 .replace(formatOpenParen, formatReplaceOpenParen) 382 .replace(formatCloseParen, formatReplaceCloseParen), buf); 383 } 384 if (hasVariant) { 385 String result = variantDisplayName(variant, true); 386 if (result == null) { return null; } 387 appendWithSep(result 388 .replace(formatOpenParen, formatReplaceOpenParen) 389 .replace(formatCloseParen, formatReplaceCloseParen), buf); 390 } 391 392 Iterator<String> keys = locale.getKeywords(); 393 if (keys != null) { 394 while (keys.hasNext()) { 395 String key = keys.next(); 396 String value = locale.getKeywordValue(key); 397 String keyDisplayName = keyDisplayName(key, true); 398 if (keyDisplayName == null) { return null; } 399 keyDisplayName = keyDisplayName 400 .replace(formatOpenParen, formatReplaceOpenParen) 401 .replace(formatCloseParen, formatReplaceCloseParen); 402 String valueDisplayName = keyValueDisplayName(key, value, true); 403 if (valueDisplayName == null) { return null; } 404 valueDisplayName = valueDisplayName 405 .replace(formatOpenParen, formatReplaceOpenParen) 406 .replace(formatCloseParen, formatReplaceCloseParen); 407 if (!valueDisplayName.equals(value)) { 408 appendWithSep(valueDisplayName, buf); 409 } else if (!key.equals(keyDisplayName)) { 410 String keyValue = SimpleFormatterImpl.formatCompiledPattern( 411 keyTypeFormat, keyDisplayName, valueDisplayName); 412 appendWithSep(keyValue, buf); 413 } else { 414 appendWithSep(keyDisplayName, buf) 415 .append("=") 416 .append(valueDisplayName); 417 } 418 } 419 } 420 421 String resultRemainder = null; 422 if (buf.length() > 0) { 423 resultRemainder = buf.toString(); 424 } 425 426 if (resultRemainder != null) { 427 resultName = SimpleFormatterImpl.formatCompiledPattern( 428 format, resultName, resultRemainder); 429 } 430 431 return adjustForUsageAndContext(CapitalizationContextUsage.LANGUAGE, resultName); 432 } 433 434 private String localeIdName(String localeId) { 435 if (nameLength == DisplayContext.LENGTH_SHORT) { 436 String locIdName = langData.get("Languages%short", localeId); 437 if (locIdName != null && !locIdName.equals(localeId)) { 438 return locIdName; 439 } 440 } 441 return langData.get("Languages", localeId); 442 } 443 444 @Override 445 public String languageDisplayName(String lang) { 446 // Special case to eliminate non-languages, which pollute our data. 447 if (lang.equals("root") || lang.indexOf('_') != -1) { 448 return substituteHandling == DisplayContext.SUBSTITUTE ? lang : null; 449 } 450 if (nameLength == DisplayContext.LENGTH_SHORT) { 451 String langName = langData.get("Languages%short", lang); 452 if (langName != null && !langName.equals(lang)) { 453 return adjustForUsageAndContext(CapitalizationContextUsage.LANGUAGE, langName); 454 } 455 } 456 return adjustForUsageAndContext(CapitalizationContextUsage.LANGUAGE, langData.get("Languages", lang)); 457 } 458 459 @Override 460 public String scriptDisplayName(String script) { 461 String str = langData.get("Scripts%stand-alone", script); 462 if (str == null || str.equals(script)) { 463 if (nameLength == DisplayContext.LENGTH_SHORT) { 464 str = langData.get("Scripts%short", script); 465 if (str != null && !str.equals(script)) { 466 return adjustForUsageAndContext(CapitalizationContextUsage.SCRIPT, str); 467 } 468 } 469 str = langData.get("Scripts", script); 470 } 471 return adjustForUsageAndContext(CapitalizationContextUsage.SCRIPT, str); 472 } 473 474 private String scriptDisplayNameInContext(String script, boolean skipAdjust) { 475 if (nameLength == DisplayContext.LENGTH_SHORT) { 476 String scriptName = langData.get("Scripts%short", script); 477 if (scriptName != null && !scriptName.equals(script)) { 478 return skipAdjust? scriptName: adjustForUsageAndContext(CapitalizationContextUsage.SCRIPT, scriptName); 479 } 480 } 481 String scriptName = langData.get("Scripts", script); 482 return skipAdjust? scriptName: adjustForUsageAndContext(CapitalizationContextUsage.SCRIPT, scriptName); 483 } 484 485 @Override 486 public String scriptDisplayNameInContext(String script) { 487 return scriptDisplayNameInContext(script, false); 488 } 489 490 @Override 491 public String scriptDisplayName(int scriptCode) { 492 return scriptDisplayName(UScript.getShortName(scriptCode)); 493 } 494 495 private String regionDisplayName(String region, boolean skipAdjust) { 496 if (nameLength == DisplayContext.LENGTH_SHORT) { 497 String regionName = regionData.get("Countries%short", region); 498 if (regionName != null && !regionName.equals(region)) { 499 return skipAdjust? regionName: adjustForUsageAndContext(CapitalizationContextUsage.TERRITORY, regionName); 500 } 501 } 502 String regionName = regionData.get("Countries", region); 503 return skipAdjust? regionName: adjustForUsageAndContext(CapitalizationContextUsage.TERRITORY, regionName); 504 } 505 506 @Override 507 public String regionDisplayName(String region) { 508 return regionDisplayName(region, false); 509 } 510 511 private String variantDisplayName(String variant, boolean skipAdjust) { 512 // don't have a resource for short variant names 513 String variantName = langData.get("Variants", variant); 514 return skipAdjust? variantName: adjustForUsageAndContext(CapitalizationContextUsage.VARIANT, variantName); 515 } 516 517 @Override 518 public String variantDisplayName(String variant) { 519 return variantDisplayName(variant, false); 520 } 521 522 private String keyDisplayName(String key, boolean skipAdjust) { 523 // don't have a resource for short key names 524 String keyName = langData.get("Keys", key); 525 return skipAdjust? keyName: adjustForUsageAndContext(CapitalizationContextUsage.KEY, keyName); 526 } 527 528 @Override 529 public String keyDisplayName(String key) { 530 return keyDisplayName(key, false); 531 } 532 533 private String keyValueDisplayName(String key, String value, boolean skipAdjust) { 534 String keyValueName = null; 535 536 if (key.equals("currency")) { 537 keyValueName = currencyDisplayInfo.getName(AsciiUtil.toUpperString(value)); 538 if (keyValueName == null) { 539 keyValueName = value; 540 } 541 } else { 542 if (nameLength == DisplayContext.LENGTH_SHORT) { 543 String tmp = langData.get("Types%short", key, value); 544 if (tmp != null && !tmp.equals(value)) { 545 keyValueName = tmp; 546 } 547 } 548 if (keyValueName == null) { 549 keyValueName = langData.get("Types", key, value); 550 } 551 } 552 553 return skipAdjust? keyValueName: adjustForUsageAndContext(CapitalizationContextUsage.KEYVALUE, keyValueName); 554 } 555 556 @Override 557 public String keyValueDisplayName(String key, String value) { 558 return keyValueDisplayName(key, value, false); 559 } 560 561 @Override 562 public List<UiListItem> getUiListCompareWholeItems(Set<ULocale> localeSet, Comparator<UiListItem> comparator) { 563 DisplayContext capContext = getContext(Type.CAPITALIZATION); 564 565 List<UiListItem> result = new ArrayList<UiListItem>(); 566 Map<ULocale,Set<ULocale>> baseToLocales = new HashMap<ULocale,Set<ULocale>>(); 567 ULocale.Builder builder = new ULocale.Builder(); 568 for (ULocale locOriginal : localeSet) { 569 builder.setLocale(locOriginal); // verify well-formed. We do this here so that we consistently throw exception 570 ULocale loc = ULocale.addLikelySubtags(locOriginal); 571 ULocale base = new ULocale(loc.getLanguage()); 572 Set<ULocale> locales = baseToLocales.get(base); 573 if (locales == null) { 574 baseToLocales.put(base, locales = new HashSet<ULocale>()); 575 } 576 locales.add(loc); 577 } 578 for (Entry<ULocale, Set<ULocale>> entry : baseToLocales.entrySet()) { 579 ULocale base = entry.getKey(); 580 Set<ULocale> values = entry.getValue(); 581 if (values.size() == 1) { 582 ULocale locale = values.iterator().next(); 583 result.add(newRow(ULocale.minimizeSubtags(locale, ULocale.Minimize.FAVOR_SCRIPT), capContext)); 584 } else { 585 Set<String> scripts = new HashSet<String>(); 586 Set<String> regions = new HashSet<String>(); 587 // need the follow two steps to make sure that unusual scripts or regions are displayed 588 ULocale maxBase = ULocale.addLikelySubtags(base); 589 scripts.add(maxBase.getScript()); 590 regions.add(maxBase.getCountry()); 591 for (ULocale locale : values) { 592 scripts.add(locale.getScript()); 593 regions.add(locale.getCountry()); 594 } 595 boolean hasScripts = scripts.size() > 1; 596 boolean hasRegions = regions.size() > 1; 597 for (ULocale locale : values) { 598 ULocale.Builder modified = builder.setLocale(locale); 599 if (!hasScripts) { 600 modified.setScript(""); 601 } 602 if (!hasRegions) { 603 modified.setRegion(""); 604 } 605 result.add(newRow(modified.build(), capContext)); 606 } 607 } 608 } 609 Collections.sort(result, comparator); 610 return result; 611 } 612 613 private UiListItem newRow(ULocale modified, DisplayContext capContext) { 614 ULocale minimized = ULocale.minimizeSubtags(modified, ULocale.Minimize.FAVOR_SCRIPT); 615 String tempName = modified.getDisplayName(locale); 616 boolean titlecase = capContext == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU; 617 String nameInDisplayLocale = 618 titlecase ? toTitleWholeStringNoLowercase(locale, tempName) : tempName; 619 tempName = modified.getDisplayName(modified); 620 String nameInSelf = capContext == 621 DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU ? 622 toTitleWholeStringNoLowercase(modified, tempName) : tempName; 623 return new UiListItem(minimized, modified, nameInDisplayLocale, nameInSelf); 624 } 625 626 public static class DataTable { 627 final boolean nullIfNotFound; 628 629 DataTable(boolean nullIfNotFound) { 630 this.nullIfNotFound = nullIfNotFound; 631 } 632 633 ULocale getLocale() { 634 return ULocale.ROOT; 635 } 636 637 String get(String tableName, String code) { 638 return get(tableName, null, code); 639 } 640 641 String get(String tableName, String subTableName, String code) { 642 return nullIfNotFound ? null : code; 643 } 644 } 645 646 static class ICUDataTable extends DataTable { 647 private final ICUResourceBundle bundle; 648 649 public ICUDataTable(String path, ULocale locale, boolean nullIfNotFound) { 650 super(nullIfNotFound); 651 this.bundle = (ICUResourceBundle) UResourceBundle.getBundleInstance( 652 path, locale.getBaseName()); 653 } 654 655 @Override 656 public ULocale getLocale() { 657 return bundle.getULocale(); 658 } 659 660 @Override 661 public String get(String tableName, String subTableName, String code) { 662 return ICUResourceTableAccess.getTableString(bundle, tableName, subTableName, 663 code, nullIfNotFound ? null : code); 664 } 665 } 666 667 static abstract class DataTables { 668 public abstract DataTable get(ULocale locale, boolean nullIfNotFound); 669 public static DataTables load(String className) { 670 try { 671 return (DataTables) Class.forName(className).newInstance(); 672 } catch (Throwable t) { 673 return new DataTables() { 674 @Override 675 public DataTable get(ULocale locale, boolean nullIfNotFound) { 676 return new DataTable(nullIfNotFound); 677 } 678 }; 679 } 680 } 681 } 682 683 static abstract class ICUDataTables extends DataTables { 684 private final String path; 685 686 protected ICUDataTables(String path) { 687 this.path = path; 688 } 689 690 @Override 691 public DataTable get(ULocale locale, boolean nullIfNotFound) { 692 return new ICUDataTable(path, locale, nullIfNotFound); 693 } 694 } 695 696 static class LangDataTables { 697 static final DataTables impl = DataTables.load("android.icu.impl.ICULangDataTables"); 698 } 699 700 static class RegionDataTables { 701 static final DataTables impl = DataTables.load("android.icu.impl.ICURegionDataTables"); 702 } 703 704 public static enum DataTableType { 705 LANG, REGION; 706 } 707 708 public static boolean haveData(DataTableType type) { 709 switch (type) { 710 case LANG: return LangDataTables.impl instanceof ICUDataTables; 711 case REGION: return RegionDataTables.impl instanceof ICUDataTables; 712 default: 713 throw new IllegalArgumentException("unknown type: " + type); 714 } 715 } 716 717 private StringBuilder appendWithSep(String s, StringBuilder b) { 718 if (b.length() == 0) { 719 b.append(s); 720 } else { 721 SimpleFormatterImpl.formatAndReplace(separatorFormat, b, null, b, s); 722 } 723 return b; 724 } 725 726 private static class Cache { 727 private ULocale locale; 728 private DialectHandling dialectHandling; 729 private DisplayContext capitalization; 730 private DisplayContext nameLength; 731 private DisplayContext substituteHandling; 732 private LocaleDisplayNames cache; 733 public LocaleDisplayNames get(ULocale locale, DialectHandling dialectHandling) { 734 if (!(dialectHandling == this.dialectHandling && DisplayContext.CAPITALIZATION_NONE == this.capitalization && 735 DisplayContext.LENGTH_FULL == this.nameLength && DisplayContext.SUBSTITUTE == this.substituteHandling && 736 locale.equals(this.locale))) { 737 this.locale = locale; 738 this.dialectHandling = dialectHandling; 739 this.capitalization = DisplayContext.CAPITALIZATION_NONE; 740 this.nameLength = DisplayContext.LENGTH_FULL; 741 this.substituteHandling = DisplayContext.SUBSTITUTE; 742 this.cache = new LocaleDisplayNamesImpl(locale, dialectHandling); 743 } 744 return cache; 745 } 746 public LocaleDisplayNames get(ULocale locale, DisplayContext... contexts) { 747 DialectHandling dialectHandlingIn = DialectHandling.STANDARD_NAMES; 748 DisplayContext capitalizationIn = DisplayContext.CAPITALIZATION_NONE; 749 DisplayContext nameLengthIn = DisplayContext.LENGTH_FULL; 750 DisplayContext substituteHandling = DisplayContext.SUBSTITUTE; 751 for (DisplayContext contextItem : contexts) { 752 switch (contextItem.type()) { 753 case DIALECT_HANDLING: 754 dialectHandlingIn = (contextItem.value()==DisplayContext.STANDARD_NAMES.value())? 755 DialectHandling.STANDARD_NAMES: DialectHandling.DIALECT_NAMES; 756 break; 757 case CAPITALIZATION: 758 capitalizationIn = contextItem; 759 break; 760 case DISPLAY_LENGTH: 761 nameLengthIn = contextItem; 762 break; 763 case SUBSTITUTE_HANDLING: 764 substituteHandling = contextItem; 765 break; 766 default: 767 break; 768 } 769 } 770 if (!(dialectHandlingIn == this.dialectHandling && capitalizationIn == this.capitalization && 771 nameLengthIn == this.nameLength && substituteHandling == this.substituteHandling && 772 locale.equals(this.locale))) { 773 this.locale = locale; 774 this.dialectHandling = dialectHandlingIn; 775 this.capitalization = capitalizationIn; 776 this.nameLength = nameLengthIn; 777 this.substituteHandling = substituteHandling; 778 this.cache = new LocaleDisplayNamesImpl(locale, contexts); 779 } 780 return cache; 781 } 782 } 783} 784