1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 /* 3 ******************************************************************************* 4 * Copyright (C) 2004-2016, International Business Machines Corporation and 5 * others. All Rights Reserved. 6 ******************************************************************************* 7 */ 8 9 package android.icu.util; 10 11 import java.lang.ref.SoftReference; 12 import java.nio.ByteBuffer; 13 import java.util.Collections; 14 import java.util.Enumeration; 15 import java.util.HashMap; 16 import java.util.Locale; 17 import java.util.MissingResourceException; 18 import java.util.ResourceBundle; 19 import java.util.Set; 20 import java.util.TreeSet; 21 import java.util.concurrent.ConcurrentHashMap; 22 23 import android.icu.impl.ICUCache; 24 import android.icu.impl.ICUResourceBundle; 25 import android.icu.impl.ICUResourceBundleReader; 26 import android.icu.impl.ResourceBundleWrapper; 27 import android.icu.impl.SimpleCache; 28 29 /** 30 * <strong>[icu enhancement]</strong> ICU's replacement for {@link java.util.ResourceBundle}. Methods, fields, and other functionality specific to ICU are labeled '<strong>[icu]</strong>'. 31 * 32 * <p>A class representing a collection of resource information pertaining to a given 33 * locale. A resource bundle provides a way of accessing locale- specific information in a 34 * data file. You create a resource bundle that manages the resources for a given locale 35 * and then ask it for individual resources. 36 * 37 * <p>In ResourceBundle, an object is created and the sub-items are fetched using the 38 * getString and getObject methods. In UResourceBundle, each individual element of a 39 * resource is a resource by itself. 40 * 41 * <p>Resource bundles in ICU are currently defined using text files that conform to the 42 * following <a 43 * href="http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt">BNF 44 * definition</a>. More on resource bundle concepts and syntax can be found in the <a 45 * href="http://www.icu-project.org/userguide/ResourceManagement.html">Users Guide</a>. 46 * 47 * <p>The packaging of ICU *.res files can be of two types 48 * ICU4C: 49 * <pre> 50 * root.res 51 * | 52 * -------- 53 * | | 54 * fr.res en.res 55 * | 56 * -------- 57 * | | 58 * fr_CA.res fr_FR.res 59 * </pre> 60 * JAVA/JDK: 61 * <pre> 62 * LocaleElements.res 63 * | 64 * ------------------- 65 * | | 66 * LocaleElements_fr.res LocaleElements_en.res 67 * | 68 * --------------------------- 69 * | | 70 * LocaleElements_fr_CA.res LocaleElements_fr_FR.res 71 * </pre> 72 * 73 * Depending on the organization of your resources, the syntax to getBundleInstance will 74 * change. To open ICU style organization use: 75 * 76 * <pre> 77 * UResourceBundle bundle = 78 * UResourceBundle.getBundleInstance("com/mycompany/resources", 79 * "en_US", myClassLoader); 80 * </pre> 81 * To open Java/JDK style organization use: 82 * <pre> 83 * UResourceBundle bundle = 84 * UResourceBundle.getBundleInstance("com.mycompany.resources.LocaleElements", 85 * "en_US", myClassLoader); 86 * </pre> 87 * 88 * <p>Note: Please use pass a class loader for loading non-ICU resources. Java security does not 89 * allow loading of resources across jar files. You must provide your class loader 90 * to load the resources 91 92 * @author ram 93 * @hide Only a subset of ICU is exposed in Android 94 */ 95 public abstract class UResourceBundle extends ResourceBundle { 96 97 98 /** 99 * <strong>[icu]</strong> Creates a resource bundle using the specified base name and locale. 100 * ICU_DATA_CLASS is used as the default root. 101 * @param baseName the base name of the resource bundle, a fully qualified class name 102 * @param localeName the locale for which a resource bundle is desired 103 * @throws MissingResourceException If no resource bundle for the specified base name 104 * can be found 105 * @return a resource bundle for the given base name and locale 106 */ 107 public static UResourceBundle getBundleInstance(String baseName, String localeName){ 108 return getBundleInstance(baseName, localeName, ICUResourceBundle.ICU_DATA_CLASS_LOADER, 109 false); 110 } 111 112 /** 113 * <strong>[icu]</strong> Creates a resource bundle using the specified base name, locale, and class root. 114 * 115 * @param baseName the base name of the resource bundle, a fully qualified class name 116 * @param localeName the locale for which a resource bundle is desired 117 * @param root the class object from which to load the resource bundle 118 * @throws MissingResourceException If no resource bundle for the specified base name 119 * can be found 120 * @return a resource bundle for the given base name and locale 121 */ 122 public static UResourceBundle getBundleInstance(String baseName, String localeName, 123 ClassLoader root){ 124 return getBundleInstance(baseName, localeName, root, false); 125 } 126 127 /** 128 * <strong>[icu]</strong> Creates a resource bundle using the specified base name, locale, and class 129 * root. 130 * 131 * @param baseName the base name of the resource bundle, a fully qualified class name 132 * @param localeName the locale for which a resource bundle is desired 133 * @param root the class object from which to load the resource bundle 134 * @param disableFallback Option to disable locale inheritence. 135 * If true the fallback chain will not be built. 136 * @throws MissingResourceException 137 * if no resource bundle for the specified base name can be found 138 * @return a resource bundle for the given base name and locale 139 * 140 */ 141 protected static UResourceBundle getBundleInstance(String baseName, String localeName, 142 ClassLoader root, boolean disableFallback) { 143 return instantiateBundle(baseName, localeName, root, disableFallback); 144 } 145 146 /** 147 * <strong>[icu]</strong> Sole constructor. (For invocation by subclass constructors, typically 148 * implicit.) This is public for compatibility with Java, whose compiler 149 * will generate public default constructors for an abstract class. 150 */ 151 public UResourceBundle() { 152 } 153 154 /** 155 * <strong>[icu]</strong> Creates a UResourceBundle for the locale specified, from which users can extract 156 * resources by using their corresponding keys. 157 * @param locale specifies the locale for which we want to open the resource. 158 * If null the bundle for default locale is opened. 159 * @return a resource bundle for the given locale 160 */ 161 public static UResourceBundle getBundleInstance(ULocale locale) { 162 if (locale==null) { 163 locale = ULocale.getDefault(); 164 } 165 return getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale.toString(), 166 ICUResourceBundle.ICU_DATA_CLASS_LOADER, false); 167 } 168 169 /** 170 * <strong>[icu]</strong> Creates a UResourceBundle for the default locale and specified base name, 171 * from which users can extract resources by using their corresponding keys. 172 * @param baseName specifies the locale for which we want to open the resource. 173 * If null the bundle for default locale is opened. 174 * @return a resource bundle for the given base name and default locale 175 */ 176 public static UResourceBundle getBundleInstance(String baseName) { 177 if (baseName == null) { 178 baseName = ICUResourceBundle.ICU_BASE_NAME; 179 } 180 ULocale uloc = ULocale.getDefault(); 181 return getBundleInstance(baseName, uloc.toString(), ICUResourceBundle.ICU_DATA_CLASS_LOADER, 182 false); 183 } 184 185 /** 186 * <strong>[icu]</strong> Creates a UResourceBundle for the specified locale and specified base name, 187 * from which users can extract resources by using their corresponding keys. 188 * @param baseName specifies the locale for which we want to open the resource. 189 * If null the bundle for default locale is opened. 190 * @param locale specifies the locale for which we want to open the resource. 191 * If null the bundle for default locale is opened. 192 * @return a resource bundle for the given base name and locale 193 */ 194 195 public static UResourceBundle getBundleInstance(String baseName, Locale locale) { 196 if (baseName == null) { 197 baseName = ICUResourceBundle.ICU_BASE_NAME; 198 } 199 ULocale uloc = locale == null ? ULocale.getDefault() : ULocale.forLocale(locale); 200 201 return getBundleInstance(baseName, uloc.toString(), ICUResourceBundle.ICU_DATA_CLASS_LOADER, 202 false); 203 } 204 205 /** 206 * <strong>[icu]</strong> Creates a UResourceBundle, from which users can extract resources by using 207 * their corresponding keys. 208 * @param baseName string containing the name of the data package. 209 * If null the default ICU package name is used. 210 * @param locale specifies the locale for which we want to open the resource. 211 * If null the bundle for default locale is opened. 212 * @return a resource bundle for the given base name and locale 213 */ 214 public static UResourceBundle getBundleInstance(String baseName, ULocale locale) { 215 if (baseName == null) { 216 baseName = ICUResourceBundle.ICU_BASE_NAME; 217 } 218 if (locale == null) { 219 locale = ULocale.getDefault(); 220 } 221 return getBundleInstance(baseName, locale.toString(), 222 ICUResourceBundle.ICU_DATA_CLASS_LOADER, false); 223 } 224 225 /** 226 * <strong>[icu]</strong> Creates a UResourceBundle for the specified locale and specified base name, 227 * from which users can extract resources by using their corresponding keys. 228 * @param baseName specifies the locale for which we want to open the resource. 229 * If null the bundle for default locale is opened. 230 * @param locale specifies the locale for which we want to open the resource. 231 * If null the bundle for default locale is opened. 232 * @param loader the loader to use 233 * @return a resource bundle for the given base name and locale 234 */ 235 public static UResourceBundle getBundleInstance(String baseName, Locale locale, 236 ClassLoader loader) { 237 if (baseName == null) { 238 baseName = ICUResourceBundle.ICU_BASE_NAME; 239 } 240 ULocale uloc = locale == null ? ULocale.getDefault() : ULocale.forLocale(locale); 241 return getBundleInstance(baseName, uloc.toString(), loader, false); 242 } 243 244 /** 245 * <strong>[icu]</strong> Creates a UResourceBundle, from which users can extract resources by using 246 * their corresponding keys.<br><br> 247 * Note: Please use this API for loading non-ICU resources. Java security does not 248 * allow loading of resources across jar files. You must provide your class loader 249 * to load the resources 250 * @param baseName string containing the name of the data package. 251 * If null the default ICU package name is used. 252 * @param locale specifies the locale for which we want to open the resource. 253 * If null the bundle for default locale is opened. 254 * @param loader the loader to use 255 * @return a resource bundle for the given base name and locale 256 */ 257 public static UResourceBundle getBundleInstance(String baseName, ULocale locale, 258 ClassLoader loader) { 259 if (baseName == null) { 260 baseName = ICUResourceBundle.ICU_BASE_NAME; 261 } 262 if (locale == null) { 263 locale = ULocale.getDefault(); 264 } 265 return getBundleInstance(baseName, locale.toString(), loader, false); 266 } 267 268 /** 269 * <strong>[icu]</strong> Returns the RFC 3066 conformant locale id of this resource bundle. 270 * This method can be used after a call to getBundleInstance() to 271 * determine whether the resource bundle returned really 272 * corresponds to the requested locale or is a fallback. 273 * 274 * @return the locale of this resource bundle 275 */ 276 public abstract ULocale getULocale(); 277 278 /** 279 * <strong>[icu]</strong> Returns the localeID 280 * @return The string representation of the localeID 281 */ 282 protected abstract String getLocaleID(); 283 284 /** 285 * <strong>[icu]</strong> Returns the base name of the resource bundle 286 * @return The string representation of the base name 287 */ 288 protected abstract String getBaseName(); 289 290 /** 291 * <strong>[icu]</strong> Returns the parent bundle 292 * @return The parent bundle 293 */ 294 protected abstract UResourceBundle getParent(); 295 296 297 /** 298 * Returns the locale of this bundle 299 * @return the locale of this resource bundle 300 */ 301 public Locale getLocale(){ 302 return getULocale().toLocale(); 303 } 304 305 // Cache for ResourceBundle instantiation 306 private static ICUCache<ResourceCacheKey, UResourceBundle> BUNDLE_CACHE = 307 new SimpleCache<ResourceCacheKey, UResourceBundle>(); 308 309 /** 310 * @deprecated This API is ICU internal only. 311 * @hide draft / provisional / internal are hidden on Android 312 */ 313 @Deprecated 314 public static void resetBundleCache() { 315 /* 316 * A HACK!!!!! 317 * Currently if a resourcebundle with fallback turned ON is added to the cache 318 * and then a getBundleInstance() is called for a bundle with fallback turned OFF 319 * it will actually search the cache for any bundle of the same locale 320 * regaurdless of fallback status. This method has been created so that if 321 * The calling method KNOWS that instances of the other fallback state may be in the 322 * cache, the calling method may call this method to clear out the cache. 323 * 324 */ 325 //TODO figure a way around this method(see method comment) 326 BUNDLE_CACHE = new SimpleCache<ResourceCacheKey, UResourceBundle>(); 327 } 328 329 /** 330 * Method used by subclasses to add a resource bundle object to the managed 331 * cache. Works like a putIfAbsent(): If the cache already contains a matching 332 * bundle, that one will be retained and returned. 333 * @deprecated This API is ICU internal only. 334 * @hide draft / provisional / internal are hidden on Android 335 */ 336 @Deprecated 337 protected static UResourceBundle addToCache(String fullName, ULocale defaultLocale, UResourceBundle b) { 338 synchronized(cacheKey){ 339 cacheKey.setKeyValues(fullName, defaultLocale); 340 UResourceBundle cachedBundle = BUNDLE_CACHE.get(cacheKey); 341 if (cachedBundle != null) { 342 return cachedBundle; 343 } 344 BUNDLE_CACHE.put((ResourceCacheKey)cacheKey.clone(), b); 345 return b; 346 } 347 } 348 349 /** 350 * Method used by sub classes to load a resource bundle object from the managed cache 351 * @deprecated This API is ICU internal only. 352 * @hide draft / provisional / internal are hidden on Android 353 */ 354 @Deprecated 355 protected static UResourceBundle loadFromCache(String fullName, ULocale defaultLocale) { 356 synchronized(cacheKey){ 357 cacheKey.setKeyValues(fullName, defaultLocale); 358 return BUNDLE_CACHE.get(cacheKey); 359 } 360 } 361 362 /** 363 * Key used for cached resource bundles. The key checks 364 * the resource name, the class root, and the default 365 * locale to determine if the resource is a match to the 366 * requested one. The root may be null, but the 367 * searchName and the default locale must have a non-null value. 368 * Note that the default locale may change over time, and 369 * lookup should always be based on the current default 370 * locale (if at all). 371 */ 372 private static final class ResourceCacheKey implements Cloneable { 373 private String searchName; 374 private ULocale defaultLocale; 375 private int hashCodeCache; 376 ///CLOVER:OFF 377 public boolean equals(Object other) { 378 if (other == null) { 379 return false; 380 } 381 if (this == other) { 382 return true; 383 } 384 try { 385 final ResourceCacheKey otherEntry = (ResourceCacheKey) other; 386 //quick check to see if they are not equal 387 if (hashCodeCache != otherEntry.hashCodeCache) { 388 return false; 389 } 390 //are the names the same? 391 if (!searchName.equals(otherEntry.searchName)) { 392 return false; 393 } 394 // are the default locales the same? 395 if (defaultLocale == null) { 396 if (otherEntry.defaultLocale != null) { 397 return false; 398 } 399 } else { 400 if (!defaultLocale.equals(otherEntry.defaultLocale)) { 401 return false; 402 } 403 } 404 return true; 405 } catch (NullPointerException e) { 406 return false; 407 } catch (ClassCastException e) { 408 return false; 409 } 410 } 411 412 public int hashCode() { 413 return hashCodeCache; 414 } 415 416 public Object clone() { 417 try { 418 return super.clone(); 419 } catch (CloneNotSupportedException e) { 420 //this should never happen 421 throw new ICUCloneNotSupportedException(e); 422 } 423 } 424 425 ///CLOVER:ON 426 private synchronized void setKeyValues(String searchName, ULocale defaultLocale) { 427 this.searchName = searchName; 428 hashCodeCache = searchName.hashCode(); 429 this.defaultLocale = defaultLocale; 430 if (defaultLocale != null) { 431 hashCodeCache ^= defaultLocale.hashCode(); 432 } 433 } 434 /*private void clear() { 435 setKeyValues(null, "", null); 436 }*/ 437 } 438 439 private static final ResourceCacheKey cacheKey = new ResourceCacheKey(); 440 441 private static final int ROOT_MISSING = 0; 442 private static final int ROOT_ICU = 1; 443 private static final int ROOT_JAVA = 2; 444 445 private static SoftReference<ConcurrentHashMap<String, Integer>> ROOT_CACHE = 446 new SoftReference<ConcurrentHashMap<String, Integer>>(new ConcurrentHashMap<String, Integer>()); 447 448 private static int getRootType(String baseName, ClassLoader root) { 449 ConcurrentHashMap<String, Integer> m = null; 450 Integer rootType; 451 452 m = ROOT_CACHE.get(); 453 if (m == null) { 454 synchronized(UResourceBundle.class) { 455 m = ROOT_CACHE.get(); 456 if (m == null) { 457 m = new ConcurrentHashMap<String, Integer>(); 458 ROOT_CACHE = new SoftReference<ConcurrentHashMap<String, Integer>>(m); 459 } 460 } 461 } 462 463 rootType = m.get(baseName); 464 465 if (rootType == null) { 466 String rootLocale = (baseName.indexOf('.')==-1) ? "root" : ""; 467 int rt = ROOT_MISSING; // value set on success 468 try{ 469 ICUResourceBundle.getBundleInstance(baseName, rootLocale, root, true); 470 rt = ROOT_ICU; 471 }catch(MissingResourceException ex){ 472 try{ 473 ResourceBundleWrapper.getBundleInstance(baseName, rootLocale, root, true); 474 rt = ROOT_JAVA; 475 }catch(MissingResourceException e){ 476 //throw away the exception 477 } 478 } 479 480 rootType = Integer.valueOf(rt); 481 m.putIfAbsent(baseName, rootType); 482 } 483 484 return rootType.intValue(); 485 } 486 487 private static void setRootType(String baseName, int rootType) { 488 Integer rt = Integer.valueOf(rootType); 489 ConcurrentHashMap<String, Integer> m = null; 490 491 m = ROOT_CACHE.get(); 492 if (m == null) { 493 synchronized(UResourceBundle.class) { 494 m = ROOT_CACHE.get(); 495 if (m == null) { 496 m = new ConcurrentHashMap<String, Integer>(); 497 ROOT_CACHE = new SoftReference<ConcurrentHashMap<String, Integer>>(m); 498 } 499 } 500 } 501 502 m.put(baseName, rt); 503 } 504 505 /** 506 * <strong>[icu]</strong> Loads a new resource bundle for the given base name, locale and class loader. 507 * Optionally will disable loading of fallback bundles. 508 * @param baseName the base name of the resource bundle, a fully qualified class name 509 * @param localeName the locale for which a resource bundle is desired 510 * @param root the class object from which to load the resource bundle 511 * @param disableFallback disables loading of fallback lookup chain 512 * @throws MissingResourceException If no resource bundle for the specified base name 513 * can be found 514 * @return a resource bundle for the given base name and locale 515 */ 516 protected static UResourceBundle instantiateBundle(String baseName, String localeName, 517 ClassLoader root, boolean disableFallback) { 518 UResourceBundle b = null; 519 int rootType = getRootType(baseName, root); 520 521 ULocale defaultLocale = ULocale.getDefault(); 522 523 switch (rootType) 524 { 525 case ROOT_ICU: 526 if(disableFallback) { 527 String fullName = ICUResourceBundleReader.getFullName(baseName, localeName); 528 b = loadFromCache(fullName, defaultLocale); 529 if (b == null) { 530 b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, 531 disableFallback); 532 } 533 } else { 534 b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, 535 disableFallback); 536 } 537 538 return b; 539 540 case ROOT_JAVA: 541 return ResourceBundleWrapper.getBundleInstance(baseName, localeName, root, 542 disableFallback); 543 544 default: 545 try{ 546 b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, 547 disableFallback); 548 setRootType(baseName, ROOT_ICU); 549 }catch(MissingResourceException ex){ 550 b = ResourceBundleWrapper.getBundleInstance(baseName, localeName, root, 551 disableFallback); 552 setRootType(baseName, ROOT_JAVA); 553 } 554 return b; 555 } 556 } 557 558 /** 559 * <strong>[icu]</strong> Returns a binary data item from a binary resource, as a read-only ByteBuffer. 560 * 561 * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL 562 * file. 563 * @see #getIntVector 564 * @see #getInt 565 * @throws MissingResourceException If no resource bundle can be found. 566 * @throws UResourceTypeMismatchException If the resource has a type mismatch. 567 */ 568 public ByteBuffer getBinary() { 569 throw new UResourceTypeMismatchException(""); 570 } 571 572 /** 573 * Returns a string from a string resource type 574 * 575 * @return a string 576 * @see #getBinary() 577 * @see #getIntVector 578 * @see #getInt 579 * @throws MissingResourceException If resource bundle is missing. 580 * @throws UResourceTypeMismatchException If resource bundle has a type mismatch. 581 */ 582 public String getString() { 583 throw new UResourceTypeMismatchException(""); 584 } 585 586 /** 587 * Returns a string array from a array resource type 588 * 589 * @return a string 590 * @see #getString() 591 * @see #getIntVector 592 * @throws MissingResourceException If resource bundle is missing. 593 * @throws UResourceTypeMismatchException If resource bundle has a type mismatch. 594 */ 595 public String[] getStringArray() { 596 throw new UResourceTypeMismatchException(""); 597 } 598 599 /** 600 * <strong>[icu]</strong> Returns a binary data from a binary resource, as a byte array with a copy 601 * of the bytes from the resource bundle. 602 * 603 * @param ba The byte array to write the bytes to. A null variable is OK. 604 * @return an array of bytes containing the binary data from the resource. 605 * @see #getIntVector 606 * @see #getInt 607 * @throws MissingResourceException If resource bundle is missing. 608 * @throws UResourceTypeMismatchException If resource bundle has a type mismatch. 609 */ 610 public byte[] getBinary(byte[] ba) { 611 throw new UResourceTypeMismatchException(""); 612 } 613 614 /** 615 * <strong>[icu]</strong> Returns a 32 bit integer array from a resource. 616 * 617 * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL file. 618 * @see #getBinary() 619 * @see #getInt 620 * @throws MissingResourceException If resource bundle is missing. 621 * @throws UResourceTypeMismatchException If resource bundle has a type mismatch. 622 */ 623 public int[] getIntVector() { 624 throw new UResourceTypeMismatchException(""); 625 } 626 627 /** 628 * <strong>[icu]</strong> Returns a signed integer from a resource. 629 * 630 * @return an integer value 631 * @see #getIntVector 632 * @see #getBinary() 633 * @throws MissingResourceException If resource bundle is missing. 634 * @throws UResourceTypeMismatchException If resource bundle type mismatch. 635 */ 636 public int getInt() { 637 throw new UResourceTypeMismatchException(""); 638 } 639 640 /** 641 * <strong>[icu]</strong> Returns a unsigned integer from a resource. 642 * This integer is originally 28 bit and the sign gets propagated. 643 * 644 * @return an integer value 645 * @see #getIntVector 646 * @see #getBinary() 647 * @throws MissingResourceException If resource bundle is missing. 648 * @throws UResourceTypeMismatchException If resource bundle type mismatch. 649 */ 650 public int getUInt() { 651 throw new UResourceTypeMismatchException(""); 652 } 653 654 /** 655 * <strong>[icu]</strong> Returns a resource in a given resource that has a given key. 656 * 657 * @param aKey a key associated with the wanted resource 658 * @return a resource bundle object representing the resource 659 * @throws MissingResourceException If resource bundle is missing. 660 */ 661 public UResourceBundle get(String aKey) { 662 UResourceBundle obj = findTopLevel(aKey); 663 if (obj == null) { 664 String fullName = ICUResourceBundleReader.getFullName(getBaseName(), getLocaleID()); 665 throw new MissingResourceException( 666 "Can't find resource for bundle " + fullName + ", key " 667 + aKey, this.getClass().getName(), aKey); 668 } 669 return obj; 670 } 671 672 /** 673 * Returns a resource in a given resource that has a given key, or null if the 674 * resource is not found. 675 * 676 * @param aKey the key associated with the wanted resource 677 * @return the resource, or null 678 * @see #get(String) 679 * @deprecated This API is ICU internal only. 680 * @hide draft / provisional / internal are hidden on Android 681 */ 682 @Deprecated 683 protected UResourceBundle findTopLevel(String aKey) { 684 // NOTE: this only works for top-level resources. For resources at lower 685 // levels, it fails when you fall back to the parent, since you're now 686 // looking at root resources, not at the corresponding nested resource. 687 for (UResourceBundle res = this; res != null; res = res.getParent()) { 688 UResourceBundle obj = res.handleGet(aKey, null, this); 689 if (obj != null) { 690 ((ICUResourceBundle) obj).setLoadingStatus(getLocaleID()); 691 return obj; 692 } 693 } 694 return null; 695 } 696 697 /** 698 * Returns the string in a given resource at the specified index. 699 * 700 * @param index an index to the wanted string. 701 * @return a string which lives in the resource. 702 * @throws IndexOutOfBoundsException If the index value is out of bounds of accepted values. 703 * @throws UResourceTypeMismatchException If resource bundle type mismatch. 704 */ 705 public String getString(int index) { 706 ICUResourceBundle temp = (ICUResourceBundle)get(index); 707 if (temp.getType() == STRING) { 708 return temp.getString(); 709 } 710 throw new UResourceTypeMismatchException(""); 711 } 712 713 /** 714 * <strong>[icu]</strong> Returns the resource in a given resource at the specified index. 715 * 716 * @param index an index to the wanted resource. 717 * @return the sub resource UResourceBundle object 718 * @throws IndexOutOfBoundsException If the index value is out of bounds of accepted values. 719 * @throws MissingResourceException If the resource bundle is missing. 720 */ 721 public UResourceBundle get(int index) { 722 UResourceBundle obj = handleGet(index, null, this); 723 if (obj == null) { 724 obj = (ICUResourceBundle) getParent(); 725 if (obj != null) { 726 obj = obj.get(index); 727 } 728 if (obj == null) 729 throw new MissingResourceException( 730 "Can't find resource for bundle " 731 + this.getClass().getName() + ", key " 732 + getKey(), this.getClass().getName(), getKey()); 733 } 734 ((ICUResourceBundle)obj).setLoadingStatus(getLocaleID()); 735 return obj; 736 } 737 738 /** 739 * Returns a resource in a given resource that has a given index, or null if the 740 * resource is not found. 741 * 742 * @param index the index of the resource 743 * @return the resource, or null 744 * @see #get(int) 745 * @deprecated This API is ICU internal only. 746 * @hide draft / provisional / internal are hidden on Android 747 */ 748 @Deprecated 749 protected UResourceBundle findTopLevel(int index) { 750 // NOTE: this _barely_ works for top-level resources. For resources at lower 751 // levels, it fails when you fall back to the parent, since you're now 752 // looking at root resources, not at the corresponding nested resource. 753 // Not only that, but unless the indices correspond 1-to-1, the index will 754 // lose meaning. Essentially this only works if the child resource arrays 755 // are prefixes of their parent arrays. 756 for (UResourceBundle res = this; res != null; res = res.getParent()) { 757 UResourceBundle obj = res.handleGet(index, null, this); 758 if (obj != null) { 759 ((ICUResourceBundle) obj).setLoadingStatus(getLocaleID()); 760 return obj; 761 } 762 } 763 return null; 764 } 765 766 /** 767 * Returns the keys in this bundle as an enumeration 768 * @return an enumeration containing key strings, 769 * which is empty if this is not a bundle or a table resource 770 */ 771 public Enumeration<String> getKeys() { 772 return Collections.enumeration(keySet()); 773 } 774 775 /** 776 * Returns a Set of all keys contained in this ResourceBundle and its parent bundles. 777 * @return a Set of all keys contained in this ResourceBundle and its parent bundles, 778 * which is empty if this is not a bundle or a table resource 779 * @deprecated This API is ICU internal only. 780 * @hide draft / provisional / internal are hidden on Android 781 */ 782 @Deprecated 783 public Set<String> keySet() { 784 // TODO: Java 6 ResourceBundle has keySet() which calls handleKeySet() 785 // and caches the results. 786 // When we upgrade to Java 6, we still need to check for isTopLevelResource(). 787 // Keep the else branch as is. The if body should just return super.keySet(). 788 // Remove then-redundant caching of the keys. 789 Set<String> keys = null; 790 ICUResourceBundle icurb = null; 791 if(isTopLevelResource() && this instanceof ICUResourceBundle) { 792 // We do not cache the top-level keys in this base class so that 793 // not every string/int/binary... resource has to have a keys cache field. 794 icurb = (ICUResourceBundle)this; 795 keys = icurb.getTopLevelKeySet(); 796 } 797 if(keys == null) { 798 if(isTopLevelResource()) { 799 TreeSet<String> newKeySet; 800 if(parent == null) { 801 newKeySet = new TreeSet<String>(); 802 } else if(parent instanceof UResourceBundle) { 803 newKeySet = new TreeSet<String>(((UResourceBundle)parent).keySet()); 804 } else { 805 // TODO: Java 6 ResourceBundle has keySet(); use it when we upgrade to Java 6 806 // and remove this else branch. 807 newKeySet = new TreeSet<String>(); 808 Enumeration<String> parentKeys = parent.getKeys(); 809 while(parentKeys.hasMoreElements()) { 810 newKeySet.add(parentKeys.nextElement()); 811 } 812 } 813 newKeySet.addAll(handleKeySet()); 814 keys = Collections.unmodifiableSet(newKeySet); 815 if(icurb != null) { 816 icurb.setTopLevelKeySet(keys); 817 } 818 } else { 819 return handleKeySet(); 820 } 821 } 822 return keys; 823 } 824 825 /** 826 * Returns a Set of the keys contained <i>only</i> in this ResourceBundle. 827 * This does not include further keys from parent bundles. 828 * @return a Set of the keys contained only in this ResourceBundle, 829 * which is empty if this is not a bundle or a table resource 830 * @deprecated This API is ICU internal only. 831 * @hide draft / provisional / internal are hidden on Android 832 */ 833 @Deprecated 834 protected Set<String> handleKeySet() { 835 return Collections.emptySet(); 836 } 837 838 /** 839 * <strong>[icu]</strong> Returns the size of a resource. Size for scalar types is always 1, and for 840 * vector/table types is the number of child resources. 841 * 842 * <br><b>Note:</b> Integer array is treated as a scalar type. There are no APIs to 843 * access individual members of an integer array. It is always returned as a whole. 844 * @return number of resources in a given resource. 845 */ 846 public int getSize() { 847 return 1; 848 } 849 850 /** 851 * <strong>[icu]</strong> Returns the type of a resource. 852 * Available types are {@link #INT INT}, {@link #ARRAY ARRAY}, 853 * {@link #BINARY BINARY}, {@link #INT_VECTOR INT_VECTOR}, 854 * {@link #STRING STRING}, {@link #TABLE TABLE}. 855 * 856 * @return type of the given resource. 857 */ 858 public int getType() { 859 return NONE; 860 } 861 862 /** 863 * <strong>[icu]</strong> Return the version number associated with this UResourceBundle as an 864 * VersionInfo object. 865 * @return VersionInfo object containing the version of the bundle 866 */ 867 public VersionInfo getVersion() { 868 return null; 869 } 870 871 /** 872 * <strong>[icu]</strong> Returns the iterator which iterates over this 873 * resource bundle 874 * @return UResourceBundleIterator that iterates over the resources in the bundle 875 */ 876 public UResourceBundleIterator getIterator() { 877 return new UResourceBundleIterator(this); 878 } 879 880 /** 881 * <strong>[icu]</strong> Returns the key associated with a given resource. Not all the resources have 882 * a key - only those that are members of a table. 883 * @return a key associated to this resource, or null if it doesn't have a key 884 */ 885 public String getKey() { 886 return null; 887 } 888 889 /** 890 * <strong>[icu]</strong> Resource type constant for "no resource". 891 */ 892 public static final int NONE = -1; 893 894 /** 895 * <strong>[icu]</strong> Resource type constant for strings. 896 */ 897 public static final int STRING = 0; 898 899 /** 900 * <strong>[icu]</strong> Resource type constant for binary data. 901 */ 902 public static final int BINARY = 1; 903 904 /** 905 * <strong>[icu]</strong> Resource type constant for tables of key-value pairs. 906 */ 907 public static final int TABLE = 2; 908 909 /** 910 * <strong>[icu]</strong> Resource type constant for a single 28-bit integer, interpreted as 911 * signed or unsigned by the getInt() function. 912 * @see #getInt 913 */ 914 public static final int INT = 7; 915 916 /** 917 * <strong>[icu]</strong> Resource type constant for arrays of resources. 918 */ 919 public static final int ARRAY = 8; 920 921 /** 922 * Resource type constant for vectors of 32-bit integers. 923 * @see #getIntVector 924 */ 925 public static final int INT_VECTOR = 14; 926 927 //====== protected members ============== 928 929 /** 930 * <strong>[icu]</strong> Actual worker method for fetching a resource based on the given key. 931 * Sub classes must override this method if they support resources with keys. 932 * @param aKey the key string of the resource to be fetched 933 * @param aliasesVisited hashtable object to hold references of resources already seen 934 * @param requested the original resource bundle object on which the get method was invoked. 935 * The requested bundle and the bundle on which this method is invoked 936 * are the same, except in the cases where aliases are involved. 937 * @return UResourceBundle a resource associated with the key 938 */ 939 protected UResourceBundle handleGet(String aKey, HashMap<String, String> aliasesVisited, 940 UResourceBundle requested) { 941 return null; 942 } 943 944 /** 945 * <strong>[icu]</strong> Actual worker method for fetching a resource based on the given index. 946 * Sub classes must override this method if they support arrays of resources. 947 * @param index the index of the resource to be fetched 948 * @param aliasesVisited hashtable object to hold references of resources already seen 949 * @param requested the original resource bundle object on which the get method was invoked. 950 * The requested bundle and the bundle on which this method is invoked 951 * are the same, except in the cases where aliases are involved. 952 * @return UResourceBundle a resource associated with the index 953 */ 954 protected UResourceBundle handleGet(int index, HashMap<String, String> aliasesVisited, 955 UResourceBundle requested) { 956 return null; 957 } 958 959 /** 960 * <strong>[icu]</strong> Actual worker method for fetching the array of strings in a resource. 961 * Sub classes must override this method if they support arrays of strings. 962 * @return String[] An array of strings containing strings 963 */ 964 protected String[] handleGetStringArray() { 965 return null; 966 } 967 968 /** 969 * <strong>[icu]</strong> Actual worker method for fetching the keys of resources contained in the resource. 970 * Sub classes must override this method if they support keys and associated resources. 971 * 972 * @return Enumeration An enumeration of all the keys in this resource. 973 */ 974 protected Enumeration<String> handleGetKeys(){ 975 return null; 976 } 977 978 /** 979 * {@inheritDoc} 980 */ 981 // this method is declared in ResourceBundle class 982 // so cannot change the signature 983 // Override this method 984 protected Object handleGetObject(String aKey) { 985 return handleGetObjectImpl(aKey, this); 986 } 987 988 /** 989 * Override the superclass method 990 */ 991 // To facilitate XPath style aliases we need a way to pass the reference 992 // to requested locale. The only way I could figure out is to implement 993 // the look up logic here. This has a disadvantage that if the client 994 // loads an ICUResourceBundle, calls ResourceBundle.getObject method 995 // with a key that does not exist in the bundle then the lookup is 996 // done twice before throwing a MissingResourceExpection. 997 private Object handleGetObjectImpl(String aKey, UResourceBundle requested) { 998 Object obj = resolveObject(aKey, requested); 999 if (obj == null) { 1000 UResourceBundle parentBundle = getParent(); 1001 if (parentBundle != null) { 1002 obj = parentBundle.handleGetObjectImpl(aKey, requested); 1003 } 1004 if (obj == null) 1005 throw new MissingResourceException( 1006 "Can't find resource for bundle " 1007 + this.getClass().getName() + ", key " + aKey, 1008 this.getClass().getName(), aKey); 1009 } 1010 return obj; 1011 } 1012 1013 // Routine for figuring out the type of object to be returned 1014 // string or string array 1015 private Object resolveObject(String aKey, UResourceBundle requested) { 1016 if (getType() == STRING) { 1017 return getString(); 1018 } 1019 UResourceBundle obj = handleGet(aKey, null, requested); 1020 if (obj != null) { 1021 if (obj.getType() == STRING) { 1022 return obj.getString(); 1023 } 1024 try { 1025 if (obj.getType() == ARRAY) { 1026 return obj.handleGetStringArray(); 1027 } 1028 } catch (UResourceTypeMismatchException ex) { 1029 return obj; 1030 } 1031 } 1032 return obj; 1033 } 1034 1035 /** 1036 * This method is for setting the loading status of the resource. 1037 * The status is analogous to the warning status in ICU4C. 1038 * @deprecated This API is ICU internal only. 1039 * @hide draft / provisional / internal are hidden on Android 1040 */ 1041 @Deprecated 1042 protected abstract void setLoadingStatus(int newStatus); 1043 1044 /** 1045 * Is this a top-level resource, that is, a whole bundle? 1046 * @return true if this is a top-level resource 1047 * @deprecated This API is ICU internal only. 1048 * @hide draft / provisional / internal are hidden on Android 1049 */ 1050 @Deprecated 1051 protected boolean isTopLevelResource() { 1052 return true; 1053 } 1054 } 1055