2 * *****************************************************************************
\r
3 * Copyright (C) 2005-2010, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 * *****************************************************************************
\r
8 package com.ibm.icu.impl;
\r
10 import java.io.BufferedReader;
\r
11 import java.io.IOException;
\r
12 import java.io.InputStream;
\r
13 import java.io.InputStreamReader;
\r
14 import java.lang.ref.SoftReference;
\r
15 import java.net.URL;
\r
16 import java.util.ArrayList;
\r
17 import java.util.Arrays;
\r
18 import java.util.Collections;
\r
19 import java.util.Enumeration;
\r
20 import java.util.HashMap;
\r
21 import java.util.HashSet;
\r
22 import java.util.List;
\r
23 import java.util.Locale;
\r
24 import java.util.Map;
\r
25 import java.util.MissingResourceException;
\r
26 import java.util.ResourceBundle;
\r
27 import java.util.Set;
\r
28 import java.util.Vector;
\r
29 import java.util.concurrent.ConcurrentHashMap;
\r
31 import com.ibm.icu.impl.URLHandler.URLVisitor;
\r
32 import com.ibm.icu.util.StringTokenizer;
\r
33 import com.ibm.icu.util.ULocale;
\r
34 import com.ibm.icu.util.UResourceBundle;
\r
35 import com.ibm.icu.util.UResourceBundleIterator;
\r
36 import com.ibm.icu.util.VersionInfo;
\r
38 public class ICUResourceBundle extends UResourceBundle {
\r
40 * The data path to be used with getBundleInstance API
\r
42 protected static final String ICU_DATA_PATH = "com/ibm/icu/impl/";
\r
44 * The data path to be used with getBundleInstance API
\r
46 public static final String ICU_BUNDLE = "data/icudt" + VersionInfo.ICU_DATA_VERSION_PATH;
\r
49 * The base name of ICU data to be used with getBundleInstance API
\r
51 public static final String ICU_BASE_NAME = ICU_DATA_PATH + ICU_BUNDLE;
\r
54 * The base name of collation data to be used with getBundleInstance API
\r
56 public static final String ICU_COLLATION_BASE_NAME = ICU_BASE_NAME + "/coll";
\r
59 * The base name of rbbi data to be used with getData API
\r
61 public static final String ICU_BRKITR_NAME = "/brkitr";
\r
64 * The base name of rbbi data to be used with getBundleInstance API
\r
66 public static final String ICU_BRKITR_BASE_NAME = ICU_BASE_NAME + ICU_BRKITR_NAME;
\r
69 * The base name of rbnf data to be used with getBundleInstance API
\r
71 public static final String ICU_RBNF_BASE_NAME = ICU_BASE_NAME + "/rbnf";
\r
74 * The base name of transliterator data to be used with getBundleInstance API
\r
76 public static final String ICU_TRANSLIT_BASE_NAME = ICU_BASE_NAME + "/translit";
\r
78 public static final String ICU_LANG_BASE_NAME = ICU_BASE_NAME + "/lang";
\r
79 public static final String ICU_CURR_BASE_NAME = ICU_BASE_NAME + "/curr";
\r
80 public static final String ICU_REGION_BASE_NAME = ICU_BASE_NAME + "/region";
\r
81 public static final String ICU_ZONE_BASE_NAME = ICU_BASE_NAME + "/zone";
\r
84 * The actual path of the resource
\r
86 protected String resPath;
\r
89 * The class loader constant to be used with getBundleInstance API
\r
91 public static final ClassLoader ICU_DATA_CLASS_LOADER;
\r
93 ClassLoader loader = ICUData.class.getClassLoader();
\r
94 if (loader == null) {
\r
95 loader = Utility.getFallbackClassLoader();
\r
97 ICU_DATA_CLASS_LOADER = loader;
\r
101 * The name of the resource containing the installed locales
\r
103 protected static final String INSTALLED_LOCALES = "InstalledLocales";
\r
105 public static final int FROM_FALLBACK = 1, FROM_ROOT = 2, FROM_DEFAULT = 3, FROM_LOCALE = 4;
\r
107 private int loadingStatus = -1;
\r
109 public void setLoadingStatus(int newStatus) {
\r
110 loadingStatus = newStatus;
\r
113 * Returns the loading status of a particular resource.
\r
115 * @return FROM_FALLBACK if the resource is fetched from fallback bundle
\r
116 * FROM_ROOT if the resource is fetched from root bundle.
\r
117 * FROM_DEFAULT if the resource is fetched from the default locale.
\r
119 public int getLoadingStatus() {
\r
120 return loadingStatus;
\r
123 public void setLoadingStatus(String requestedLocale){
\r
124 String locale = getLocaleID();
\r
125 if(locale.equals("root")) {
\r
126 setLoadingStatus(FROM_ROOT);
\r
127 } else if(locale.equals(requestedLocale)) {
\r
128 setLoadingStatus(FROM_LOCALE);
\r
130 setLoadingStatus(FROM_FALLBACK);
\r
135 * Returns the respath of this bundle
\r
136 * @return the respath of the bundle
\r
138 public String getResPath(){
\r
143 * Returns a functionally equivalent locale, considering keywords as well, for the specified keyword.
\r
144 * @param baseName resource specifier
\r
145 * @param resName top level resource to consider (such as "collations")
\r
146 * @param keyword a particular keyword to consider (such as "collation" )
\r
147 * @param locID The requested locale
\r
148 * @param isAvailable If non-null, 1-element array of fillin parameter that indicates whether the
\r
149 * requested locale was available. The locale is defined as 'available' if it physically
\r
150 * exists within the specified tree and included in 'InstalledLocales'.
\r
151 * @param omitDefault if true, omit keyword and value if default.
\r
152 * 'de_DE\@collation=standard' -> 'de_DE'
\r
153 * @return the locale
\r
154 * @internal ICU 3.0
\r
156 public static final ULocale getFunctionalEquivalent(String baseName, ClassLoader loader,
\r
157 String resName, String keyword, ULocale locID,
\r
158 boolean isAvailable[], boolean omitDefault) {
\r
159 String kwVal = locID.getKeywordValue(keyword);
\r
160 String baseLoc = locID.getBaseName();
\r
161 String defStr = null;
\r
162 ULocale parent = new ULocale(baseLoc);
\r
163 ULocale defLoc = null; // locale where default (found) resource is
\r
164 boolean lookForDefault = false; // true if kwVal needs to be set
\r
165 ULocale fullBase = null; // base locale of found (target) resource
\r
166 int defDepth = 0; // depth of 'default' marker
\r
167 int resDepth = 0; // depth of found resource;
\r
169 if ((kwVal == null) || (kwVal.length() == 0)
\r
170 || kwVal.equals(DEFAULT_TAG)) {
\r
171 kwVal = ""; // default tag is treated as no keyword
\r
172 lookForDefault = true;
\r
175 // Check top level locale first
\r
176 ICUResourceBundle r = null;
\r
178 r = (ICUResourceBundle) UResourceBundle.getBundleInstance(baseName, parent);
\r
179 if (isAvailable != null) {
\r
180 isAvailable[0] = false;
\r
181 ULocale[] availableULocales = getAvailEntry(baseName, loader).getULocaleList();
\r
182 for (int i = 0; i < availableULocales.length; i++) {
\r
183 if (parent.equals(availableULocales[i])) {
\r
184 isAvailable[0] = true;
\r
189 // determine in which locale (if any) the currently relevant 'default' is
\r
192 ICUResourceBundle irb = (ICUResourceBundle) r.get(resName);
\r
193 defStr = irb.getString(DEFAULT_TAG);
\r
194 if (lookForDefault == true) {
\r
196 lookForDefault = false;
\r
198 defLoc = r.getULocale();
\r
199 } catch (MissingResourceException t) {
\r
200 // Ignore error and continue search.
\r
202 if (defLoc == null) {
\r
203 r = (ICUResourceBundle) r.getParent();
\r
206 } while ((r != null) && (defLoc == null));
\r
208 // Now, search for the named resource
\r
209 parent = new ULocale(baseLoc);
\r
210 r = (ICUResourceBundle) UResourceBundle.getBundleInstance(baseName, parent);
\r
211 // determine in which locale (if any) the named resource is located
\r
214 ICUResourceBundle irb = (ICUResourceBundle)r.get(resName);
\r
215 /* UResourceBundle urb = */irb.get(kwVal);
\r
216 fullBase = irb.getULocale();
\r
217 // If the get() completed, we have the full base locale
\r
218 // If we fell back to an ancestor of the old 'default',
\r
219 // we need to re calculate the "default" keyword.
\r
220 if ((fullBase != null) && ((resDepth) > defDepth)) {
\r
221 defStr = irb.getString(DEFAULT_TAG);
\r
222 defLoc = r.getULocale();
\r
223 defDepth = resDepth;
\r
225 } catch (MissingResourceException t) {
\r
228 if (fullBase == null) {
\r
229 r = (ICUResourceBundle) r.getParent();
\r
232 } while ((r != null) && (fullBase == null));
\r
234 if (fullBase == null && // Could not find resource 'kwVal'
\r
235 (defStr != null) && // default was defined
\r
236 !defStr.equals(kwVal)) { // kwVal is not default
\r
237 // couldn't find requested resource. Fall back to default.
\r
238 kwVal = defStr; // Fall back to default.
\r
239 parent = new ULocale(baseLoc);
\r
240 r = (ICUResourceBundle) UResourceBundle.getBundleInstance(baseName, parent);
\r
242 // determine in which locale (if any) the named resource is located
\r
245 ICUResourceBundle irb = (ICUResourceBundle)r.get(resName);
\r
246 UResourceBundle urb = irb.get(kwVal);
\r
248 // if we didn't fail before this..
\r
249 fullBase = r.getULocale();
\r
251 // If the fetched item (urb) is in a different locale than our outer locale (r/fullBase)
\r
252 // then we are in a 'fallback' situation. treat as a missing resource situation.
\r
253 if(!fullBase.toString().equals(urb.getLocale().toString())) {
\r
254 fullBase = null; // fallback condition. Loop and try again.
\r
257 // If we fell back to an ancestor of the old 'default',
\r
258 // we need to re calculate the "default" keyword.
\r
259 if ((fullBase != null) && ((resDepth) > defDepth)) {
\r
260 defStr = irb.getString(DEFAULT_TAG);
\r
261 defLoc = r.getULocale();
\r
262 defDepth = resDepth;
\r
264 } catch (MissingResourceException t) {
\r
265 // Ignore error, continue search.
\r
267 if (fullBase == null) {
\r
268 r = (ICUResourceBundle) r.getParent();
\r
271 } while ((r != null) && (fullBase == null));
\r
274 if (fullBase == null) {
\r
275 throw new MissingResourceException(
\r
276 "Could not find locale containing requested or default keyword.",
\r
277 baseName, keyword + "=" + kwVal);
\r
281 && defStr.equals(kwVal) // if default was requested and
\r
282 && resDepth <= defDepth) { // default was set in same locale or child
\r
283 return fullBase; // Keyword value is default - no keyword needed in locale
\r
285 return new ULocale(fullBase.toString() + "@" + keyword + "=" + kwVal);
\r
290 * Given a tree path and keyword, return a string enumeration of all possible values for that keyword.
\r
291 * @param baseName resource specifier
\r
292 * @param keyword a particular keyword to consider, must match a top level resource name
\r
293 * within the tree. (i.e. "collations")
\r
294 * @internal ICU 3.0
\r
296 public static final String[] getKeywordValues(String baseName, String keyword) {
\r
297 Set<String> keywords = new HashSet<String>();
\r
298 ULocale locales[] = createULocaleList(baseName, ICU_DATA_CLASS_LOADER);
\r
301 for (i = 0; i < locales.length; i++) {
\r
303 UResourceBundle b = UResourceBundle.getBundleInstance(baseName, locales[i]);
\r
304 // downcast to ICUResourceBundle?
\r
305 ICUResourceBundle irb = (ICUResourceBundle) (b.getObject(keyword));
\r
306 Enumeration<String> e = irb.getKeys();
\r
307 while (e.hasMoreElements()) {
\r
308 String s = e.nextElement();
\r
309 if (!DEFAULT_TAG.equals(s)) {
\r
310 // don't add 'default' items
\r
314 } catch (Throwable t) {
\r
315 //System.err.println("Error in - " + new Integer(i).toString()
\r
316 // + " - " + t.toString());
\r
317 // ignore the err - just skip that resource
\r
320 return keywords.toArray(new String[0]);
\r
324 * This method performs multilevel fallback for fetching items from the
\r
325 * bundle e.g: If resource is in the form de__PHONEBOOK{ collations{
\r
326 * default{ "phonebook"} } } If the value of "default" key needs to be
\r
327 * accessed, then do: <code>
\r
328 * UResourceBundle bundle = UResourceBundle.getBundleInstance("de__PHONEBOOK");
\r
329 * ICUResourceBundle result = null;
\r
330 * if(bundle instanceof ICUResourceBundle){
\r
331 * result = ((ICUResourceBundle) bundle).getWithFallback("collations/default");
\r
335 * @param path The path to the required resource key
\r
336 * @return resource represented by the key
\r
337 * @exception MissingResourceException If a resource was not found.
\r
339 public ICUResourceBundle getWithFallback(String path) throws MissingResourceException {
\r
340 ICUResourceBundle result = null;
\r
341 ICUResourceBundle actualBundle = this;
\r
343 // now recurse to pick up sub levels of the items
\r
344 result = findResourceWithFallback(path, actualBundle, null);
\r
346 if (result == null) {
\r
347 throw new MissingResourceException(
\r
348 "Can't find resource for bundle "
\r
349 + this.getClass().getName() + ", key " + getType(),
\r
355 public ICUResourceBundle at(int index) {
\r
356 return (ICUResourceBundle) handleGet(index, null, this);
\r
359 public ICUResourceBundle at(String key) {
\r
360 // don't ever presume the key is an int in disguise, like ResourceArray does.
\r
361 if (this instanceof ICUResourceBundleImpl.ResourceTable) {
\r
362 return (ICUResourceBundle) handleGet(key, null, this);
\r
368 public ICUResourceBundle findTopLevel(int index) {
\r
369 return (ICUResourceBundle) super.findTopLevel(index);
\r
373 public ICUResourceBundle findTopLevel(String aKey) {
\r
374 return (ICUResourceBundle) super.findTopLevel(aKey);
\r
378 * Like getWithFallback, but returns null if the resource is not found instead of
\r
379 * throwing an exception.
\r
380 * @param path the path to the resource
\r
381 * @return the resource, or null
\r
383 public ICUResourceBundle findWithFallback(String path) {
\r
384 return findResourceWithFallback(path, this, null);
\r
387 // will throw type mismatch exception if the resource is not a string
\r
388 public String getStringWithFallback(String path) throws MissingResourceException {
\r
389 return getWithFallback(path).getString();
\r
393 * Return a set of the locale names supported by a collection of resource
\r
396 * @param bundlePrefix the prefix of the resource bundles to use.
\r
398 public static Set<String> getAvailableLocaleNameSet(String bundlePrefix, ClassLoader loader) {
\r
399 return getAvailEntry(bundlePrefix, loader).getLocaleNameSet();
\r
403 * Return a set of all the locale names supported by a collection of
\r
404 * resource bundles.
\r
406 public static Set<String> getFullLocaleNameSet() {
\r
407 return getFullLocaleNameSet(ICU_BASE_NAME, ICU_DATA_CLASS_LOADER);
\r
411 * Return a set of all the locale names supported by a collection of
\r
412 * resource bundles.
\r
414 * @param bundlePrefix the prefix of the resource bundles to use.
\r
416 public static Set<String> getFullLocaleNameSet(String bundlePrefix, ClassLoader loader) {
\r
417 return getAvailEntry(bundlePrefix, loader).getFullLocaleNameSet();
\r
421 * Return a set of the locale names supported by a collection of resource
\r
424 public static Set<String> getAvailableLocaleNameSet() {
\r
425 return getAvailableLocaleNameSet(ICU_BASE_NAME, ICU_DATA_CLASS_LOADER);
\r
429 * Get the set of Locales installed in the specified bundles.
\r
430 * @return the list of available locales
\r
432 public static final ULocale[] getAvailableULocales(String baseName, ClassLoader loader) {
\r
433 return getAvailEntry(baseName, loader).getULocaleList();
\r
437 * Get the set of ULocales installed the base bundle.
\r
438 * @return the list of available locales
\r
440 public static final ULocale[] getAvailableULocales() {
\r
441 return getAvailableULocales(ICU_BASE_NAME, ICU_DATA_CLASS_LOADER);
\r
445 * Get the set of Locales installed in the specified bundles.
\r
446 * @return the list of available locales
\r
448 public static final Locale[] getAvailableLocales(String baseName, ClassLoader loader) {
\r
449 return getAvailEntry(baseName, loader).getLocaleList();
\r
453 * Get the set of Locales installed the base bundle.
\r
454 * @return the list of available locales
\r
456 public static final Locale[] getAvailableLocales() {
\r
457 return getAvailEntry(ICU_BASE_NAME, ICU_DATA_CLASS_LOADER).getLocaleList();
\r
461 * Convert a list of ULocales to a list of Locales. ULocales with a script code will not be converted
\r
462 * since they cannot be represented as a Locale. This means that the two lists will <b>not</b> match
\r
463 * one-to-one, and that the returned list might be shorter than the input list.
\r
464 * @param ulocales a list of ULocales to convert to a list of Locales.
\r
465 * @return the list of converted ULocales
\r
467 public static final Locale[] getLocaleList(ULocale[] ulocales) {
\r
468 ArrayList<Locale> list = new ArrayList<Locale>(ulocales.length);
\r
469 HashSet<Locale> uniqueSet = new HashSet<Locale>();
\r
470 for (int i = 0; i < ulocales.length; i++) {
\r
471 Locale loc = ulocales[i].toLocale();
\r
472 if (!uniqueSet.contains(loc)) {
\r
474 uniqueSet.add(loc);
\r
477 return list.toArray(new Locale[list.size()]);
\r
481 * Returns the locale of this resource bundle. This method can be used after
\r
482 * a call to getBundle() to determine whether the resource bundle returned
\r
483 * really corresponds to the requested locale or is a fallback.
\r
485 * @return the locale of this resource bundle
\r
487 public Locale getLocale() {
\r
488 return getULocale().toLocale();
\r
492 // ========== privates ==========
\r
493 private static final String ICU_RESOURCE_INDEX = "res_index";
\r
495 private static final String DEFAULT_TAG = "default";
\r
497 // Flag for enabling/disabling debugging code
\r
498 private static final boolean DEBUG = ICUDebug.enabled("localedata");
\r
500 // Cache for getAvailableLocales
\r
501 private static SoftReference<Map<String, AvailEntry>> GET_AVAILABLE_CACHE;
\r
502 private static final ULocale[] createULocaleList(String baseName,
\r
503 ClassLoader root) {
\r
504 // the canned list is a subset of all the available .res files, the idea
\r
505 // is we don't export them
\r
506 // all. gotta be a better way to do this, since to add a locale you have
\r
507 // to update this list,
\r
508 // and it's embedded in our binary resources.
\r
509 ICUResourceBundle bundle = (ICUResourceBundle) UResourceBundle.instantiateBundle(baseName, ICU_RESOURCE_INDEX, root, true);
\r
511 bundle = (ICUResourceBundle)bundle.get(INSTALLED_LOCALES);
\r
512 int length = bundle.getSize();
\r
514 ULocale[] locales = new ULocale[length];
\r
515 UResourceBundleIterator iter = bundle.getIterator();
\r
517 while (iter.hasNext()) {
\r
518 String locstr = iter.next().getKey();
\r
519 if (locstr.equals("root")) {
\r
520 locales[i++] = ULocale.ROOT;
\r
522 locales[i++] = new ULocale(locstr);
\r
529 private static final Locale[] createLocaleList(String baseName, ClassLoader loader) {
\r
530 ULocale[] ulocales = getAvailEntry(baseName, loader).getULocaleList();
\r
531 return getLocaleList(ulocales);
\r
534 private static final String[] createLocaleNameArray(String baseName,
\r
535 ClassLoader root) {
\r
536 ICUResourceBundle bundle = (ICUResourceBundle) UResourceBundle.instantiateBundle( baseName, ICU_RESOURCE_INDEX, root, true);
\r
537 bundle = (ICUResourceBundle)bundle.get(INSTALLED_LOCALES);
\r
538 int length = bundle.getSize();
\r
540 String[] locales = new String[length];
\r
541 UResourceBundleIterator iter = bundle.getIterator();
\r
543 while (iter.hasNext()) {
\r
544 String locstr = iter.next(). getKey();
\r
545 if (locstr.equals("root")) {
\r
546 locales[i++] = ULocale.ROOT.toString();
\r
548 locales[i++] = locstr;
\r
555 private static final List<String> createFullLocaleNameArray(
\r
556 final String baseName, final ClassLoader root) {
\r
558 List<String> list = java.security.AccessController
\r
559 .doPrivileged(new java.security.PrivilegedAction<List<String>>() {
\r
560 public List<String> run() {
\r
561 // WebSphere class loader will return null for a raw
\r
562 // directory name without trailing slash
\r
563 String bn = baseName.endsWith("/")
\r
567 List<String> resList = null;
\r
569 // scan available locale resources under the base url first
\r
571 Enumeration<URL> urls = root.getResources(bn);
\r
572 while (urls.hasMoreElements()) {
\r
573 URL url = urls.nextElement();
\r
574 URLHandler handler = URLHandler.get(url);
\r
575 if (handler != null) {
\r
576 final List<String> lst = new ArrayList<String>();
\r
577 URLVisitor v = new URLVisitor() {
\r
578 public void visit(String s) {
\r
579 //TODO: This is ugly hack. We have to figure out how
\r
580 // we can distinguish locale data from others
\r
581 if (s.endsWith(".res")) {
\r
582 String locstr = s.substring(0, s.length() - 4);
\r
583 if (locstr.contains("_") && !locstr.equals("res_index")) {
\r
584 // locale data with country/script contain "_",
\r
585 // except for res_index.res
\r
587 } else if (locstr.length() == 2 || locstr.length() == 3) {
\r
588 // all 2-letter or 3-letter entries are all locale
\r
589 // data at least for now
\r
591 } else if (locstr.equalsIgnoreCase("root")) {
\r
592 // root locale is a special case
\r
593 lst.add(ULocale.ROOT.toString());
\r
598 handler.guide(v, false);
\r
600 if (resList == null) {
\r
601 resList = new ArrayList<String>(lst);
\r
603 resList.addAll(lst);
\r
606 if (DEBUG) System.out.println("handler for " + url + " is null");
\r
609 } catch (IOException e) {
\r
610 if (DEBUG) System.out.println("ouch: " + e.getMessage());
\r
614 if (resList == null) {
\r
615 // look for prebuilt indices next
\r
617 InputStream s = root.getResourceAsStream(bn + ICU_RESOURCE_INDEX + ".txt");
\r
619 resList = new ArrayList<String>();
\r
620 BufferedReader br = new BufferedReader(new InputStreamReader(s, "ASCII"));
\r
622 while ((line = br.readLine()) != null) {
\r
623 if (line.length() != 0 && !line.startsWith("#")) {
\r
624 if (line.equalsIgnoreCase("root")) {
\r
625 resList.add(ULocale.ROOT.toString());
\r
632 } catch (IOException e) {
\r
644 private static Set<String> createFullLocaleNameSet(String baseName, ClassLoader loader) {
\r
645 List<String> list = createFullLocaleNameArray(baseName, loader);
\r
647 if (DEBUG) System.out.println("createFullLocaleNameArray returned null");
\r
648 // Use locale name set as the last resort fallback
\r
649 return createLocaleNameSet(baseName, loader);
\r
651 HashSet<String> set = new HashSet<String>();
\r
653 return Collections.unmodifiableSet(set);
\r
656 private static Set<String> createLocaleNameSet(String baseName, ClassLoader loader) {
\r
658 String[] locales = createLocaleNameArray(baseName, loader);
\r
660 HashSet<String> set = new HashSet<String>();
\r
661 set.addAll(Arrays.asList(locales));
\r
662 return Collections.unmodifiableSet(set);
\r
663 } catch (MissingResourceException e) {
\r
665 System.out.println("couldn't find index for bundleName: " + baseName);
\r
666 Thread.dumpStack();
\r
669 return Collections.emptySet();
\r
673 * Holds the prefix, and lazily creates the Locale[] list or the locale name
\r
676 private static final class AvailEntry {
\r
677 private String prefix;
\r
678 private ClassLoader loader;
\r
679 private ULocale[] ulocales;
\r
680 private Locale[] locales;
\r
681 private Set<String> nameSet;
\r
682 private Set<String> fullNameSet;
\r
684 AvailEntry(String prefix, ClassLoader loader) {
\r
685 this.prefix = prefix;
\r
686 this.loader = loader;
\r
689 ULocale[] getULocaleList() {
\r
690 if (ulocales == null) {
\r
691 ulocales = createULocaleList(prefix, loader);
\r
695 Locale[] getLocaleList() {
\r
696 if (locales == null) {
\r
697 locales = createLocaleList(prefix, loader);
\r
701 Set<String> getLocaleNameSet() {
\r
702 if (nameSet == null) {
\r
703 nameSet = createLocaleNameSet(prefix, loader);
\r
707 Set<String> getFullLocaleNameSet() {
\r
708 if (fullNameSet == null) {
\r
709 fullNameSet = createFullLocaleNameSet(prefix, loader);
\r
711 return fullNameSet;
\r
716 * Stores the locale information in a cache accessed by key (bundle prefix).
\r
717 * The cached objects are AvailEntries. The cache is held by a SoftReference
\r
718 * so it can be GC'd.
\r
720 private static AvailEntry getAvailEntry(String key, ClassLoader loader) {
\r
721 AvailEntry ae = null;
\r
722 Map<String, AvailEntry> lcache = null;
\r
723 if (GET_AVAILABLE_CACHE != null) {
\r
724 lcache = GET_AVAILABLE_CACHE.get();
\r
725 if (lcache != null) {
\r
726 ae = lcache.get(key);
\r
731 ae = new AvailEntry(key, loader);
\r
732 if (lcache == null) {
\r
733 lcache = new HashMap<String, AvailEntry>();
\r
734 lcache.put(key, ae);
\r
735 GET_AVAILABLE_CACHE = new SoftReference<Map<String, AvailEntry>>(lcache);
\r
737 lcache.put(key, ae);
\r
744 protected static final ICUResourceBundle findResourceWithFallback(String path,
\r
745 UResourceBundle actualBundle, UResourceBundle requested) {
\r
746 ICUResourceBundle sub = null;
\r
747 if (requested == null) {
\r
748 requested = actualBundle;
\r
750 while (actualBundle != null) {
\r
751 ICUResourceBundle current = (ICUResourceBundle) actualBundle;
\r
752 if (path.indexOf('/') == -1) { // skip the tokenizer
\r
753 sub = (ICUResourceBundle) current.handleGet(path, null, requested);
\r
759 StringTokenizer st = new StringTokenizer(path, "/");
\r
760 while (st.hasMoreTokens()) {
\r
761 String subKey = st.nextToken();
\r
762 sub = (ICUResourceBundle) current.handleGet(subKey, null, requested);
\r
773 if (((ICUResourceBundle)actualBundle).resPath.length() != 0) {
\r
774 path = ((ICUResourceBundle)actualBundle).resPath + "/" + path;
\r
776 // if not try the parent bundle
\r
777 actualBundle = ((ICUResourceBundle) actualBundle).getParent();
\r
781 sub.setLoadingStatus(((ICUResourceBundle)requested).getLocaleID());
\r
785 public boolean equals(Object other) {
\r
786 if (this == other) {
\r
789 if (other instanceof ICUResourceBundle) {
\r
790 ICUResourceBundle o = (ICUResourceBundle) other;
\r
791 if (getBaseName().equals(o.getBaseName())
\r
792 && getLocaleID().equals(o.getLocaleID())) {
\r
798 // This method is for super class's instantiateBundle method
\r
799 public static UResourceBundle getBundleInstance(String baseName, String localeID,
\r
800 ClassLoader root, boolean disableFallback){
\r
801 UResourceBundle b = instantiateBundle(baseName, localeID, root, disableFallback);
\r
803 throw new MissingResourceException("Could not find the bundle "+ baseName+"/"+ localeID+".res","","");
\r
807 // recursively build bundle .. over-ride super class method.
\r
808 protected synchronized static UResourceBundle instantiateBundle(String baseName, String localeID,
\r
809 ClassLoader root, boolean disableFallback){
\r
810 ULocale defaultLocale = ULocale.getDefault();
\r
811 String localeName = localeID;
\r
812 if(localeName.indexOf('@')>0){
\r
813 localeName = ULocale.getBaseName(localeID);
\r
815 String fullName = getFullName(baseName, localeName);
\r
816 ICUResourceBundle b = (ICUResourceBundle)loadFromCache(root, fullName, defaultLocale);
\r
818 // here we assume that java type resource bundle organization
\r
819 // is required then the base name contains '.' else
\r
820 // the resource organization is of ICU type
\r
821 // so clients can instantiate resources of the type
\r
822 // com.mycompany.data.MyLocaleElements_en.res and
\r
823 // com.mycompany.data.MyLocaleElements.res
\r
825 final String rootLocale = (baseName.indexOf('.')==-1) ? "root" : "";
\r
826 final String defaultID = defaultLocale.toString();
\r
828 if(localeName.equals("")){
\r
829 localeName = rootLocale;
\r
831 if(DEBUG) System.out.println("Creating "+fullName+ " currently b is "+b);
\r
833 b = ICUResourceBundle.createBundle(baseName, localeName, root);
\r
835 if(DEBUG)System.out.println("The bundle created is: "+b+" and disableFallback="+disableFallback+" and bundle.getNoFallback="+(b!=null && b.getNoFallback()));
\r
836 if(disableFallback || (b!=null && b.getNoFallback())){
\r
837 // no fallback because the caller said so or because the bundle says so
\r
838 return addToCache(root, fullName, defaultLocale, b);
\r
841 // fallback to locale ID parent
\r
843 int i = localeName.lastIndexOf('_');
\r
845 String temp = localeName.substring(0, i);
\r
846 b = (ICUResourceBundle)instantiateBundle(baseName, temp, root, disableFallback);
\r
847 if(b!=null && b.getULocale().equals(temp)){
\r
848 b.setLoadingStatus(ICUResourceBundle.FROM_FALLBACK);
\r
851 if(defaultID.indexOf(localeName)==-1){
\r
852 b = (ICUResourceBundle)instantiateBundle(baseName, defaultID, root, disableFallback);
\r
854 b.setLoadingStatus(ICUResourceBundle.FROM_DEFAULT);
\r
856 }else if(rootLocale.length()!=0){
\r
857 b = ICUResourceBundle.createBundle(baseName, rootLocale, root);
\r
859 b.setLoadingStatus(ICUResourceBundle.FROM_ROOT);
\r
864 UResourceBundle parent = null;
\r
865 localeName = b.getLocaleID();
\r
866 int i = localeName.lastIndexOf('_');
\r
868 b = (ICUResourceBundle)addToCache(root, fullName, defaultLocale, b);
\r
870 boolean ParentIsRoot = false;
\r
871 if (b.getTableResource("%%ParentIsRoot") != RES_BOGUS) {
\r
872 ParentIsRoot = true;
\r
875 if (i != -1 && !ParentIsRoot) {
\r
876 parent = instantiateBundle(baseName, localeName.substring(0, i), root, disableFallback);
\r
877 } else if (!localeName.equals(rootLocale)){
\r
878 parent = instantiateBundle(baseName, rootLocale, root, true);
\r
881 if (!b.equals(parent)){
\r
882 b.setParent(parent);
\r
888 UResourceBundle get(String aKey, HashMap<String, String> table, UResourceBundle requested) {
\r
889 ICUResourceBundle obj = (ICUResourceBundle)handleGet(aKey, table, requested);
\r
891 obj = (ICUResourceBundle)getParent();
\r
893 //call the get method to recursively fetch the resource
\r
894 obj = (ICUResourceBundle)obj.get(aKey, table, requested);
\r
897 String fullName = getFullName(getBaseName(), getLocaleID());
\r
898 throw new MissingResourceException(
\r
899 "Can't find resource for bundle " + fullName + ", key "
\r
900 + aKey, this.getClass().getName(), aKey);
\r
903 obj.setLoadingStatus(((ICUResourceBundle)requested).getLocaleID());
\r
907 private static final String ICU_RESOURCE_SUFFIX = ".res";
\r
909 * Gets the full name of the resource with suffix.
\r
911 public static String getFullName(String baseName, String localeName){
\r
912 if(baseName==null || baseName.length()==0){
\r
913 if(localeName.length()==0){
\r
914 return localeName=ULocale.getDefault().toString();
\r
916 return localeName+ICU_RESOURCE_SUFFIX;
\r
918 if(baseName.indexOf('.')==-1){
\r
919 if(baseName.charAt(baseName.length()-1)!= '/'){
\r
920 return baseName+"/"+localeName+ICU_RESOURCE_SUFFIX;
\r
922 return baseName+localeName+ICU_RESOURCE_SUFFIX;
\r
925 baseName = baseName.replace('.','/');
\r
926 if(localeName.length()==0){
\r
927 return baseName+ICU_RESOURCE_SUFFIX;
\r
929 return baseName+"_"+localeName+ICU_RESOURCE_SUFFIX;
\r
935 protected String localeID;
\r
936 protected String baseName;
\r
937 protected ULocale ulocale;
\r
938 protected ClassLoader loader;
\r
941 * Access to the bits and bytes of the resource bundle.
\r
942 * Hides low-level details.
\r
944 protected ICUResourceBundleReader reader;
\r
945 /** Data member where the subclasses store the key. */
\r
946 protected String key;
\r
947 /** Data member where the subclasses store the offset within resource data. */
\r
948 protected int resource;
\r
951 * A resource word value that means "no resource".
\r
952 * Note: 0xffffffff == -1
\r
953 * This has the same value as UResourceBundle.NONE, but they are semantically
\r
954 * different and should be used appropriately according to context:
\r
955 * NONE means "no type".
\r
956 * (The type of RES_BOGUS is RES_RESERVED=15 which was defined in ICU4C ures.h.)
\r
958 public static final int RES_BOGUS = 0xffffffff;
\r
961 * Resource type constant for aliases;
\r
962 * internally stores a string which identifies the actual resource
\r
963 * storing the data (can be in a different resource bundle).
\r
964 * Resolved internally before delivering the actual resource through the API.
\r
966 public static final int ALIAS = 3;
\r
968 /** Resource type constant for tables with 32-bit count, key offsets and values. */
\r
969 public static final int TABLE32 = 4;
\r
972 * Resource type constant for tables with 16-bit count, key offsets and values.
\r
973 * All values are STRING_V2 strings.
\r
975 public static final int TABLE16 = 5;
\r
977 /** Resource type constant for 16-bit Unicode strings in formatVersion 2. */
\r
978 public static final int STRING_V2 = 6;
\r
981 * Resource type constant for arrays with 16-bit count and values.
\r
982 * All values are STRING_V2 strings.
\r
984 public static final int ARRAY16 = 9;
\r
986 private static final ConcurrentHashMap<String, ICUResourceBundle> cache =
\r
987 new ConcurrentHashMap<String, ICUResourceBundle>();
\r
988 private static final ICUResourceBundle NULL_BUNDLE =
\r
989 new ICUResourceBundle(null, null, null, 0, null) {
\r
990 public int hashCode() {
\r
993 public boolean equals(Object rhs) {
\r
994 return this == rhs;
\r
1000 * @param baseName The name for the bundle.
\r
1001 * @param localeID The locale identification.
\r
1002 * @param root The ClassLoader object root.
\r
1003 * @return the new bundle
\r
1005 public static ICUResourceBundle createBundle(String baseName, String localeID,
\r
1006 ClassLoader root) {
\r
1008 String resKey = Integer.toHexString(root.hashCode()) + baseName + localeID;
\r
1009 ICUResourceBundle b = cache.get(resKey);
\r
1011 String resolvedName = getFullName(baseName, localeID);
\r
1012 ICUResourceBundleReader reader = ICUResourceBundleReader.getReader(resolvedName, root);
\r
1013 // could not open the .res file so return null
\r
1014 if (reader == null) {
\r
1017 b = getBundle(reader, baseName, localeID, root);
\r
1019 cache.put(resKey, b);
\r
1021 return b == NULL_BUNDLE ? null : b;
\r
1024 protected String getLocaleID() {
\r
1028 protected String getBaseName() {
\r
1032 public ULocale getULocale() {
\r
1036 public UResourceBundle getParent() {
\r
1037 return (UResourceBundle) parent;
\r
1040 protected void setParent(ResourceBundle parent) {
\r
1041 this.parent = parent;
\r
1044 public String getKey() {
\r
1048 private static final int[] gPublicTypes = new int[] {
\r
1054 TABLE, /* TABLE32 */
\r
1055 TABLE, /* TABLE16 */
\r
1056 STRING, /* STRING_V2 */
\r
1060 ARRAY, /* ARRAY16 */
\r
1070 public int getType() {
\r
1071 return gPublicTypes[ICUResourceBundleReader.RES_GET_TYPE(resource)];
\r
1075 * Get the noFallback flag specified in the loaded bundle.
\r
1076 * @return The noFallback flag.
\r
1078 private boolean getNoFallback() {
\r
1079 return reader.getNoFallback();
\r
1082 private static ICUResourceBundle getBundle(ICUResourceBundleReader reader,
\r
1083 String baseName, String localeID,
\r
1084 ClassLoader loader) {
\r
1085 ICUResourceBundleImpl bundle;
\r
1086 int rootRes = reader.getRootResource();
\r
1087 if(gPublicTypes[ICUResourceBundleReader.RES_GET_TYPE(rootRes)] == TABLE) {
\r
1088 bundle = new ICUResourceBundleImpl.ResourceTable(reader, null, "", rootRes, null);
\r
1090 throw new IllegalStateException("Invalid format error");
\r
1092 bundle.baseName = baseName;
\r
1093 bundle.localeID = localeID;
\r
1094 bundle.ulocale = new ULocale(localeID);
\r
1095 bundle.loader = loader;
\r
1096 if(bundle.reader.getUsesPoolBundle()) {
\r
1097 bundle.reader.setPoolBundleKeys(
\r
1098 ((ICUResourceBundleImpl)getBundleInstance(baseName, "pool", loader, true)).reader);
\r
1100 UResourceBundle alias = bundle.handleGetImpl("%%ALIAS", null, bundle, null, null); // handleGet will cache the bundle with no parent set
\r
1101 if(alias != null) {
\r
1102 return (ICUResourceBundle)UResourceBundle.getBundleInstance(baseName, alias.getString());
\r
1107 // constructor for inner classes
\r
1108 protected ICUResourceBundle(ICUResourceBundleReader reader, String key, String resPath, int resource,
\r
1109 ICUResourceBundle container) {
\r
1110 this.reader = reader;
\r
1112 this.resPath = resPath;
\r
1113 this.resource = resource;
\r
1114 if(container != null) {
\r
1115 baseName = container.baseName;
\r
1116 localeID = container.localeID;
\r
1117 ulocale = container.ulocale;
\r
1118 loader = container.loader;
\r
1119 this.parent = container.parent;
\r
1123 private String getAliasValue(int res) {
\r
1124 String result = reader.getAlias(res);
\r
1125 return result != null ? result : "";
\r
1127 private static final char RES_PATH_SEP_CHAR = '/';
\r
1128 private static final String RES_PATH_SEP_STR = "/";
\r
1129 private static final String ICUDATA = "ICUDATA";
\r
1130 private static final char HYPHEN = '-';
\r
1131 private static final String LOCALE = "LOCALE";
\r
1133 protected ICUResourceBundle findResource(String _key, int _resource,
\r
1134 HashMap<String, String> table,
\r
1135 UResourceBundle requested) {
\r
1136 ClassLoader loaderToUse = loader;
\r
1137 String locale = null, keyPath = null;
\r
1138 String bundleName;
\r
1139 String rpath = getAliasValue(_resource);
\r
1140 if (table == null) {
\r
1141 table = new HashMap<String, String>();
\r
1143 if (table.get(rpath) != null) {
\r
1144 throw new IllegalArgumentException(
\r
1145 "Circular references in the resource bundles");
\r
1147 table.put(rpath, "");
\r
1148 if (rpath.indexOf(RES_PATH_SEP_CHAR) == 0) {
\r
1149 int i = rpath.indexOf(RES_PATH_SEP_CHAR, 1);
\r
1150 int j = rpath.indexOf(RES_PATH_SEP_CHAR, i + 1);
\r
1151 bundleName = rpath.substring(1, i);
\r
1153 locale = rpath.substring(i + 1);
\r
1155 locale = rpath.substring(i + 1, j);
\r
1156 keyPath = rpath.substring(j + 1, rpath.length());
\r
1158 //there is a path included
\r
1159 if (bundleName.equals(ICUDATA)) {
\r
1160 bundleName = ICU_BASE_NAME;
\r
1161 loaderToUse = ICU_DATA_CLASS_LOADER;
\r
1162 }else if(bundleName.indexOf(ICUDATA)>-1){
\r
1163 int idx = bundleName.indexOf(HYPHEN);
\r
1165 bundleName = ICU_BASE_NAME+RES_PATH_SEP_STR+bundleName.substring(idx+1,bundleName.length());
\r
1166 loaderToUse = ICU_DATA_CLASS_LOADER;
\r
1170 //no path start with locale
\r
1171 int i = rpath.indexOf(RES_PATH_SEP_CHAR);
\r
1172 keyPath = rpath.substring(i + 1);
\r
1174 locale = rpath.substring(0, i);
\r
1177 keyPath = null;//keyPath.substring(i, keyPath.length());
\r
1179 bundleName = baseName;
\r
1181 ICUResourceBundle bundle = null;
\r
1182 ICUResourceBundle sub = null;
\r
1183 if(bundleName.equals(LOCALE)){
\r
1184 bundleName = baseName;
\r
1185 bundle = (ICUResourceBundle)requested;
\r
1186 keyPath = rpath.substring(LOCALE.length() + 2/* prepending and appending / */, rpath.length());
\r
1187 locale = ((ICUResourceBundle)requested).getLocaleID();
\r
1188 sub = ICUResourceBundle.findResourceWithFallback(keyPath, requested, null);
\r
1189 if (sub != null) {
\r
1190 sub.resPath = "/" + sub.getLocaleID() + "/" + keyPath;
\r
1193 if (locale == null) {
\r
1194 // {dlf} must use requestor's class loader to get resources from same jar
\r
1195 bundle = (ICUResourceBundle) getBundleInstance(bundleName, "",
\r
1196 loaderToUse, false);
\r
1198 bundle = (ICUResourceBundle) getBundleInstance(bundleName, locale,
\r
1199 loaderToUse, false);
\r
1201 if (keyPath != null) {
\r
1202 StringTokenizer st = new StringTokenizer(keyPath, "/");
\r
1203 ICUResourceBundle current = bundle;
\r
1204 while (st.hasMoreTokens()) {
\r
1205 String subKey = st.nextToken();
\r
1206 sub = (ICUResourceBundle)current.get(subKey, table, requested);
\r
1207 if (sub == null) {
\r
1213 // if the sub resource is not found
\r
1214 // try fetching the sub resource with
\r
1215 // the key of this alias resource
\r
1216 sub = (ICUResourceBundle)bundle.get(_key);
\r
1218 if (sub != null) {
\r
1219 sub.resPath = rpath;
\r
1222 if (sub == null) {
\r
1223 throw new MissingResourceException(localeID, baseName, _key);
\r
1228 // Resource bundle lookup cache, which may be used by subclasses
\r
1229 // which have nested resources
\r
1230 protected ICUCache<Object, UResourceBundle> lookup;
\r
1231 private static final int MAX_INITIAL_LOOKUP_SIZE = 64;
\r
1233 protected void createLookupCache() {
\r
1234 lookup = new SimpleCache<Object, UResourceBundle>(ICUCache.WEAK, Math.max(getSize()*2, MAX_INITIAL_LOOKUP_SIZE));
\r
1237 protected UResourceBundle handleGet(String resKey, HashMap<String, String> table, UResourceBundle requested) {
\r
1238 UResourceBundle res = null;
\r
1239 if (lookup != null) {
\r
1240 res = lookup.get(resKey);
\r
1242 if (res == null) {
\r
1243 int[] index = new int[1];
\r
1244 boolean[] alias = new boolean[1];
\r
1245 res = handleGetImpl(resKey, table, requested, index, alias);
\r
1246 if (res != null && lookup != null && !alias[0]) {
\r
1247 // We do not want to cache a result from alias entry
\r
1248 lookup.put(resKey, res);
\r
1249 lookup.put(Integer.valueOf(index[0]), res);
\r
1255 protected UResourceBundle handleGet(int index, HashMap<String, String> table, UResourceBundle requested) {
\r
1256 UResourceBundle res = null;
\r
1257 Integer indexKey = null;
\r
1258 if (lookup != null) {
\r
1259 indexKey = Integer.valueOf(index);
\r
1260 res = lookup.get(indexKey);
\r
1262 if (res == null) {
\r
1263 boolean[] alias = new boolean[1];
\r
1264 res = handleGetImpl(index, table, requested, alias);
\r
1265 if (res != null && lookup != null && !alias[0]) {
\r
1266 // We do not want to cache a result from alias entry
\r
1267 lookup.put(res.getKey(), res);
\r
1268 lookup.put(indexKey, res);
\r
1274 // Subclass which supports key based resource access to implement this method
\r
1275 protected UResourceBundle handleGetImpl(String resKey, HashMap<String, String> table, UResourceBundle requested,
\r
1276 int[] index, boolean[] isAlias) {
\r
1280 // Subclass which supports index based resource access to implement this method
\r
1281 protected UResourceBundle handleGetImpl(int index, HashMap<String, String> table, UResourceBundle requested,
\r
1282 boolean[] isAlias) {
\r
1287 // TODO Below is a set of workarounds created for org.unicode.cldr.icu.ICU2LDMLWriter
\r
1289 * Calling getKeys() on a table that has alias's can throw a NullPointerException if parent is not set,
\r
1290 * see trac bug: 6514
\r
1291 * -Brian Rower - IBM - Sept. 2008
\r
1295 * Returns the resource handle for the given key within the calling resource table.
\r
1298 * @deprecated This API is ICU internal only and a workaround see ticket #6514.
\r
1299 * @author Brian Rower
\r
1301 protected int getTableResource(String resKey) {
\r
1304 protected int getTableResource(int index) {
\r
1309 * Determines if the object at the specified index of the calling resource table
\r
1310 * is an alias. If it is, returns true
\r
1312 * @param index The index of the resource to check
\r
1313 * @returns True if the resource at 'index' is an alias, false otherwise.
\r
1316 * @deprecated This API is ICU internal only and part of a work around see ticket #6514
\r
1317 * @author Brian Rower
\r
1319 public boolean isAlias(int index)
\r
1321 //TODO this is part of a workaround for ticket #6514
\r
1322 //if index is out of the resource, return false.
\r
1323 return ICUResourceBundleReader.RES_GET_TYPE(getTableResource(index)) == ALIAS;
\r
1329 * @deprecated This API is ICU internal only and part of a workaround see ticket #6514.
\r
1330 * @author Brian Rower
\r
1332 public boolean isAlias()
\r
1334 //TODO this is part of a workaround for ticket #6514
\r
1335 return ICUResourceBundleReader.RES_GET_TYPE(resource) == ALIAS;
\r
1339 * Determines if the object with the specified key
\r
1340 * is an alias. If it is, returns true
\r
1342 * @returns True if the resource with 'key' is an alias, false otherwise.
\r
1345 * @deprecated This API is ICU internal only and part of a workaround see ticket #6514.
\r
1346 * @author Brian Rower
\r
1348 public boolean isAlias(String k)
\r
1350 //TODO this is part of a workaround for ticket #6514
\r
1351 //this only applies to tables
\r
1352 return ICUResourceBundleReader.RES_GET_TYPE(getTableResource(k)) == ALIAS;
\r
1356 * This method can be used to retrieve the underlying alias path (aka where the alias points to)
\r
1357 * This method was written to allow conversion from ICU back to LDML format.
\r
1359 * @param index The index where the alias path points to.
\r
1360 * @return The alias path.
\r
1361 * @author Brian Rower
\r
1363 * @deprecated This API is ICU internal only.
\r
1364 * @author Brian Rower
\r
1366 public String getAliasPath(int index)
\r
1368 return getAliasValue(getTableResource(index));
\r
1374 * @deprecated This API is ICU internal only
\r
1375 * @author Brian Rower
\r
1377 public String getAliasPath()
\r
1379 //TODO cannot allow alias path to end up in public API
\r
1380 return getAliasValue(resource);
\r
1386 * @deprecated This API is ICU internal only
\r
1387 * @author Brian Rower
\r
1389 public String getAliasPath(String k)
\r
1391 //TODO cannot allow alias path to end up in public API
\r
1392 return getAliasValue(getTableResource(k));
\r
1396 * Helper method for getKeysSafe
\r
1398 protected String getKey(int index) {
\r
1403 * Returns an Enumeration of the keys belonging to this table or array.
\r
1404 * This method differs from the getKeys() method by not following alias paths. This method exposes
\r
1405 * underlying alias's. For all general purposes of the ICU resource bundle please use getKeys().
\r
1407 * @return Keys in this table or array.
\r
1409 * @deprecated This API is ICU internal only and a workaround see ticket #6514.
\r
1410 * @author Brian Rower
\r
1412 public Enumeration<String> getKeysSafe()
\r
1414 //TODO this is part of a workaround for ticket #6514
\r
1415 //the safeness only applies to tables, so use the other method if it's not a table
\r
1416 if(!ICUResourceBundleReader.URES_IS_TABLE(resource))
\r
1420 Vector<String> v = new Vector<String>();
\r
1421 int size = getSize();
\r
1422 for(int index = 0; index < size; index++)
\r
1424 String curKey = getKey(index);
\r
1427 return v.elements();
\r
1430 // This is the worker function for the public getKeys().
\r
1431 // TODO: Now that UResourceBundle uses handleKeySet(), this function is obsolete.
\r
1432 // It is also not inherited from ResourceBundle, and it is not implemented
\r
1433 // by ResourceBundleWrapper despite its documentation requiring all subclasses to
\r
1435 // Consider deprecating UResourceBundle.handleGetKeys(), and consider making it always return null.
\r
1436 protected Enumeration<String> handleGetKeys() {
\r
1437 return Collections.enumeration(handleKeySet());
\r
1440 protected boolean isTopLevelResource() {
\r
1441 return resPath.length() == 0;
\r