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) 2001-2016, International Business Machines Corporation and 7 * others. All Rights Reserved. 8 ******************************************************************************* 9 */ 10 package android.icu.impl; 11 12 import java.util.Collections; 13 import java.util.Locale; 14 import java.util.Map; 15 import java.util.Set; 16 17 import android.icu.util.ULocale; 18 19 /** 20 * @hide Only a subset of ICU is exposed in Android 21 */ 22 public class ICULocaleService extends ICUService { 23 private ULocale fallbackLocale; 24 private String fallbackLocaleName; 25 26 /** 27 * Construct an ICULocaleService. 28 */ 29 public ICULocaleService() { 30 } 31 32 /** 33 * Construct an ICULocaleService with a name (useful for debugging). 34 */ 35 public ICULocaleService(String name) { 36 super(name); 37 } 38 39 /** 40 * Convenience override for callers using locales. This calls 41 * get(ULocale, int, ULocale[]) with KIND_ANY for kind and null for 42 * actualReturn. 43 */ 44 public Object get(ULocale locale) { 45 return get(locale, LocaleKey.KIND_ANY, null); 46 } 47 48 /** 49 * Convenience override for callers using locales. This calls 50 * get(ULocale, int, ULocale[]) with a null actualReturn. 51 */ 52 public Object get(ULocale locale, int kind) { 53 return get(locale, kind, null); 54 } 55 56 /** 57 * Convenience override for callers using locales. This calls 58 * get(ULocale, int, ULocale[]) with KIND_ANY for kind. 59 */ 60 public Object get(ULocale locale, ULocale[] actualReturn) { 61 return get(locale, LocaleKey.KIND_ANY, actualReturn); 62 } 63 64 /** 65 * Convenience override for callers using locales. This uses 66 * createKey(ULocale.toString(), kind) to create a key, calls getKey, and then 67 * if actualReturn is not null, returns the actualResult from 68 * getKey (stripping any prefix) into a ULocale. 69 */ 70 public Object get(ULocale locale, int kind, ULocale[] actualReturn) { 71 Key key = createKey(locale, kind); 72 if (actualReturn == null) { 73 return getKey(key); 74 } 75 76 String[] temp = new String[1]; 77 Object result = getKey(key, temp); 78 if (result != null) { 79 int n = temp[0].indexOf("/"); 80 if (n >= 0) { 81 temp[0] = temp[0].substring(n+1); 82 } 83 actualReturn[0] = new ULocale(temp[0]); 84 } 85 return result; 86 } 87 88 /** 89 * Convenience override for callers using locales. This calls 90 * registerObject(Object, ULocale, int kind, boolean visible) 91 * passing KIND_ANY for the kind, and true for the visibility. 92 */ 93 public Factory registerObject(Object obj, ULocale locale) { 94 return registerObject(obj, locale, LocaleKey.KIND_ANY, true); 95 } 96 97 /** 98 * Convenience override for callers using locales. This calls 99 * registerObject(Object, ULocale, int kind, boolean visible) 100 * passing KIND_ANY for the kind. 101 */ 102 public Factory registerObject(Object obj, ULocale locale, boolean visible) { 103 return registerObject(obj, locale, LocaleKey.KIND_ANY, visible); 104 } 105 106 /** 107 * Convenience function for callers using locales. This calls 108 * registerObject(Object, ULocale, int kind, boolean visible) 109 * passing true for the visibility. 110 */ 111 public Factory registerObject(Object obj, ULocale locale, int kind) { 112 return registerObject(obj, locale, kind, true); 113 } 114 115 /** 116 * Convenience function for callers using locales. This instantiates 117 * a SimpleLocaleKeyFactory, and registers the factory. 118 */ 119 public Factory registerObject(Object obj, ULocale locale, int kind, boolean visible) { 120 Factory factory = new SimpleLocaleKeyFactory(obj, locale, kind, visible); 121 return registerFactory(factory); 122 } 123 124 /** 125 * Convenience method for callers using locales. This returns the standard 126 * Locale list, built from the Set of visible ids. 127 */ 128 public Locale[] getAvailableLocales() { 129 // TODO make this wrap getAvailableULocales later 130 Set<String> visIDs = getVisibleIDs(); 131 Locale[] locales = new Locale[visIDs.size()]; 132 int n = 0; 133 for (String id : visIDs) { 134 Locale loc = LocaleUtility.getLocaleFromName(id); 135 locales[n++] = loc; 136 } 137 return locales; 138 } 139 140 /** 141 * Convenience method for callers using locales. This returns the standard 142 * ULocale list, built from the Set of visible ids. 143 */ 144 public ULocale[] getAvailableULocales() { 145 Set<String> visIDs = getVisibleIDs(); 146 ULocale[] locales = new ULocale[visIDs.size()]; 147 int n = 0; 148 for (String id : visIDs) { 149 locales[n++] = new ULocale(id); 150 } 151 return locales; 152 } 153 154 /** 155 * A subclass of Key that implements a locale fallback mechanism. 156 * The first locale to search for is the locale provided by the 157 * client, and the fallback locale to search for is the current 158 * default locale. If a prefix is present, the currentDescriptor 159 * includes it before the locale proper, separated by "/". This 160 * is the default key instantiated by ICULocaleService.</p> 161 * 162 * <p>Canonicalization adjusts the locale string so that the 163 * section before the first understore is in lower case, and the rest 164 * is in upper case, with no trailing underscores.</p> 165 */ 166 public static class LocaleKey extends ICUService.Key { 167 private int kind; 168 private int varstart; 169 private String primaryID; 170 private String fallbackID; 171 private String currentID; 172 173 public static final int KIND_ANY = -1; 174 175 /** 176 * Create a LocaleKey with canonical primary and fallback IDs. 177 */ 178 public static LocaleKey createWithCanonicalFallback(String primaryID, String canonicalFallbackID) { 179 return createWithCanonicalFallback(primaryID, canonicalFallbackID, KIND_ANY); 180 } 181 182 /** 183 * Create a LocaleKey with canonical primary and fallback IDs. 184 */ 185 public static LocaleKey createWithCanonicalFallback(String primaryID, String canonicalFallbackID, int kind) { 186 if (primaryID == null) { 187 return null; 188 } 189 String canonicalPrimaryID = ULocale.getName(primaryID); 190 return new LocaleKey(primaryID, canonicalPrimaryID, canonicalFallbackID, kind); 191 } 192 193 /** 194 * Create a LocaleKey with canonical primary and fallback IDs. 195 */ 196 public static LocaleKey createWithCanonical(ULocale locale, String canonicalFallbackID, int kind) { 197 if (locale == null) { 198 return null; 199 } 200 String canonicalPrimaryID = locale.getName(); 201 return new LocaleKey(canonicalPrimaryID, canonicalPrimaryID, canonicalFallbackID, kind); 202 } 203 204 /** 205 * PrimaryID is the user's requested locale string, 206 * canonicalPrimaryID is this string in canonical form, 207 * fallbackID is the current default locale's string in 208 * canonical form. 209 */ 210 protected LocaleKey(String primaryID, String canonicalPrimaryID, String canonicalFallbackID, int kind) { 211 super(primaryID); 212 this.kind = kind; 213 214 if (canonicalPrimaryID == null || canonicalPrimaryID.equalsIgnoreCase("root")) { 215 this.primaryID = ""; 216 this.fallbackID = null; 217 } else { 218 int idx = canonicalPrimaryID.indexOf('@'); 219 if (idx == 4 && canonicalPrimaryID.regionMatches(true, 0, "root", 0, 4)) { 220 this.primaryID = canonicalPrimaryID.substring(4); 221 this.varstart = 0; 222 this.fallbackID = null; 223 } else { 224 this.primaryID = canonicalPrimaryID; 225 this.varstart = idx; 226 227 if (canonicalFallbackID == null || this.primaryID.equals(canonicalFallbackID)) { 228 this.fallbackID = ""; 229 } else { 230 this.fallbackID = canonicalFallbackID; 231 } 232 } 233 } 234 235 this.currentID = varstart == -1 ? this.primaryID : this.primaryID.substring(0, varstart); 236 } 237 238 /** 239 * Return the prefix associated with the kind, or null if the kind is KIND_ANY. 240 */ 241 public String prefix() { 242 return kind == KIND_ANY ? null : Integer.toString(kind()); 243 } 244 245 /** 246 * Return the kind code associated with this key. 247 */ 248 public int kind() { 249 return kind; 250 } 251 252 /** 253 * Return the (canonical) original ID. 254 */ 255 @Override 256 public String canonicalID() { 257 return primaryID; 258 } 259 260 /** 261 * Return the (canonical) current ID, or null if no current id. 262 */ 263 @Override 264 public String currentID() { 265 return currentID; 266 } 267 268 /** 269 * Return the (canonical) current descriptor, or null if no current id. 270 * Includes the keywords, whereas the ID does not include keywords. 271 */ 272 @Override 273 public String currentDescriptor() { 274 String result = currentID(); 275 if (result != null) { 276 StringBuilder buf = new StringBuilder(); // default capacity 16 is usually good enough 277 if (kind != KIND_ANY) { 278 buf.append(prefix()); 279 } 280 buf.append('/'); 281 buf.append(result); 282 if (varstart != -1) { 283 buf.append(primaryID.substring(varstart, primaryID.length())); 284 } 285 result = buf.toString(); 286 } 287 return result; 288 } 289 290 /** 291 * Convenience method to return the locale corresponding to the (canonical) original ID. 292 */ 293 public ULocale canonicalLocale() { 294 return new ULocale(primaryID); 295 } 296 297 /** 298 * Convenience method to return the ulocale corresponding to the (canonical) currentID. 299 */ 300 public ULocale currentLocale() { 301 if (varstart == -1) { 302 return new ULocale(currentID); 303 } else { 304 return new ULocale(currentID + primaryID.substring(varstart)); 305 } 306 } 307 308 /** 309 * If the key has a fallback, modify the key and return true, 310 * otherwise return false.</p> 311 * 312 * <p>First falls back through the primary ID, then through 313 * the fallbackID. The final fallback is "" (root) 314 * unless the primary id was "" (root), in which case 315 * there is no fallback. 316 */ 317 @Override 318 public boolean fallback() { 319 int x = currentID.lastIndexOf('_'); 320 if (x != -1) { 321 while (--x >= 0 && currentID.charAt(x) == '_') { // handle zh__PINYIN 322 } 323 currentID = currentID.substring(0, x+1); 324 return true; 325 } 326 if (fallbackID != null) { 327 currentID = fallbackID; 328 if (fallbackID.length() == 0) { 329 fallbackID = null; 330 } else { 331 fallbackID = ""; 332 } 333 return true; 334 } 335 currentID = null; 336 return false; 337 } 338 339 /** 340 * If a key created from id would eventually fallback to match the 341 * canonical ID of this key, return true. 342 */ 343 @Override 344 public boolean isFallbackOf(String id) { 345 return LocaleUtility.isFallbackOf(canonicalID(), id); 346 } 347 } 348 349 /** 350 * A subclass of Factory that uses LocaleKeys. If 'visible' the 351 * factory reports its IDs. 352 */ 353 public static abstract class LocaleKeyFactory implements Factory { 354 protected final String name; 355 protected final boolean visible; 356 357 public static final boolean VISIBLE = true; 358 public static final boolean INVISIBLE = false; 359 360 /** 361 * Constructor used by subclasses. 362 */ 363 protected LocaleKeyFactory(boolean visible) { 364 this.visible = visible; 365 this.name = null; 366 } 367 368 /** 369 * Constructor used by subclasses. 370 */ 371 protected LocaleKeyFactory(boolean visible, String name) { 372 this.visible = visible; 373 this.name = name; 374 } 375 376 /** 377 * Implement superclass abstract method. This checks the currentID of 378 * the key against the supported IDs, and passes the canonicalLocale and 379 * kind off to handleCreate (which subclasses must implement). 380 */ 381 @Override 382 public Object create(Key key, ICUService service) { 383 if (handlesKey(key)) { 384 LocaleKey lkey = (LocaleKey)key; 385 int kind = lkey.kind(); 386 387 ULocale uloc = lkey.currentLocale(); 388 return handleCreate(uloc, kind, service); 389 } else { 390 // System.out.println("factory: " + this + " did not support id: " + key.currentID()); 391 // System.out.println("supported ids: " + getSupportedIDs()); 392 } 393 return null; 394 } 395 396 protected boolean handlesKey(Key key) { 397 if (key != null) { 398 String id = key.currentID(); 399 Set<String> supported = getSupportedIDs(); 400 return supported.contains(id); 401 } 402 return false; 403 } 404 405 /** 406 * Override of superclass method. 407 */ 408 @Override 409 public void updateVisibleIDs(Map<String, Factory> result) { 410 Set<String> cache = getSupportedIDs(); 411 for (String id : cache) { 412 if (visible) { 413 result.put(id, this); 414 } else { 415 result.remove(id); 416 } 417 } 418 } 419 420 /** 421 * Return a localized name for the locale represented by id. 422 */ 423 @Override 424 public String getDisplayName(String id, ULocale locale) { 425 // assume if the user called this on us, we must have handled some fallback of this id 426 // if (isSupportedID(id)) { 427 if (locale == null) { 428 return id; 429 } 430 ULocale loc = new ULocale(id); 431 return loc.getDisplayName(locale); 432 // } 433 // return null; 434 } 435 436 ///CLOVER:OFF 437 /** 438 * Utility method used by create(Key, ICUService). Subclasses can 439 * implement this instead of create. 440 */ 441 protected Object handleCreate(ULocale loc, int kind, ICUService service) { 442 return null; 443 } 444 ///CLOVER:ON 445 446 /** 447 * Return true if this id is one the factory supports (visible or 448 * otherwise). 449 */ 450 protected boolean isSupportedID(String id) { 451 return getSupportedIDs().contains(id); 452 } 453 454 /** 455 * Return the set of ids that this factory supports (visible or 456 * otherwise). This can be called often and might need to be 457 * cached if it is expensive to create. 458 */ 459 protected Set<String> getSupportedIDs() { 460 return Collections.emptySet(); 461 } 462 463 /** 464 * For debugging. 465 */ 466 @Override 467 public String toString() { 468 StringBuilder buf = new StringBuilder(super.toString()); 469 if (name != null) { 470 buf.append(", name: "); 471 buf.append(name); 472 } 473 buf.append(", visible: "); 474 buf.append(visible); 475 return buf.toString(); 476 } 477 } 478 479 /** 480 * A LocaleKeyFactory that just returns a single object for a kind/locale. 481 */ 482 public static class SimpleLocaleKeyFactory extends LocaleKeyFactory { 483 private final Object obj; 484 private final String id; 485 private final int kind; 486 487 // TODO: remove when we no longer need this 488 public SimpleLocaleKeyFactory(Object obj, ULocale locale, int kind, boolean visible) { 489 this(obj, locale, kind, visible, null); 490 } 491 492 public SimpleLocaleKeyFactory(Object obj, ULocale locale, int kind, boolean visible, String name) { 493 super(visible, name); 494 495 this.obj = obj; 496 this.id = locale.getBaseName(); 497 this.kind = kind; 498 } 499 500 /** 501 * Returns the service object if kind/locale match. Service is not used. 502 */ 503 @Override 504 public Object create(Key key, ICUService service) { 505 if (!(key instanceof LocaleKey)) { 506 return null; 507 } 508 509 LocaleKey lkey = (LocaleKey)key; 510 if (kind != LocaleKey.KIND_ANY && kind != lkey.kind()) { 511 return null; 512 } 513 if (!id.equals(lkey.currentID())) { 514 return null; 515 } 516 517 return obj; 518 } 519 520 @Override 521 protected boolean isSupportedID(String idToCheck) { 522 return this.id.equals(idToCheck); 523 } 524 525 @Override 526 public void updateVisibleIDs(Map<String, Factory> result) { 527 if (visible) { 528 result.put(id, this); 529 } else { 530 result.remove(id); 531 } 532 } 533 534 @Override 535 public String toString() { 536 StringBuilder buf = new StringBuilder(super.toString()); 537 buf.append(", id: "); 538 buf.append(id); 539 buf.append(", kind: "); 540 buf.append(kind); 541 return buf.toString(); 542 } 543 } 544 545 /** 546 * A LocaleKeyFactory that creates a service based on the ICU locale data. 547 * This is a base class for most ICU factories. Subclasses instantiate it 548 * with a constructor that takes a bundle name, which determines the supported 549 * IDs. Subclasses then override handleCreate to create the actual service 550 * object. The default implementation returns a resource bundle. 551 */ 552 public static class ICUResourceBundleFactory extends LocaleKeyFactory { 553 protected final String bundleName; 554 555 /** 556 * Convenience constructor that uses the main ICU bundle name. 557 */ 558 public ICUResourceBundleFactory() { 559 this(ICUData.ICU_BASE_NAME); 560 } 561 562 /** 563 * A service factory based on ICU resource data in resources 564 * with the given name. 565 */ 566 public ICUResourceBundleFactory(String bundleName) { 567 super(true); 568 569 this.bundleName = bundleName; 570 } 571 572 /** 573 * Return the supported IDs. This is the set of all locale names for the bundleName. 574 */ 575 @Override 576 protected Set<String> getSupportedIDs() { 577 return ICUResourceBundle.getFullLocaleNameSet(bundleName, loader()); 578 } 579 580 /** 581 * Override of superclass method. 582 */ 583 @Override 584 public void updateVisibleIDs(Map<String, Factory> result) { 585 Set<String> visibleIDs = ICUResourceBundle.getAvailableLocaleNameSet(bundleName, loader()); // only visible ids 586 for (String id : visibleIDs) { 587 result.put(id, this); 588 } 589 } 590 591 /** 592 * Create the service. The default implementation returns the resource bundle 593 * for the locale, ignoring kind, and service. 594 */ 595 @Override 596 protected Object handleCreate(ULocale loc, int kind, ICUService service) { 597 return ICUResourceBundle.getBundleInstance(bundleName, loc, loader()); 598 } 599 600 protected ClassLoader loader() { 601 return ClassLoaderUtil.getClassLoader(getClass()); 602 } 603 604 @Override 605 public String toString() { 606 return super.toString() + ", bundle: " + bundleName; 607 } 608 } 609 610 /** 611 * Return the name of the current fallback locale. If it has changed since this was 612 * last accessed, the service cache is cleared. 613 */ 614 public String validateFallbackLocale() { 615 ULocale loc = ULocale.getDefault(); 616 if (loc != fallbackLocale) { 617 synchronized (this) { 618 if (loc != fallbackLocale) { 619 fallbackLocale = loc; 620 fallbackLocaleName = loc.getBaseName(); 621 clearServiceCache(); 622 } 623 } 624 } 625 return fallbackLocaleName; 626 } 627 628 @Override 629 public Key createKey(String id) { 630 return LocaleKey.createWithCanonicalFallback(id, validateFallbackLocale()); 631 } 632 633 public Key createKey(String id, int kind) { 634 return LocaleKey.createWithCanonicalFallback(id, validateFallbackLocale(), kind); 635 } 636 637 public Key createKey(ULocale l, int kind) { 638 return LocaleKey.createWithCanonical(l, validateFallbackLocale(), kind); 639 } 640 } 641