1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2004-2016, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ******************************************************************************* 8 */ 9 10 package com.ibm.icu.util; 11 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.Map; 18 import java.util.MissingResourceException; 19 import java.util.ResourceBundle; 20 import java.util.Set; 21 import java.util.TreeSet; 22 import java.util.concurrent.ConcurrentHashMap; 23 24 import com.ibm.icu.impl.ICUData; 25 import com.ibm.icu.impl.ICUResourceBundle; 26 import com.ibm.icu.impl.ICUResourceBundleReader; 27 import com.ibm.icu.impl.ResourceBundleWrapper; 28 29 /** 30 * {@icuenhanced java.util.ResourceBundle}.{@icu _usage_} 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 * @stable ICU 3.0 93 * @author ram 94 */ 95 public abstract class UResourceBundle extends ResourceBundle { 96 97 98 /** 99 * {@icu} Creates a resource bundle using the specified base name and locale. 100 * ICU_DATA_CLASS is used as the default root. 101 * @param baseName string containing the name of the data package. 102 * If null the default ICU package name is used. 103 * @param localeName the locale for which a resource bundle is desired 104 * @throws MissingResourceException If no resource bundle for the specified base name 105 * can be found 106 * @return a resource bundle for the given base name and locale 107 * @stable ICU 3.0 108 */ getBundleInstance(String baseName, String localeName)109 public static UResourceBundle getBundleInstance(String baseName, String localeName){ 110 return getBundleInstance(baseName, localeName, ICUResourceBundle.ICU_DATA_CLASS_LOADER, 111 false); 112 } 113 114 /** 115 * {@icu} Creates a resource bundle using the specified base name, locale, and class root. 116 * 117 * @param baseName string containing the name of the data package. 118 * If null the default ICU package name is used. 119 * @param localeName the locale for which a resource bundle is desired 120 * @param root the class object from which to load the resource bundle 121 * @throws MissingResourceException If no resource bundle for the specified base name 122 * can be found 123 * @return a resource bundle for the given base name and locale 124 * @stable ICU 3.0 125 */ getBundleInstance(String baseName, String localeName, ClassLoader root)126 public static UResourceBundle getBundleInstance(String baseName, String localeName, 127 ClassLoader root){ 128 return getBundleInstance(baseName, localeName, root, false); 129 } 130 131 /** 132 * {@icu} Creates a resource bundle using the specified base name, locale, and class 133 * root. 134 * 135 * @param baseName string containing the name of the data package. 136 * If null the default ICU package name is used. 137 * @param localeName the locale for which a resource bundle is desired 138 * @param root the class object from which to load the resource bundle 139 * @param disableFallback Option to disable locale inheritence. 140 * If true the fallback chain will not be built. 141 * @throws MissingResourceException 142 * if no resource bundle for the specified base name can be found 143 * @return a resource bundle for the given base name and locale 144 * @stable ICU 3.0 145 * 146 */ getBundleInstance(String baseName, String localeName, ClassLoader root, boolean disableFallback)147 protected static UResourceBundle getBundleInstance(String baseName, String localeName, 148 ClassLoader root, boolean disableFallback) { 149 return instantiateBundle(baseName, localeName, root, disableFallback); 150 } 151 152 /** 153 * {@icu} Sole constructor. (For invocation by subclass constructors, typically 154 * implicit.) This is public for compatibility with Java, whose compiler 155 * will generate public default constructors for an abstract class. 156 * @stable ICU 3.0 157 */ UResourceBundle()158 public UResourceBundle() { 159 } 160 161 /** 162 * {@icu} Creates a UResourceBundle for the locale specified, from which users can extract 163 * resources by using their corresponding keys. 164 * @param locale specifies the locale for which we want to open the resource. 165 * If null the bundle for default locale is opened. 166 * @return a resource bundle for the given locale 167 * @stable ICU 3.0 168 */ getBundleInstance(ULocale locale)169 public static UResourceBundle getBundleInstance(ULocale locale) { 170 if (locale==null) { 171 locale = ULocale.getDefault(); 172 } 173 return getBundleInstance(ICUData.ICU_BASE_NAME, locale.getBaseName(), 174 ICUResourceBundle.ICU_DATA_CLASS_LOADER, false); 175 } 176 177 /** 178 * {@icu} Creates a UResourceBundle for the default locale and specified base name, 179 * from which users can extract resources by using their corresponding keys. 180 * @param baseName string containing the name of the data package. 181 * If null the default ICU package name is used. 182 * @return a resource bundle for the given base name and default locale 183 * @stable ICU 3.0 184 */ getBundleInstance(String baseName)185 public static UResourceBundle getBundleInstance(String baseName) { 186 if (baseName == null) { 187 baseName = ICUData.ICU_BASE_NAME; 188 } 189 ULocale uloc = ULocale.getDefault(); 190 return getBundleInstance(baseName, uloc.getBaseName(), ICUResourceBundle.ICU_DATA_CLASS_LOADER, 191 false); 192 } 193 194 /** 195 * {@icu} Creates a UResourceBundle for the specified locale and specified base name, 196 * from which users can extract resources by using their corresponding keys. 197 * @param baseName string containing the name of the data package. 198 * If null the default ICU package name is used. 199 * @param locale specifies the locale for which we want to open the resource. 200 * If null the bundle for default locale is opened. 201 * @return a resource bundle for the given base name and locale 202 * @stable ICU 3.0 203 */ 204 getBundleInstance(String baseName, Locale locale)205 public static UResourceBundle getBundleInstance(String baseName, Locale locale) { 206 if (baseName == null) { 207 baseName = ICUData.ICU_BASE_NAME; 208 } 209 ULocale uloc = locale == null ? ULocale.getDefault() : ULocale.forLocale(locale); 210 211 return getBundleInstance(baseName, uloc.getBaseName(), 212 ICUResourceBundle.ICU_DATA_CLASS_LOADER, false); 213 } 214 215 /** 216 * {@icu} Creates a UResourceBundle, from which users can extract resources by using 217 * their corresponding keys. 218 * @param baseName string containing the name of the data package. 219 * If null the default ICU package name is used. 220 * @param locale specifies the locale for which we want to open the resource. 221 * If null the bundle for default locale is opened. 222 * @return a resource bundle for the given base name and locale 223 * @stable ICU 3.0 224 */ getBundleInstance(String baseName, ULocale locale)225 public static UResourceBundle getBundleInstance(String baseName, ULocale locale) { 226 if (baseName == null) { 227 baseName = ICUData.ICU_BASE_NAME; 228 } 229 if (locale == null) { 230 locale = ULocale.getDefault(); 231 } 232 return getBundleInstance(baseName, locale.getBaseName(), 233 ICUResourceBundle.ICU_DATA_CLASS_LOADER, false); 234 } 235 236 /** 237 * {@icu} Creates a UResourceBundle for the specified locale and specified base name, 238 * from which users can extract resources by using their corresponding keys. 239 * @param baseName string containing the name of the data package. 240 * If null the default ICU package name is used. 241 * @param locale specifies the locale for which we want to open the resource. 242 * If null the bundle for default locale is opened. 243 * @param loader the loader to use 244 * @return a resource bundle for the given base name and locale 245 * @stable ICU 3.8 246 */ getBundleInstance(String baseName, Locale locale, ClassLoader loader)247 public static UResourceBundle getBundleInstance(String baseName, Locale locale, 248 ClassLoader loader) { 249 if (baseName == null) { 250 baseName = ICUData.ICU_BASE_NAME; 251 } 252 ULocale uloc = locale == null ? ULocale.getDefault() : ULocale.forLocale(locale); 253 return getBundleInstance(baseName, uloc.getBaseName(), loader, false); 254 } 255 256 /** 257 * {@icu} Creates a UResourceBundle, from which users can extract resources by using 258 * their corresponding keys.<br><br> 259 * Note: Please use this API for loading non-ICU resources. Java security does not 260 * allow loading of resources across jar files. You must provide your class loader 261 * to load the resources 262 * @param baseName string containing the name of the data package. 263 * If null the default ICU package name is used. 264 * @param locale specifies the locale for which we want to open the resource. 265 * If null the bundle for default locale is opened. 266 * @param loader the loader to use 267 * @return a resource bundle for the given base name and locale 268 * @stable ICU 3.8 269 */ getBundleInstance(String baseName, ULocale locale, ClassLoader loader)270 public static UResourceBundle getBundleInstance(String baseName, ULocale locale, 271 ClassLoader loader) { 272 if (baseName == null) { 273 baseName = ICUData.ICU_BASE_NAME; 274 } 275 if (locale == null) { 276 locale = ULocale.getDefault(); 277 } 278 return getBundleInstance(baseName, locale.getBaseName(), loader, false); 279 } 280 281 /** 282 * {@icu} Returns the RFC 3066 conformant locale id of this resource bundle. 283 * This method can be used after a call to getBundleInstance() to 284 * determine whether the resource bundle returned really 285 * corresponds to the requested locale or is a fallback. 286 * 287 * @return the locale of this resource bundle 288 * @stable ICU 3.0 289 */ getULocale()290 public abstract ULocale getULocale(); 291 292 /** 293 * {@icu} Returns the localeID 294 * @return The string representation of the localeID 295 * @stable ICU 3.0 296 */ getLocaleID()297 protected abstract String getLocaleID(); 298 299 /** 300 * {@icu} Returns the base name of the resource bundle 301 * @return The string representation of the base name 302 * @stable ICU 3.0 303 */ getBaseName()304 protected abstract String getBaseName(); 305 306 /** 307 * {@icu} Returns the parent bundle 308 * @return The parent bundle 309 * @stable ICU 3.0 310 */ getParent()311 protected abstract UResourceBundle getParent(); 312 313 314 /** 315 * Returns the locale of this bundle 316 * @return the locale of this resource bundle 317 * @stable ICU 3.0 318 */ 319 @Override getLocale()320 public Locale getLocale(){ 321 return getULocale().toLocale(); 322 } 323 324 private enum RootType { MISSING, ICU, JAVA } 325 326 private static Map<String, RootType> ROOT_CACHE = new ConcurrentHashMap<String, RootType>(); 327 getRootType(String baseName, ClassLoader root)328 private static RootType getRootType(String baseName, ClassLoader root) { 329 RootType rootType = ROOT_CACHE.get(baseName); 330 331 if (rootType == null) { 332 String rootLocale = (baseName.indexOf('.')==-1) ? "root" : ""; 333 try{ 334 ICUResourceBundle.getBundleInstance(baseName, rootLocale, root, true); 335 rootType = RootType.ICU; 336 }catch(MissingResourceException ex){ 337 try{ 338 ResourceBundleWrapper.getBundleInstance(baseName, rootLocale, root, true); 339 rootType = RootType.JAVA; 340 }catch(MissingResourceException e){ 341 //throw away the exception 342 rootType = RootType.MISSING; 343 } 344 } 345 346 ROOT_CACHE.put(baseName, rootType); 347 } 348 349 return rootType; 350 } 351 setRootType(String baseName, RootType rootType)352 private static void setRootType(String baseName, RootType rootType) { 353 ROOT_CACHE.put(baseName, rootType); 354 } 355 356 /** 357 * {@icu} Loads a new resource bundle for the given base name, locale and class loader. 358 * Optionally will disable loading of fallback bundles. 359 * @param baseName string containing the name of the data package. 360 * If null the default ICU package name is used. 361 * @param localeName the locale for which a resource bundle is desired 362 * @param root the class object from which to load the resource bundle 363 * @param disableFallback disables loading of fallback lookup chain 364 * @throws MissingResourceException If no resource bundle for the specified base name 365 * can be found 366 * @return a resource bundle for the given base name and locale 367 * @stable ICU 3.0 368 */ instantiateBundle(String baseName, String localeName, ClassLoader root, boolean disableFallback)369 protected static UResourceBundle instantiateBundle(String baseName, String localeName, 370 ClassLoader root, boolean disableFallback) { 371 RootType rootType = getRootType(baseName, root); 372 373 switch (rootType) { 374 case ICU: 375 return ICUResourceBundle.getBundleInstance(baseName, localeName, root, disableFallback); 376 377 case JAVA: 378 return ResourceBundleWrapper.getBundleInstance(baseName, localeName, root, 379 disableFallback); 380 381 case MISSING: 382 default: 383 UResourceBundle b; 384 try{ 385 b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, 386 disableFallback); 387 setRootType(baseName, RootType.ICU); 388 }catch(MissingResourceException ex){ 389 b = ResourceBundleWrapper.getBundleInstance(baseName, localeName, root, 390 disableFallback); 391 setRootType(baseName, RootType.JAVA); 392 } 393 return b; 394 } 395 } 396 397 /** 398 * {@icu} Returns a binary data item from a binary resource, as a read-only ByteBuffer. 399 * 400 * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL 401 * file. 402 * @see #getIntVector 403 * @see #getInt 404 * @throws MissingResourceException If no resource bundle can be found. 405 * @throws UResourceTypeMismatchException If the resource has a type mismatch. 406 * @stable ICU 3.8 407 */ getBinary()408 public ByteBuffer getBinary() { 409 throw new UResourceTypeMismatchException(""); 410 } 411 412 /** 413 * Returns a string from a string resource type 414 * 415 * @return a string 416 * @see #getBinary() 417 * @see #getIntVector 418 * @see #getInt 419 * @throws MissingResourceException If resource bundle is missing. 420 * @throws UResourceTypeMismatchException If resource bundle has a type mismatch. 421 * @stable ICU 3.8 422 */ getString()423 public String getString() { 424 throw new UResourceTypeMismatchException(""); 425 } 426 427 /** 428 * Returns a string array from a array resource type 429 * 430 * @return a string 431 * @see #getString() 432 * @see #getIntVector 433 * @throws MissingResourceException If resource bundle is missing. 434 * @throws UResourceTypeMismatchException If resource bundle has a type mismatch. 435 * @stable ICU 3.8 436 */ getStringArray()437 public String[] getStringArray() { 438 throw new UResourceTypeMismatchException(""); 439 } 440 441 /** 442 * {@icu} Returns a binary data from a binary resource, as a byte array with a copy 443 * of the bytes from the resource bundle. 444 * 445 * @param ba The byte array to write the bytes to. A null variable is OK. 446 * @return an array of bytes containing the binary data from the resource. 447 * @see #getIntVector 448 * @see #getInt 449 * @throws MissingResourceException If resource bundle is missing. 450 * @throws UResourceTypeMismatchException If resource bundle has a type mismatch. 451 * @stable ICU 3.8 452 */ getBinary(byte[] ba)453 public byte[] getBinary(byte[] ba) { 454 throw new UResourceTypeMismatchException(""); 455 } 456 457 /** 458 * {@icu} Returns a 32 bit integer array from a resource. 459 * 460 * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL file. 461 * @see #getBinary() 462 * @see #getInt 463 * @throws MissingResourceException If resource bundle is missing. 464 * @throws UResourceTypeMismatchException If resource bundle has a type mismatch. 465 * @stable ICU 3.8 466 */ getIntVector()467 public int[] getIntVector() { 468 throw new UResourceTypeMismatchException(""); 469 } 470 471 /** 472 * {@icu} Returns a signed integer from a resource. 473 * 474 * @return an integer value 475 * @see #getIntVector 476 * @see #getBinary() 477 * @throws MissingResourceException If resource bundle is missing. 478 * @throws UResourceTypeMismatchException If resource bundle type mismatch. 479 * @stable ICU 3.8 480 */ getInt()481 public int getInt() { 482 throw new UResourceTypeMismatchException(""); 483 } 484 485 /** 486 * {@icu} Returns a unsigned integer from a resource. 487 * This integer is originally 28 bit and the sign gets propagated. 488 * 489 * @return an integer value 490 * @see #getIntVector 491 * @see #getBinary() 492 * @throws MissingResourceException If resource bundle is missing. 493 * @throws UResourceTypeMismatchException If resource bundle type mismatch. 494 * @stable ICU 3.8 495 */ getUInt()496 public int getUInt() { 497 throw new UResourceTypeMismatchException(""); 498 } 499 500 /** 501 * {@icu} Returns a resource in a given resource that has a given key. 502 * 503 * @param aKey a key associated with the wanted resource 504 * @return a resource bundle object representing the resource 505 * @throws MissingResourceException If resource bundle is missing. 506 * @stable ICU 3.8 507 */ get(String aKey)508 public UResourceBundle get(String aKey) { 509 UResourceBundle obj = findTopLevel(aKey); 510 if (obj == null) { 511 String fullName = ICUResourceBundleReader.getFullName(getBaseName(), getLocaleID()); 512 throw new MissingResourceException( 513 "Can't find resource for bundle " + fullName + ", key " 514 + aKey, this.getClass().getName(), aKey); 515 } 516 return obj; 517 } 518 519 /** 520 * Returns a resource in a given resource that has a given key, or null if the 521 * resource is not found. 522 * 523 * @param aKey the key associated with the wanted resource 524 * @return the resource, or null 525 * @see #get(String) 526 * @internal 527 * @deprecated This API is ICU internal only. 528 */ 529 @Deprecated findTopLevel(String aKey)530 protected UResourceBundle findTopLevel(String aKey) { 531 // NOTE: this only works for top-level resources. For resources at lower 532 // levels, it fails when you fall back to the parent, since you're now 533 // looking at root resources, not at the corresponding nested resource. 534 for (UResourceBundle res = this; res != null; res = res.getParent()) { 535 UResourceBundle obj = res.handleGet(aKey, null, this); 536 if (obj != null) { 537 return obj; 538 } 539 } 540 return null; 541 } 542 543 /** 544 * Returns the string in a given resource at the specified index. 545 * 546 * @param index an index to the wanted string. 547 * @return a string which lives in the resource. 548 * @throws IndexOutOfBoundsException If the index value is out of bounds of accepted values. 549 * @throws UResourceTypeMismatchException If resource bundle type mismatch. 550 * @stable ICU 3.8 551 */ getString(int index)552 public String getString(int index) { 553 ICUResourceBundle temp = (ICUResourceBundle)get(index); 554 if (temp.getType() == STRING) { 555 return temp.getString(); 556 } 557 throw new UResourceTypeMismatchException(""); 558 } 559 560 /** 561 * {@icu} Returns the resource in a given resource at the specified index. 562 * 563 * @param index an index to the wanted resource. 564 * @return the sub resource UResourceBundle object 565 * @throws IndexOutOfBoundsException If the index value is out of bounds of accepted values. 566 * @throws MissingResourceException If the resource bundle is missing. 567 * @stable ICU 3.8 568 */ get(int index)569 public UResourceBundle get(int index) { 570 UResourceBundle obj = handleGet(index, null, this); 571 if (obj == null) { 572 obj = getParent(); 573 if (obj != null) { 574 obj = obj.get(index); 575 } 576 if (obj == null) 577 throw new MissingResourceException( 578 "Can't find resource for bundle " 579 + this.getClass().getName() + ", key " 580 + getKey(), this.getClass().getName(), getKey()); 581 } 582 return obj; 583 } 584 585 /** 586 * Returns a resource in a given resource that has a given index, or null if the 587 * resource is not found. 588 * 589 * @param index the index of the resource 590 * @return the resource, or null 591 * @see #get(int) 592 * @internal 593 * @deprecated This API is ICU internal only. 594 */ 595 @Deprecated findTopLevel(int index)596 protected UResourceBundle findTopLevel(int index) { 597 // NOTE: this _barely_ works for top-level resources. For resources at lower 598 // levels, it fails when you fall back to the parent, since you're now 599 // looking at root resources, not at the corresponding nested resource. 600 // Not only that, but unless the indices correspond 1-to-1, the index will 601 // lose meaning. Essentially this only works if the child resource arrays 602 // are prefixes of their parent arrays. 603 for (UResourceBundle res = this; res != null; res = res.getParent()) { 604 UResourceBundle obj = res.handleGet(index, null, this); 605 if (obj != null) { 606 return obj; 607 } 608 } 609 return null; 610 } 611 612 /** 613 * Returns the keys in this bundle as an enumeration 614 * @return an enumeration containing key strings, 615 * which is empty if this is not a bundle or a table resource 616 * @stable ICU 3.8 617 */ 618 @Override getKeys()619 public Enumeration<String> getKeys() { 620 return Collections.enumeration(keySet()); 621 } 622 623 /** 624 * Returns a Set of all keys contained in this ResourceBundle and its parent bundles. 625 * @return a Set of all keys contained in this ResourceBundle and its parent bundles, 626 * which is empty if this is not a bundle or a table resource 627 * @internal 628 * @deprecated This API is ICU internal only. 629 */ 630 @Override 631 @Deprecated keySet()632 public Set<String> keySet() { 633 // TODO: Java 6 ResourceBundle has keySet() which calls handleKeySet() 634 // and caches the results. 635 // When we upgrade to Java 6, we still need to check for isTopLevelResource(). 636 // Keep the else branch as is. The if body should just return super.keySet(). 637 // Remove then-redundant caching of the keys. 638 Set<String> keys = null; 639 ICUResourceBundle icurb = null; 640 if(isTopLevelResource() && this instanceof ICUResourceBundle) { 641 // We do not cache the top-level keys in this base class so that 642 // not every string/int/binary... resource has to have a keys cache field. 643 icurb = (ICUResourceBundle)this; 644 keys = icurb.getTopLevelKeySet(); 645 } 646 if(keys == null) { 647 if(isTopLevelResource()) { 648 TreeSet<String> newKeySet; 649 if(parent == null) { 650 newKeySet = new TreeSet<String>(); 651 } else if(parent instanceof UResourceBundle) { 652 newKeySet = new TreeSet<String>(((UResourceBundle)parent).keySet()); 653 } else { 654 // TODO: Java 6 ResourceBundle has keySet(); use it when we upgrade to Java 6 655 // and remove this else branch. 656 newKeySet = new TreeSet<String>(); 657 Enumeration<String> parentKeys = parent.getKeys(); 658 while(parentKeys.hasMoreElements()) { 659 newKeySet.add(parentKeys.nextElement()); 660 } 661 } 662 newKeySet.addAll(handleKeySet()); 663 keys = Collections.unmodifiableSet(newKeySet); 664 if(icurb != null) { 665 icurb.setTopLevelKeySet(keys); 666 } 667 } else { 668 return handleKeySet(); 669 } 670 } 671 return keys; 672 } 673 674 /** 675 * Returns a Set of the keys contained <i>only</i> in this ResourceBundle. 676 * This does not include further keys from parent bundles. 677 * @return a Set of the keys contained only in this ResourceBundle, 678 * which is empty if this is not a bundle or a table resource 679 * @internal 680 * @deprecated This API is ICU internal only. 681 */ 682 @Override 683 @Deprecated handleKeySet()684 protected Set<String> handleKeySet() { 685 return Collections.emptySet(); 686 } 687 688 /** 689 * {@icu} Returns the size of a resource. Size for scalar types is always 1, and for 690 * vector/table types is the number of child resources. 691 * 692 * <br><b>Note:</b> Integer array is treated as a scalar type. There are no APIs to 693 * access individual members of an integer array. It is always returned as a whole. 694 * @return number of resources in a given resource. 695 * @stable ICU 3.8 696 */ getSize()697 public int getSize() { 698 return 1; 699 } 700 701 /** 702 * {@icu} Returns the type of a resource. 703 * Available types are {@link #INT INT}, {@link #ARRAY ARRAY}, 704 * {@link #BINARY BINARY}, {@link #INT_VECTOR INT_VECTOR}, 705 * {@link #STRING STRING}, {@link #TABLE TABLE}. 706 * 707 * @return type of the given resource. 708 * @stable ICU 3.8 709 */ getType()710 public int getType() { 711 return NONE; 712 } 713 714 /** 715 * {@icu} Return the version number associated with this UResourceBundle as an 716 * VersionInfo object. 717 * @return VersionInfo object containing the version of the bundle 718 * @stable ICU 3.8 719 */ getVersion()720 public VersionInfo getVersion() { 721 return null; 722 } 723 724 /** 725 * {@icu} Returns the iterator which iterates over this 726 * resource bundle 727 * @return UResourceBundleIterator that iterates over the resources in the bundle 728 * @stable ICU 3.8 729 */ getIterator()730 public UResourceBundleIterator getIterator() { 731 return new UResourceBundleIterator(this); 732 } 733 734 /** 735 * {@icu} Returns the key associated with a given resource. Not all the resources have 736 * a key - only those that are members of a table. 737 * @return a key associated to this resource, or null if it doesn't have a key 738 * @stable ICU 3.8 739 */ getKey()740 public String getKey() { 741 return null; 742 } 743 744 /** 745 * {@icu} Resource type constant for "no resource". 746 * @stable ICU 3.8 747 */ 748 public static final int NONE = -1; 749 750 /** 751 * {@icu} Resource type constant for strings. 752 * @stable ICU 3.8 753 */ 754 public static final int STRING = 0; 755 756 /** 757 * {@icu} Resource type constant for binary data. 758 * @stable ICU 3.8 759 */ 760 public static final int BINARY = 1; 761 762 /** 763 * {@icu} Resource type constant for tables of key-value pairs. 764 * @stable ICU 3.8 765 */ 766 public static final int TABLE = 2; 767 768 /** 769 * {@icu} Resource type constant for a single 28-bit integer, interpreted as 770 * signed or unsigned by the getInt() function. 771 * @see #getInt 772 * @stable ICU 3.8 773 */ 774 public static final int INT = 7; 775 776 /** 777 * {@icu} Resource type constant for arrays of resources. 778 * @stable ICU 3.8 779 */ 780 public static final int ARRAY = 8; 781 782 /** 783 * Resource type constant for vectors of 32-bit integers. 784 * @see #getIntVector 785 * @stable ICU 3.8 786 */ 787 public static final int INT_VECTOR = 14; 788 789 //====== protected members ============== 790 791 /** 792 * {@icu} Actual worker method for fetching a resource based on the given key. 793 * Sub classes must override this method if they support resources with keys. 794 * @param aKey the key string of the resource to be fetched 795 * @param aliasesVisited hashtable object to hold references of resources already seen 796 * @param requested the original resource bundle object on which the get method was invoked. 797 * The requested bundle and the bundle on which this method is invoked 798 * are the same, except in the cases where aliases are involved. 799 * @return UResourceBundle a resource associated with the key 800 * @stable ICU 3.8 801 */ handleGet(String aKey, HashMap<String, String> aliasesVisited, UResourceBundle requested)802 protected UResourceBundle handleGet(String aKey, HashMap<String, String> aliasesVisited, 803 UResourceBundle requested) { 804 return null; 805 } 806 807 /** 808 * {@icu} Actual worker method for fetching a resource based on the given index. 809 * Sub classes must override this method if they support arrays of resources. 810 * @param index the index of the resource to be fetched 811 * @param aliasesVisited hashtable object to hold references of resources already seen 812 * @param requested the original resource bundle object on which the get method was invoked. 813 * The requested bundle and the bundle on which this method is invoked 814 * are the same, except in the cases where aliases are involved. 815 * @return UResourceBundle a resource associated with the index 816 * @stable ICU 3.8 817 */ handleGet(int index, HashMap<String, String> aliasesVisited, UResourceBundle requested)818 protected UResourceBundle handleGet(int index, HashMap<String, String> aliasesVisited, 819 UResourceBundle requested) { 820 return null; 821 } 822 823 /** 824 * {@icu} Actual worker method for fetching the array of strings in a resource. 825 * Sub classes must override this method if they support arrays of strings. 826 * @return String[] An array of strings containing strings 827 * @stable ICU 3.8 828 */ handleGetStringArray()829 protected String[] handleGetStringArray() { 830 return null; 831 } 832 833 /** 834 * {@icu} Actual worker method for fetching the keys of resources contained in the resource. 835 * Sub classes must override this method if they support keys and associated resources. 836 * 837 * @return Enumeration An enumeration of all the keys in this resource. 838 * @stable ICU 3.8 839 */ handleGetKeys()840 protected Enumeration<String> handleGetKeys(){ 841 return null; 842 } 843 844 /** 845 * {@inheritDoc} 846 * @stable ICU 3.8 847 */ 848 // this method is declared in ResourceBundle class 849 // so cannot change the signature 850 // Override this method 851 @Override handleGetObject(String aKey)852 protected Object handleGetObject(String aKey) { 853 return handleGetObjectImpl(aKey, this); 854 } 855 856 /** 857 * Override the superclass method 858 */ 859 // To facilitate XPath style aliases we need a way to pass the reference 860 // to requested locale. The only way I could figure out is to implement 861 // the look up logic here. This has a disadvantage that if the client 862 // loads an ICUResourceBundle, calls ResourceBundle.getObject method 863 // with a key that does not exist in the bundle then the lookup is 864 // done twice before throwing a MissingResourceExpection. handleGetObjectImpl(String aKey, UResourceBundle requested)865 private Object handleGetObjectImpl(String aKey, UResourceBundle requested) { 866 Object obj = resolveObject(aKey, requested); 867 if (obj == null) { 868 UResourceBundle parentBundle = getParent(); 869 if (parentBundle != null) { 870 obj = parentBundle.handleGetObjectImpl(aKey, requested); 871 } 872 if (obj == null) 873 throw new MissingResourceException( 874 "Can't find resource for bundle " 875 + this.getClass().getName() + ", key " + aKey, 876 this.getClass().getName(), aKey); 877 } 878 return obj; 879 } 880 881 // Routine for figuring out the type of object to be returned 882 // string or string array resolveObject(String aKey, UResourceBundle requested)883 private Object resolveObject(String aKey, UResourceBundle requested) { 884 if (getType() == STRING) { 885 return getString(); 886 } 887 UResourceBundle obj = handleGet(aKey, null, requested); 888 if (obj != null) { 889 if (obj.getType() == STRING) { 890 return obj.getString(); 891 } 892 try { 893 if (obj.getType() == ARRAY) { 894 return obj.handleGetStringArray(); 895 } 896 } catch (UResourceTypeMismatchException ex) { 897 return obj; 898 } 899 } 900 return obj; 901 } 902 903 /** 904 * Is this a top-level resource, that is, a whole bundle? 905 * @return true if this is a top-level resource 906 * @internal 907 * @deprecated This API is ICU internal only. 908 */ 909 @Deprecated isTopLevelResource()910 protected boolean isTopLevelResource() { 911 return true; 912 } 913 } 914