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