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