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