]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-52_1/main/classes/core/src/com/ibm/icu/impl/ICUResourceBundle.java
Clean up imports.
[Dictionary.git] / jars / icu4j-52_1 / main / classes / core / src / com / ibm / icu / impl / ICUResourceBundle.java
1 /*
2  * *****************************************************************************
3  * Copyright (C) 2005-2013, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  * *****************************************************************************
6  */
7
8 package com.ibm.icu.impl;
9
10 import java.io.BufferedReader;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.io.InputStreamReader;
14 import java.net.URL;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collections;
18 import java.util.Enumeration;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Locale;
23 import java.util.MissingResourceException;
24 import java.util.ResourceBundle;
25 import java.util.Set;
26 import java.util.StringTokenizer;
27
28 import com.ibm.icu.impl.URLHandler.URLVisitor;
29 import com.ibm.icu.util.ULocale;
30 import com.ibm.icu.util.UResourceBundle;
31 import com.ibm.icu.util.UResourceBundleIterator;
32 import com.ibm.icu.util.VersionInfo;
33
34 public  class ICUResourceBundle extends UResourceBundle {
35     /**
36      * The data path to be used with getBundleInstance API
37      */
38     protected static final String ICU_DATA_PATH = "com/ibm/icu/impl/";
39     /**
40      * The data path to be used with getBundleInstance API
41      */
42     public static final String ICU_BUNDLE = "data/icudt" + VersionInfo.ICU_DATA_VERSION_PATH;
43
44     /**
45      * The base name of ICU data to be used with getBundleInstance API
46      */
47     public static final String ICU_BASE_NAME = ICU_DATA_PATH + ICU_BUNDLE;
48
49     /**
50      * The base name of collation data to be used with getBundleInstance API
51      */
52     public static final String ICU_COLLATION_BASE_NAME = ICU_BASE_NAME + "/coll";
53
54     /**
55      * The base name of rbbi data to be used with getData API
56      */
57     public static final String ICU_BRKITR_NAME = "/brkitr";
58
59     /**
60      * The base name of rbbi data to be used with getBundleInstance API
61      */
62     public static final String ICU_BRKITR_BASE_NAME = ICU_BASE_NAME + ICU_BRKITR_NAME;
63
64     /**
65      * The base name of rbnf data to be used with getBundleInstance API
66      */
67     public static final String ICU_RBNF_BASE_NAME = ICU_BASE_NAME + "/rbnf";
68
69     /**
70      * The base name of transliterator data to be used with getBundleInstance API
71      */
72     public static final String ICU_TRANSLIT_BASE_NAME = ICU_BASE_NAME + "/translit";
73
74     public static final String ICU_LANG_BASE_NAME = ICU_BASE_NAME + "/lang";
75     public static final String ICU_CURR_BASE_NAME = ICU_BASE_NAME + "/curr";
76     public static final String ICU_REGION_BASE_NAME = ICU_BASE_NAME + "/region";
77     public static final String ICU_ZONE_BASE_NAME = ICU_BASE_NAME + "/zone";
78
79     private static final String NO_INHERITANCE_MARKER = "\u2205\u2205\u2205";
80
81     /**
82      * The actual path of the resource
83      */
84     protected String resPath;
85
86     /**
87      * The class loader constant to be used with getBundleInstance API
88      */
89     public static final ClassLoader ICU_DATA_CLASS_LOADER;
90     static {
91         ClassLoader loader = ICUData.class.getClassLoader();
92         if (loader == null) {
93             loader = Utility.getFallbackClassLoader();
94         }
95         ICU_DATA_CLASS_LOADER = loader;
96     }
97
98     /**
99      * The name of the resource containing the installed locales
100      */
101     protected static final String INSTALLED_LOCALES = "InstalledLocales";
102
103     public static final int FROM_FALLBACK = 1, FROM_ROOT = 2, FROM_DEFAULT = 3, FROM_LOCALE = 4;
104
105     private int loadingStatus = -1;
106
107     public void setLoadingStatus(int newStatus) {
108         loadingStatus = newStatus;
109     }
110     /**
111      * Returns the loading status of a particular resource.
112      *
113      * @return FROM_FALLBACK if the resource is fetched from fallback bundle
114      *         FROM_ROOT if the resource is fetched from root bundle.
115      *         FROM_DEFAULT if the resource is fetched from the default locale.
116      */
117     public int getLoadingStatus() {
118         return loadingStatus;
119     }
120
121     public void setLoadingStatus(String requestedLocale){
122         String locale = getLocaleID();
123         if(locale.equals("root")) {
124             setLoadingStatus(FROM_ROOT);
125         } else if(locale.equals(requestedLocale)) {
126             setLoadingStatus(FROM_LOCALE);
127         } else {
128             setLoadingStatus(FROM_FALLBACK);
129         }
130      }
131
132     /**
133      * Returns the respath of this bundle
134      * @return the respath of the bundle
135      */
136     public String getResPath(){
137         return resPath;
138     }
139
140     /**
141      * Returns a functionally equivalent locale, considering keywords as well, for the specified keyword.
142      * @param baseName resource specifier
143      * @param resName top level resource to consider (such as "collations")
144      * @param keyword a particular keyword to consider (such as "collation" )
145      * @param locID The requested locale
146      * @param isAvailable If non-null, 1-element array of fillin parameter that indicates whether the
147      * requested locale was available. The locale is defined as 'available' if it physically
148      * exists within the specified tree and included in 'InstalledLocales'.
149      * @param omitDefault  if true, omit keyword and value if default.
150      * 'de_DE\@collation=standard' -> 'de_DE'
151      * @return the locale
152      * @internal ICU 3.0
153      */
154     public static final ULocale getFunctionalEquivalent(String baseName, ClassLoader loader,
155             String resName, String keyword, ULocale locID,
156             boolean isAvailable[], boolean omitDefault) {
157         String kwVal = locID.getKeywordValue(keyword);
158         String baseLoc = locID.getBaseName();
159         String defStr = null;
160         ULocale parent = new ULocale(baseLoc);
161         ULocale defLoc = null; // locale where default (found) resource is
162         boolean lookForDefault = false; // true if kwVal needs to be set
163         ULocale fullBase = null; // base locale of found (target) resource
164         int defDepth = 0; // depth of 'default' marker
165         int resDepth = 0; // depth of found resource;
166
167         if ((kwVal == null) || (kwVal.length() == 0)
168                 || kwVal.equals(DEFAULT_TAG)) {
169             kwVal = ""; // default tag is treated as no keyword
170             lookForDefault = true;
171         }
172
173         // Check top level locale first
174         ICUResourceBundle r = null;
175
176         r = (ICUResourceBundle) UResourceBundle.getBundleInstance(baseName, parent);
177         if (isAvailable != null) {
178             isAvailable[0] = false;
179             ULocale[] availableULocales = getAvailEntry(baseName, loader).getULocaleList();
180             for (int i = 0; i < availableULocales.length; i++) {
181                 if (parent.equals(availableULocales[i])) {
182                     isAvailable[0] = true;
183                     break;
184                 }
185             }
186         }
187         // determine in which locale (if any) the currently relevant 'default' is
188         do {
189             try {
190                 ICUResourceBundle irb = (ICUResourceBundle) r.get(resName);
191                 defStr = irb.getString(DEFAULT_TAG);
192                 if (lookForDefault == true) {
193                     kwVal = defStr;
194                     lookForDefault = false;
195                 }
196                 defLoc = r.getULocale();
197             } catch (MissingResourceException t) {
198                 // Ignore error and continue search.
199             }
200             if (defLoc == null) {
201                 r = (ICUResourceBundle) r.getParent();
202                 defDepth++;
203             }
204         } while ((r != null) && (defLoc == null));
205
206         // Now, search for the named resource
207         parent = new ULocale(baseLoc);
208         r = (ICUResourceBundle) UResourceBundle.getBundleInstance(baseName, parent);
209         // determine in which locale (if any) the named resource is located
210         do {
211             try {
212                 ICUResourceBundle irb = (ICUResourceBundle)r.get(resName);
213                 /* UResourceBundle urb = */irb.get(kwVal);
214                 fullBase = irb.getULocale();
215                 // If the get() completed, we have the full base locale
216                 // If we fell back to an ancestor of the old 'default',
217                 // we need to re calculate the "default" keyword.
218                 if ((fullBase != null) && ((resDepth) > defDepth)) {
219                     defStr = irb.getString(DEFAULT_TAG);
220                     defLoc = r.getULocale();
221                     defDepth = resDepth;
222                 }
223             } catch (MissingResourceException t) {
224                 // Ignore error,
225             }
226             if (fullBase == null) {
227                 r = (ICUResourceBundle) r.getParent();
228                 resDepth++;
229             }
230         } while ((r != null) && (fullBase == null));
231
232         if (fullBase == null && // Could not find resource 'kwVal'
233                 (defStr != null) && // default was defined
234                 !defStr.equals(kwVal)) { // kwVal is not default
235             // couldn't find requested resource. Fall back to default.
236             kwVal = defStr; // Fall back to default.
237             parent = new ULocale(baseLoc);
238             r = (ICUResourceBundle) UResourceBundle.getBundleInstance(baseName, parent);
239             resDepth = 0;
240             // determine in which locale (if any) the named resource is located
241             do {
242                 try {
243                     ICUResourceBundle irb = (ICUResourceBundle)r.get(resName);
244                     UResourceBundle urb = irb.get(kwVal);
245
246                     // if we didn't fail before this..
247                     fullBase = r.getULocale();
248
249                     // If the fetched item (urb) is in a different locale than our outer locale (r/fullBase)
250                     // then we are in a 'fallback' situation. treat as a missing resource situation.
251                     if(!fullBase.toString().equals(urb.getLocale().toString())) {
252                         fullBase = null; // fallback condition. Loop and try again.
253                     }
254
255                     // If we fell back to an ancestor of the old 'default',
256                     // we need to re calculate the "default" keyword.
257                     if ((fullBase != null) && ((resDepth) > defDepth)) {
258                         defStr = irb.getString(DEFAULT_TAG);
259                         defLoc = r.getULocale();
260                         defDepth = resDepth;
261                     }
262                 } catch (MissingResourceException t) {
263                     // Ignore error, continue search.
264                 }
265                 if (fullBase == null) {
266                     r = (ICUResourceBundle) r.getParent();
267                     resDepth++;
268                 }
269             } while ((r != null) && (fullBase == null));
270         }
271
272         if (fullBase == null) {
273             throw new MissingResourceException(
274                 "Could not find locale containing requested or default keyword.",
275                 baseName, keyword + "=" + kwVal);
276         }
277
278         if (omitDefault
279             && defStr.equals(kwVal) // if default was requested and
280             && resDepth <= defDepth) { // default was set in same locale or child
281             return fullBase; // Keyword value is default - no keyword needed in locale
282         } else {
283             return new ULocale(fullBase.toString() + "@" + keyword + "=" + kwVal);
284         }
285     }
286
287     /**
288      * Given a tree path and keyword, return a string enumeration of all possible values for that keyword.
289      * @param baseName resource specifier
290      * @param keyword a particular keyword to consider, must match a top level resource name
291      * within the tree. (i.e. "collations")
292      * @internal ICU 3.0
293      */
294     public static final String[] getKeywordValues(String baseName, String keyword) {
295         Set<String> keywords = new HashSet<String>();
296         ULocale locales[] = createULocaleList(baseName, ICU_DATA_CLASS_LOADER);
297         int i;
298
299         for (i = 0; i < locales.length; i++) {
300             try {
301                 UResourceBundle b = UResourceBundle.getBundleInstance(baseName, locales[i]);
302                 // downcast to ICUResourceBundle?
303                 ICUResourceBundle irb = (ICUResourceBundle) (b.getObject(keyword));
304                 Enumeration<String> e = irb.getKeys();
305                 while (e.hasMoreElements()) {
306                     String s = e.nextElement();
307                     if (!DEFAULT_TAG.equals(s)) {
308                         // don't add 'default' items
309                         keywords.add(s);
310                     }
311                 }
312             } catch (Throwable t) {
313                 //System.err.println("Error in - " + new Integer(i).toString()
314                 // + " - " + t.toString());
315                 // ignore the err - just skip that resource
316             }
317         }
318         return keywords.toArray(new String[0]);
319     }
320
321     /**
322      * This method performs multilevel fallback for fetching items from the
323      * bundle e.g: If resource is in the form de__PHONEBOOK{ collations{
324      * default{ "phonebook"} } } If the value of "default" key needs to be
325      * accessed, then do: <code>
326      *  UResourceBundle bundle = UResourceBundle.getBundleInstance("de__PHONEBOOK");
327      *  ICUResourceBundle result = null;
328      *  if(bundle instanceof ICUResourceBundle){
329      *      result = ((ICUResourceBundle) bundle).getWithFallback("collations/default");
330      *  }
331      * </code>
332      *
333      * @param path The path to the required resource key
334      * @return resource represented by the key
335      * @exception MissingResourceException If a resource was not found.
336      */
337     public ICUResourceBundle getWithFallback(String path) throws MissingResourceException {
338         ICUResourceBundle result = null;
339         ICUResourceBundle actualBundle = this;
340
341         // now recurse to pick up sub levels of the items
342         result = findResourceWithFallback(path, actualBundle, null);
343
344         if (result == null) {
345             throw new MissingResourceException(
346                 "Can't find resource for bundle "
347                 + this.getClass().getName() + ", key " + getType(),
348                 path, getKey());
349         }
350
351         if ( result.getType() == ICUResourceBundle.STRING && result.getString().equals(NO_INHERITANCE_MARKER)) {
352             throw new MissingResourceException("Encountered NO_INHERITANCE_MARKER",path,getKey());
353         }
354
355         return result;
356     }
357     
358     public ICUResourceBundle at(int index) {
359         return (ICUResourceBundle) handleGet(index, null, this);
360     }
361     
362     public ICUResourceBundle at(String key) {
363         // don't ever presume the key is an int in disguise, like ResourceArray does.
364         if (this instanceof ICUResourceBundleImpl.ResourceTable) {
365             return (ICUResourceBundle) handleGet(key, null, this);
366         }
367         return null;
368     }
369     
370     @Override
371     public ICUResourceBundle findTopLevel(int index) {
372         return (ICUResourceBundle) super.findTopLevel(index);
373     }
374     
375     @Override
376     public ICUResourceBundle findTopLevel(String aKey) {
377         return (ICUResourceBundle) super.findTopLevel(aKey);
378     }
379     
380     /**
381      * Like getWithFallback, but returns null if the resource is not found instead of
382      * throwing an exception.
383      * @param path the path to the resource
384      * @return the resource, or null
385      */
386     public ICUResourceBundle findWithFallback(String path) {
387         return findResourceWithFallback(path, this, null);
388     }
389
390     // will throw type mismatch exception if the resource is not a string
391     public String getStringWithFallback(String path) throws MissingResourceException {
392         return getWithFallback(path).getString();
393     }
394
395     /**
396      * Return a set of the locale names supported by a collection of resource
397      * bundles.
398      *
399      * @param bundlePrefix the prefix of the resource bundles to use.
400      */
401     public static Set<String> getAvailableLocaleNameSet(String bundlePrefix, ClassLoader loader) {
402         return getAvailEntry(bundlePrefix, loader).getLocaleNameSet();
403     }
404
405     /**
406      * Return a set of all the locale names supported by a collection of
407      * resource bundles.
408      */
409     public static Set<String> getFullLocaleNameSet() {
410         return getFullLocaleNameSet(ICU_BASE_NAME, ICU_DATA_CLASS_LOADER);
411     }
412
413     /**
414      * Return a set of all the locale names supported by a collection of
415      * resource bundles.
416      *
417      * @param bundlePrefix the prefix of the resource bundles to use.
418      */
419     public static Set<String> getFullLocaleNameSet(String bundlePrefix, ClassLoader loader) {
420         return getAvailEntry(bundlePrefix, loader).getFullLocaleNameSet();
421     }
422
423     /**
424      * Return a set of the locale names supported by a collection of resource
425      * bundles.
426      */
427     public static Set<String> getAvailableLocaleNameSet() {
428         return getAvailableLocaleNameSet(ICU_BASE_NAME, ICU_DATA_CLASS_LOADER);
429     }
430
431     /**
432      * Get the set of Locales installed in the specified bundles.
433      * @return the list of available locales
434      */
435     public static final ULocale[] getAvailableULocales(String baseName, ClassLoader loader) {
436         return getAvailEntry(baseName, loader).getULocaleList();
437     }
438
439     /**
440      * Get the set of ULocales installed the base bundle.
441      * @return the list of available locales
442      */
443     public static final ULocale[] getAvailableULocales() {
444         return getAvailableULocales(ICU_BASE_NAME, ICU_DATA_CLASS_LOADER);
445     }
446
447     /**
448      * Get the set of Locales installed in the specified bundles.
449      * @return the list of available locales
450      */
451     public static final Locale[] getAvailableLocales(String baseName, ClassLoader loader) {
452         return getAvailEntry(baseName, loader).getLocaleList();
453     }
454
455    /**
456      * Get the set of Locales installed the base bundle.
457      * @return the list of available locales
458      */
459     public static final Locale[] getAvailableLocales() {
460         return getAvailEntry(ICU_BASE_NAME, ICU_DATA_CLASS_LOADER).getLocaleList();
461     }
462
463     /**
464      * Convert a list of ULocales to a list of Locales.  ULocales with a script code will not be converted
465      * since they cannot be represented as a Locale.  This means that the two lists will <b>not</b> match
466      * one-to-one, and that the returned list might be shorter than the input list.
467      * @param ulocales a list of ULocales to convert to a list of Locales.
468      * @return the list of converted ULocales
469      */
470     public static final Locale[] getLocaleList(ULocale[] ulocales) {
471         ArrayList<Locale> list = new ArrayList<Locale>(ulocales.length);
472         HashSet<Locale> uniqueSet = new HashSet<Locale>();
473         for (int i = 0; i < ulocales.length; i++) {
474             Locale loc = ulocales[i].toLocale();
475             if (!uniqueSet.contains(loc)) {
476                 list.add(loc);
477                 uniqueSet.add(loc);
478             }
479         }
480         return list.toArray(new Locale[list.size()]);
481     }
482
483     /**
484      * Returns the locale of this resource bundle. This method can be used after
485      * a call to getBundle() to determine whether the resource bundle returned
486      * really corresponds to the requested locale or is a fallback.
487      *
488      * @return the locale of this resource bundle
489      */
490     public Locale getLocale() {
491         return getULocale().toLocale();
492     }
493
494
495     // ========== privates ==========
496     private static final String ICU_RESOURCE_INDEX = "res_index";
497
498     private static final String DEFAULT_TAG = "default";
499
500     // The name of text file generated by ICU4J build script including all locale names
501     // (canonical, alias and root)
502     private static final String FULL_LOCALE_NAMES_LIST = "fullLocaleNames.lst";
503
504     // Flag for enabling/disabling debugging code
505     private static final boolean DEBUG = ICUDebug.enabled("localedata");
506
507     private static final ULocale[] createULocaleList(String baseName,
508             ClassLoader root) {
509         // the canned list is a subset of all the available .res files, the idea
510         // is we don't export them
511         // all. gotta be a better way to do this, since to add a locale you have
512         // to update this list,
513         // and it's embedded in our binary resources.
514         ICUResourceBundle bundle = (ICUResourceBundle) UResourceBundle.instantiateBundle(baseName, ICU_RESOURCE_INDEX, root, true);
515
516         bundle = (ICUResourceBundle)bundle.get(INSTALLED_LOCALES);
517         int length = bundle.getSize();
518         int i = 0;
519         ULocale[] locales = new ULocale[length];
520         UResourceBundleIterator iter = bundle.getIterator();
521         iter.reset();
522         while (iter.hasNext()) {
523             String locstr = iter.next().getKey();
524             if (locstr.equals("root")) {
525                 locales[i++] = ULocale.ROOT;
526             } else {
527                 locales[i++] = new ULocale(locstr);
528             }
529         }
530         bundle = null;
531         return locales;
532     }
533
534     private static final Locale[] createLocaleList(String baseName, ClassLoader loader) {
535         ULocale[] ulocales = getAvailEntry(baseName, loader).getULocaleList();
536         return getLocaleList(ulocales);
537     }
538
539     private static final String[] createLocaleNameArray(String baseName,
540             ClassLoader root) {
541         ICUResourceBundle bundle = (ICUResourceBundle) UResourceBundle.instantiateBundle( baseName, ICU_RESOURCE_INDEX, root, true);
542         bundle = (ICUResourceBundle)bundle.get(INSTALLED_LOCALES);
543         int length = bundle.getSize();
544         int i = 0;
545         String[] locales = new String[length];
546         UResourceBundleIterator iter = bundle.getIterator();
547         iter.reset();
548         while (iter.hasNext()) {
549             String locstr = iter.next(). getKey();
550             if (locstr.equals("root")) {
551                 locales[i++] = ULocale.ROOT.toString();
552             } else {
553                 locales[i++] = locstr;
554             }
555         }
556         bundle = null;
557         return locales;
558     }
559
560     private static final List<String> createFullLocaleNameArray(
561             final String baseName, final ClassLoader root) {
562
563         List<String> list = java.security.AccessController
564             .doPrivileged(new java.security.PrivilegedAction<List<String>>() {
565                 public List<String> run() {
566                     // WebSphere class loader will return null for a raw
567                     // directory name without trailing slash
568                     String bn = baseName.endsWith("/")
569                         ? baseName
570                         : baseName + "/";
571
572                     List<String> resList = null;
573
574                     String skipScan = ICUConfig.get("com.ibm.icu.impl.ICUResourceBundle.skipRuntimeLocaleResourceScan", "false");
575                     if (!skipScan.equalsIgnoreCase("true")) {
576                         // scan available locale resources under the base url first
577                         try {
578                             Enumeration<URL> urls = root.getResources(bn);
579                             while (urls.hasMoreElements()) {
580                                 URL url = urls.nextElement();
581                                 URLHandler handler = URLHandler.get(url);
582                                 if (handler != null) {
583                                     final List<String> lst = new ArrayList<String>();
584                                     URLVisitor v = new URLVisitor() {
585                                             public void visit(String s) {
586                                                 //TODO: This is ugly hack.  We have to figure out how
587                                                 // we can distinguish locale data from others
588                                                 if (s.endsWith(".res")) {
589                                                     String locstr = s.substring(0, s.length() - 4);
590                                                     if (locstr.contains("_") && !locstr.equals("res_index")) {
591                                                         // locale data with country/script contain "_",
592                                                         // except for res_index.res
593                                                         lst.add(locstr);
594                                                     } else if (locstr.length() == 2 || locstr.length() == 3) {
595                                                         // all 2-letter or 3-letter entries are all locale
596                                                         // data at least for now
597                                                         lst.add(locstr);
598                                                     } else if (locstr.equalsIgnoreCase("root")) {
599                                                         // root locale is a special case
600                                                         lst.add(ULocale.ROOT.toString());
601                                                     }
602                                                 }
603                                             }
604                                         };
605                                     handler.guide(v, false);
606
607                                     if (resList == null) {
608                                         resList = new ArrayList<String>(lst);
609                                     } else {
610                                         resList.addAll(lst);
611                                     }
612                                 } else {
613                                     if (DEBUG) System.out.println("handler for " + url + " is null");
614                                 }
615                             }
616                         } catch (IOException e) {
617                             if (DEBUG) System.out.println("ouch: " + e.getMessage());
618                             resList = null;
619                         }
620                     }
621
622                     if (resList == null) {
623                         // look for prebuilt full locale names list next
624                         try {
625                             InputStream s = root.getResourceAsStream(bn + FULL_LOCALE_NAMES_LIST);
626                             if (s != null) {
627                                 resList = new ArrayList<String>();
628                                 BufferedReader br = new BufferedReader(new InputStreamReader(s, "ASCII"));
629                                 String line;
630                                 while ((line = br.readLine()) != null) {
631                                     if (line.length() != 0 && !line.startsWith("#")) {
632                                         if (line.equalsIgnoreCase("root")) {
633                                             resList.add(ULocale.ROOT.toString());
634                                         } else {
635                                             resList.add(line);
636                                         }
637                                     }
638                                 }
639                                 br.close();
640                             }
641                         } catch (IOException e) {
642                             // swallow it
643                         }
644                     }
645
646                     return resList;
647                 }
648             });
649
650         return list;
651     }
652
653     private static Set<String> createFullLocaleNameSet(String baseName, ClassLoader loader) {
654         List<String> list = createFullLocaleNameArray(baseName, loader);
655         if(list == null){
656             if (DEBUG) System.out.println("createFullLocaleNameArray returned null");
657             // Use locale name set as the last resort fallback
658             Set<String> locNameSet = createLocaleNameSet(baseName, loader);
659             String rootLocaleID = ULocale.ROOT.toString();
660             if (!locNameSet.contains(rootLocaleID)) {
661                 // We need to add the root locale in the set
662                 Set<String> tmp = new HashSet<String>(locNameSet);
663                 tmp.add(rootLocaleID);
664                 locNameSet = Collections.unmodifiableSet(tmp);
665             }
666             return locNameSet;
667         }
668         Set<String> fullLocNameSet = new HashSet<String>();
669         fullLocNameSet.addAll(list);
670         return Collections.unmodifiableSet(fullLocNameSet);
671     }
672
673     private static Set<String> createLocaleNameSet(String baseName, ClassLoader loader) {
674         try {
675             String[] locales = createLocaleNameArray(baseName, loader);
676
677             HashSet<String> set = new HashSet<String>();
678             set.addAll(Arrays.asList(locales));
679             return Collections.unmodifiableSet(set);
680         } catch (MissingResourceException e) {
681             if (DEBUG) {
682                 System.out.println("couldn't find index for bundleName: " + baseName);
683                 Thread.dumpStack();
684             }
685         }
686         return Collections.emptySet();
687     }
688
689     /**
690      * Holds the prefix, and lazily creates the Locale[] list or the locale name
691      * Set as needed.
692      */
693     private static final class AvailEntry {
694         private String prefix;
695         private ClassLoader loader;
696         private volatile ULocale[] ulocales;
697         private volatile Locale[] locales;
698         private volatile Set<String> nameSet;
699         private volatile Set<String> fullNameSet;
700
701         AvailEntry(String prefix, ClassLoader loader) {
702             this.prefix = prefix;
703             this.loader = loader;
704         }
705
706         ULocale[] getULocaleList() {
707             if (ulocales == null) {
708                 synchronized(this) {
709                     if (ulocales == null) {
710                         ulocales = createULocaleList(prefix, loader);
711                     }
712                 }
713             }
714             return ulocales;
715         }
716         Locale[] getLocaleList() {
717             if (locales == null) {
718                 synchronized(this) {
719                     if (locales == null) {
720                         locales = createLocaleList(prefix, loader);
721                     }
722                 }
723             }
724             return locales;
725         }
726         Set<String> getLocaleNameSet() {
727             if (nameSet == null) {
728                 synchronized(this) {
729                     if (nameSet == null) {
730                         nameSet = createLocaleNameSet(prefix, loader);
731                     }
732                 }
733             }
734             return nameSet;
735         }
736         Set<String> getFullLocaleNameSet() {
737             // When there's no prebuilt index, we iterate through the jar files
738             // and read the contents to build it.  If many threads try to read the
739             // same jar at the same time, java thrashes.  Synchronize here
740             // so that we can avoid this problem. We don't synchronize on the
741             // other methods since they don't do this.
742             //
743             // This is the common entry point for access into the code that walks
744             // through the resources, and is cached.  So it's a good place to lock
745             // access.  Locking in the URLHandler doesn't give us a common object
746             // to lock.
747             if (fullNameSet == null) {
748                 synchronized(this) {
749                     if (fullNameSet == null) {
750                         fullNameSet = createFullLocaleNameSet(prefix, loader);
751                     }
752                 }
753             }
754             return fullNameSet;
755         }
756     }
757
758
759     /*
760      * Cache used for AvailableEntry 
761      */
762     private static CacheBase<String, AvailEntry, ClassLoader> GET_AVAILABLE_CACHE =
763         new SoftCache<String, AvailEntry, ClassLoader>()  {
764             protected AvailEntry createInstance(String key, ClassLoader loader) {
765                 return new AvailEntry(key, loader);
766             }
767         };
768
769     /**
770      * Stores the locale information in a cache accessed by key (bundle prefix).
771      * The cached objects are AvailEntries. The cache is implemented by SoftCache
772      * so it can be GC'd.
773      */
774     private static AvailEntry getAvailEntry(String key, ClassLoader loader) {
775         return GET_AVAILABLE_CACHE.getInstance(key, loader);
776     }
777
778     protected static final ICUResourceBundle findResourceWithFallback(String path,
779             UResourceBundle actualBundle, UResourceBundle requested) {
780         ICUResourceBundle sub = null;
781         if (requested == null) {
782             requested = actualBundle;
783         }
784
785         ICUResourceBundle base = (ICUResourceBundle) actualBundle;
786         String basePath = ((ICUResourceBundle)actualBundle).resPath.length() > 0 ?
787                 ((ICUResourceBundle)actualBundle).resPath : "";
788
789         while (base != null) {
790             if (path.indexOf('/') == -1) { // skip the tokenizer
791                 sub = (ICUResourceBundle) base.handleGet(path, null, requested);
792                 if (sub != null) {
793                     break;
794                 }
795             } else {
796                 ICUResourceBundle currentBase = base;
797                 StringTokenizer st = new StringTokenizer(path, "/");
798                 while (st.hasMoreTokens()) {
799                     String subKey = st.nextToken();
800                     sub = ICUResourceBundle.findResourceWithFallback(subKey, currentBase, requested);
801                     if (sub == null) {
802                         break;
803                     }
804                     currentBase = sub;
805                 }
806                 if (sub != null) {
807                     //we found it
808                     break;
809                 }
810             }
811             // if not try the parent bundle - note, getParent() returns the bundle root
812             base = (ICUResourceBundle)base.getParent();
813             path = basePath.length() > 0 ? basePath + "/" + path : path;
814             basePath = "";
815         }
816         if(sub != null){
817             sub.setLoadingStatus(((ICUResourceBundle)requested).getLocaleID());
818         }
819         return sub;
820     }
821
822     public boolean equals(Object other) {
823         if (this == other) {
824             return true;
825         }
826         if (other instanceof ICUResourceBundle) {
827             ICUResourceBundle o = (ICUResourceBundle) other;
828             if (getBaseName().equals(o.getBaseName())
829                     && getLocaleID().equals(o.getLocaleID())) {
830                 return true;
831             }
832         }
833         return false;
834     }
835     
836     public int hashCode() {
837         assert false : "hashCode not designed";
838         return 42;
839     }
840     
841     // This method is for super class's instantiateBundle method
842     public static UResourceBundle getBundleInstance(String baseName, String localeID,
843                                                     ClassLoader root, boolean disableFallback){
844         UResourceBundle b = instantiateBundle(baseName, localeID, root, disableFallback);
845         if(b==null){
846             throw new MissingResourceException("Could not find the bundle "+ baseName+"/"+ localeID+".res","","");
847         }
848         return b;
849     }
850     //  recursively build bundle .. over-ride super class method.
851     protected synchronized static UResourceBundle instantiateBundle(String baseName, String localeID,
852                                                                     ClassLoader root, boolean disableFallback){
853         ULocale defaultLocale = ULocale.getDefault();
854         String localeName = localeID;
855         if(localeName.indexOf('@')>=0){
856             localeName = ULocale.getBaseName(localeID);
857         }
858         String fullName = ICUResourceBundleReader.getFullName(baseName, localeName);
859         ICUResourceBundle b = (ICUResourceBundle)loadFromCache(root, fullName, defaultLocale);
860
861         // here we assume that java type resource bundle organization
862         // is required then the base name contains '.' else
863         // the resource organization is of ICU type
864         // so clients can instantiate resources of the type
865         // com.mycompany.data.MyLocaleElements_en.res and
866         // com.mycompany.data.MyLocaleElements.res
867         //
868         final String rootLocale = (baseName.indexOf('.')==-1) ? "root" : "";
869         final String defaultID = defaultLocale.getBaseName();
870
871         if(localeName.equals("")){
872             localeName = rootLocale;
873         }
874         if(DEBUG) System.out.println("Creating "+fullName+ " currently b is "+b);
875         if (b == null) {
876             b = ICUResourceBundle.createBundle(baseName, localeName, root);
877
878             if(DEBUG)System.out.println("The bundle created is: "+b+" and disableFallback="+disableFallback+" and bundle.getNoFallback="+(b!=null && b.getNoFallback()));
879             if(disableFallback || (b!=null && b.getNoFallback())){
880                 // no fallback because the caller said so or because the bundle says so
881                 return addToCache(root, fullName, defaultLocale, b);
882             }
883
884             // fallback to locale ID parent
885             if(b == null){
886                 int i = localeName.lastIndexOf('_');
887                 if (i != -1) {
888                     String temp = localeName.substring(0, i);
889                     b = (ICUResourceBundle)instantiateBundle(baseName, temp, root, disableFallback);
890                     if(b!=null && b.getULocale().getName().equals(temp)){
891                         b.setLoadingStatus(ICUResourceBundle.FROM_FALLBACK);
892                     }
893                 }else{
894                     if(defaultID.indexOf(localeName)==-1){
895                         b = (ICUResourceBundle)instantiateBundle(baseName, defaultID, root, disableFallback);
896                         if(b!=null){
897                             b.setLoadingStatus(ICUResourceBundle.FROM_DEFAULT);
898                         }
899                     }else if(rootLocale.length()!=0){
900                         b = ICUResourceBundle.createBundle(baseName, rootLocale, root);
901                         if(b!=null){
902                             b.setLoadingStatus(ICUResourceBundle.FROM_ROOT);
903                         }
904                     }
905                 }
906             }else{
907                 UResourceBundle parent = null;
908                 localeName = b.getLocaleID();
909                 int i = localeName.lastIndexOf('_');
910
911                 b = (ICUResourceBundle)addToCache(root, fullName, defaultLocale, b);
912
913                 if (b.getTableResource("%%Parent") != RES_BOGUS) {
914                     String parentLocaleName = b.getString("%%Parent");
915                     parent = instantiateBundle(baseName, parentLocaleName, root, disableFallback);
916                 } else if (i != -1) {
917                     parent = instantiateBundle(baseName, localeName.substring(0, i), root, disableFallback);
918                 } else if (!localeName.equals(rootLocale)){
919                     parent = instantiateBundle(baseName, rootLocale, root, true);
920                 }
921
922                 if (!b.equals(parent)){
923                     b.setParent(parent);
924                 }
925             }
926         }
927         return b;
928     }
929     UResourceBundle get(String aKey, HashMap<String, String> table, UResourceBundle requested) {
930         ICUResourceBundle obj = (ICUResourceBundle)handleGet(aKey, table, requested);
931         if (obj == null) {
932             obj = (ICUResourceBundle)getParent();
933             if (obj != null) {
934                 //call the get method to recursively fetch the resource
935                 obj = (ICUResourceBundle)obj.get(aKey, table, requested);
936             }
937             if (obj == null) {
938                 String fullName = ICUResourceBundleReader.getFullName(getBaseName(), getLocaleID());
939                 throw new MissingResourceException(
940                         "Can't find resource for bundle " + fullName + ", key "
941                                 + aKey, this.getClass().getName(), aKey);
942             }
943         }
944         obj.setLoadingStatus(((ICUResourceBundle)requested).getLocaleID());
945         return obj;
946     }
947
948     protected String localeID;
949     protected String baseName;
950     protected ULocale ulocale;
951     protected ClassLoader loader;
952
953     /**
954      * Access to the bits and bytes of the resource bundle.
955      * Hides low-level details.
956      */
957     protected ICUResourceBundleReader reader;
958     /** Data member where the subclasses store the key. */
959     protected String key;
960     /** Data member where the subclasses store the offset within resource data. */
961     protected int resource;
962
963     /**
964      * A resource word value that means "no resource".
965      * Note: 0xffffffff == -1
966      * This has the same value as UResourceBundle.NONE, but they are semantically
967      * different and should be used appropriately according to context:
968      * NONE means "no type".
969      * (The type of RES_BOGUS is RES_RESERVED=15 which was defined in ICU4C ures.h.)
970      */
971     public static final int RES_BOGUS = 0xffffffff;
972
973     /**
974      * Resource type constant for aliases;
975      * internally stores a string which identifies the actual resource
976      * storing the data (can be in a different resource bundle).
977      * Resolved internally before delivering the actual resource through the API.
978      */
979     public static final int ALIAS = 3;
980
981     /** Resource type constant for tables with 32-bit count, key offsets and values. */
982     public static final int TABLE32 = 4;
983
984     /**
985      * Resource type constant for tables with 16-bit count, key offsets and values.
986      * All values are STRING_V2 strings.
987      */
988     public static final int TABLE16 = 5;
989
990     /** Resource type constant for 16-bit Unicode strings in formatVersion 2. */
991     public static final int STRING_V2 = 6;
992
993     /**
994      * Resource type constant for arrays with 16-bit count and values.
995      * All values are STRING_V2 strings.
996      */
997     public static final int ARRAY16 = 9;
998
999     /**
1000     * Create a bundle using a reader.
1001     * @param baseName The name for the bundle.
1002     * @param localeID The locale identification.
1003     * @param root The ClassLoader object root.
1004     * @return the new bundle
1005     */
1006     public static ICUResourceBundle createBundle(String baseName, String localeID, ClassLoader root) {
1007         ICUResourceBundleReader reader = ICUResourceBundleReader.getReader(baseName, localeID, root);
1008         if (reader == null) {
1009             // could not open the .res file
1010             return null;
1011         }
1012         return getBundle(reader, baseName, localeID, root);
1013     }
1014
1015     protected String getLocaleID() {
1016         return localeID;
1017     }
1018
1019     protected String getBaseName() {
1020         return baseName;
1021     }
1022
1023     public ULocale getULocale() {
1024         return ulocale;
1025     }
1026
1027     public UResourceBundle getParent() {
1028         return (UResourceBundle) parent;
1029     }
1030
1031     protected void setParent(ResourceBundle parent) {
1032         this.parent = parent;
1033     }
1034
1035     public String getKey() {
1036         return key;
1037     }
1038
1039     private static final int[] gPublicTypes = new int[] {
1040         STRING,
1041         BINARY,
1042         TABLE,
1043         ALIAS,
1044
1045         TABLE,      /* TABLE32 */
1046         TABLE,      /* TABLE16 */
1047         STRING,     /* STRING_V2 */
1048         INT,
1049
1050         ARRAY,
1051         ARRAY,      /* ARRAY16 */
1052         NONE,
1053         NONE,
1054
1055         NONE,
1056         NONE,
1057         INT_VECTOR,
1058         NONE
1059     };
1060
1061     public int getType() {
1062         return gPublicTypes[ICUResourceBundleReader.RES_GET_TYPE(resource)];
1063     }
1064
1065     /**
1066      * Get the noFallback flag specified in the loaded bundle.
1067      * @return The noFallback flag.
1068      */
1069     private boolean getNoFallback() {
1070         return reader.getNoFallback();
1071     }
1072
1073     private static ICUResourceBundle getBundle(ICUResourceBundleReader reader,
1074                                                String baseName, String localeID,
1075                                                ClassLoader loader) {
1076         ICUResourceBundleImpl bundle;
1077         int rootRes = reader.getRootResource();
1078         if(gPublicTypes[ICUResourceBundleReader.RES_GET_TYPE(rootRes)] == TABLE) {
1079             bundle = new ICUResourceBundleImpl.ResourceTable(reader, null, "", rootRes, null);
1080         } else {
1081             throw new IllegalStateException("Invalid format error");
1082         }
1083         bundle.baseName = baseName;
1084         bundle.localeID = localeID;
1085         bundle.ulocale = new ULocale(localeID);
1086         bundle.loader = loader;
1087         UResourceBundle alias = bundle.handleGetImpl("%%ALIAS", null, bundle, null, null); // handleGet will cache the bundle with no parent set
1088         if(alias != null) {
1089             return (ICUResourceBundle)UResourceBundle.getBundleInstance(baseName, alias.getString());
1090         } else {
1091             return bundle;
1092         }
1093     }
1094     // constructor for inner classes
1095     protected ICUResourceBundle(ICUResourceBundleReader reader, String key, String resPath, int resource,
1096                                 ICUResourceBundle container) {
1097         this.reader = reader;
1098         this.key = key;
1099         this.resPath = resPath;
1100         this.resource = resource;
1101         if(container != null) {
1102             baseName = container.baseName;
1103             localeID = container.localeID;
1104             ulocale = container.ulocale;
1105             loader = container.loader;
1106             this.parent = container.parent;
1107         }
1108     }
1109
1110     private String getAliasValue(int res) {
1111         String result = reader.getAlias(res);
1112         return result != null ? result : "";
1113     }
1114     private static final char RES_PATH_SEP_CHAR = '/';
1115     private static final String RES_PATH_SEP_STR = "/";
1116     private static final String ICUDATA = "ICUDATA";
1117     private static final char HYPHEN = '-';
1118     private static final String LOCALE = "LOCALE";
1119
1120     protected ICUResourceBundle findResource(String key,
1121                                              String resPath,
1122                                              int _resource,
1123                                              HashMap<String, String> table,
1124                                              UResourceBundle requested) {
1125         ClassLoader loaderToUse = loader;
1126         String locale = null, keyPath = null;
1127         String bundleName;
1128         String rpath = getAliasValue(_resource);
1129         if (table == null) {
1130             table = new HashMap<String, String>();
1131         }
1132         if (table.get(rpath) != null) {
1133             throw new IllegalArgumentException(
1134                     "Circular references in the resource bundles");
1135         }
1136         table.put(rpath, "");
1137         if (rpath.indexOf(RES_PATH_SEP_CHAR) == 0) {
1138             int i = rpath.indexOf(RES_PATH_SEP_CHAR, 1);
1139             int j = rpath.indexOf(RES_PATH_SEP_CHAR, i + 1);
1140             bundleName = rpath.substring(1, i);
1141             if (j < 0) {
1142                 locale = rpath.substring(i + 1);
1143                 // if key path is not available,
1144                 // use the given key path
1145                 keyPath = resPath;
1146             } else {
1147                 locale = rpath.substring(i + 1, j);
1148                 keyPath = rpath.substring(j + 1, rpath.length());
1149             }
1150             //there is a path included
1151             if (bundleName.equals(ICUDATA)) {
1152                 bundleName = ICU_BASE_NAME;
1153                 loaderToUse = ICU_DATA_CLASS_LOADER;
1154             }else if(bundleName.indexOf(ICUDATA)>-1){
1155                 int idx = bundleName.indexOf(HYPHEN);
1156                 if(idx>-1){
1157                     bundleName = ICU_BASE_NAME+RES_PATH_SEP_STR+bundleName.substring(idx+1,bundleName.length());
1158                     loaderToUse = ICU_DATA_CLASS_LOADER;
1159                 }
1160             }
1161         } else {
1162             //no path start with locale
1163             int i = rpath.indexOf(RES_PATH_SEP_CHAR);
1164             if (i != -1) {
1165                 locale = rpath.substring(0, i);
1166                 keyPath = rpath.substring(i + 1);
1167             } else {
1168                 locale = rpath;
1169                 // if key path is not available,
1170                 // use the given key path
1171                 keyPath = resPath;
1172             }
1173             bundleName = baseName;
1174         }
1175         ICUResourceBundle bundle = null;
1176         ICUResourceBundle sub = null;
1177         if(bundleName.equals(LOCALE)){
1178             bundleName = baseName;
1179             keyPath = rpath.substring(LOCALE.length() + 2/* prepending and appending / */, rpath.length());
1180             locale = ((ICUResourceBundle)requested).getLocaleID();
1181
1182             // Get the top bundle of the requested bundle
1183             bundle = (ICUResourceBundle)getBundleInstance(bundleName, locale, loaderToUse, false);
1184             if (bundle != null) {
1185                 sub = ICUResourceBundle.findResourceWithFallback(keyPath, bundle, null);
1186                 // TODO
1187                 // The resPath of the resolved bundle should reflect the resource path
1188                 // requested by caller. However, overwriting resPath here will affect cached
1189                 // resource instance. The resPath is exposed by ICUResourceBundle#getResPath,
1190                 // but there are no call sites in ICU (and ICUResourceBundle is an implementation
1191                 // class). We may create a safe clone to overwrite the resPath field, but
1192                 // it has no benefit at least for now. -Yoshito
1193                 //if (sub != null) {
1194                 //    sub.resPath = resPath;
1195                 //}
1196             }
1197         }else{
1198             if (locale == null) {
1199                 // {dlf} must use requestor's class loader to get resources from same jar
1200                 bundle = (ICUResourceBundle) getBundleInstance(bundleName, "",
1201                          loaderToUse, false);
1202             } else {
1203                 bundle = (ICUResourceBundle) getBundleInstance(bundleName, locale,
1204                          loaderToUse, false);
1205             }
1206
1207             StringTokenizer st = new StringTokenizer(keyPath, "/");
1208             ICUResourceBundle current = bundle;
1209             while (st.hasMoreTokens()) {
1210                 String subKey = st.nextToken();
1211                 sub = (ICUResourceBundle)current.get(subKey, table, requested);
1212                 if (sub == null) {
1213                     break;
1214                 }
1215                 current = sub;
1216             }
1217             // TODO
1218             // See the comments above.
1219             //if (sub != null) {
1220             //    sub.resPath = resPath;
1221             //}
1222         }
1223         if (sub == null) {
1224             throw new MissingResourceException(localeID, baseName, key);
1225         }
1226         return sub;
1227     }
1228
1229     // Resource bundle lookup cache, which may be used by subclasses
1230     // which have nested resources
1231     protected ICUCache<Object, UResourceBundle> lookup;
1232     private static final int MAX_INITIAL_LOOKUP_SIZE = 64;
1233
1234     protected void createLookupCache() {
1235         lookup = new SimpleCache<Object, UResourceBundle>(ICUCache.WEAK, Math.max(getSize()*2, MAX_INITIAL_LOOKUP_SIZE));
1236     }
1237
1238     protected UResourceBundle handleGet(String resKey, HashMap<String, String> table, UResourceBundle requested) {
1239         UResourceBundle res = null;
1240         if (lookup != null) {
1241             res = lookup.get(resKey);
1242         }
1243         if (res == null) {
1244             int[] index = new int[1];
1245             boolean[] alias = new boolean[1];
1246             res = handleGetImpl(resKey, table, requested, index, alias);
1247             if (res != null && lookup != null && !alias[0]) {
1248                 // We do not want to cache a result from alias entry
1249                 lookup.put(resKey, res);
1250                 lookup.put(Integer.valueOf(index[0]), res);
1251             }
1252         }
1253         return res;
1254     }
1255
1256     protected UResourceBundle handleGet(int index, HashMap<String, String> table, UResourceBundle requested) {
1257         UResourceBundle res = null;
1258         Integer indexKey = null;
1259         if (lookup != null) {
1260             indexKey = Integer.valueOf(index);
1261             res = lookup.get(indexKey);
1262         } 
1263         if (res == null) {
1264             boolean[] alias = new boolean[1];
1265             res = handleGetImpl(index, table, requested, alias);
1266             if (res != null && lookup != null && !alias[0]) {
1267                 // We do not want to cache a result from alias entry
1268                 lookup.put(res.getKey(), res);
1269                 lookup.put(indexKey, res);
1270             }
1271         }
1272         return res;
1273     }
1274
1275     // Subclass which supports key based resource access to implement this method
1276     protected UResourceBundle handleGetImpl(String resKey, HashMap<String, String> table, UResourceBundle requested,
1277             int[] index, boolean[] isAlias) {
1278         return null;
1279     }
1280
1281     // Subclass which supports index based resource access to implement this method
1282     protected UResourceBundle handleGetImpl(int index, HashMap<String, String> table, UResourceBundle requested,
1283             boolean[] isAlias) {
1284         return null;
1285     }
1286
1287
1288      // TODO Below is a set of workarounds created for org.unicode.cldr.icu.ICU2LDMLWriter
1289      /* 
1290       * Calling getKeys() on a table that has alias's can throw a NullPointerException if parent is not set, 
1291       * see trac bug: 6514
1292       * -Brian Rower - IBM - Sept. 2008
1293       */
1294     
1295     /**
1296      * Returns the resource handle for the given key within the calling resource table.
1297      * 
1298      * @internal
1299      * @deprecated This API is ICU internal only and a workaround see ticket #6514.
1300      * @author Brian Rower
1301      */
1302     protected int getTableResource(String resKey) {
1303         return RES_BOGUS;
1304     }
1305     protected int getTableResource(int index) {
1306         return RES_BOGUS;
1307     }
1308
1309     /**
1310      * Determines if the object at the specified index of the calling resource table
1311      * is an alias. If it is, returns true
1312      * 
1313      * @param index The index of the resource to check
1314      * @returns True if the resource at 'index' is an alias, false otherwise.
1315      * 
1316      * @internal
1317      * @deprecated This API is ICU internal only and part of a work around see ticket #6514
1318      * @author Brian Rower
1319      */
1320     public boolean isAlias(int index)
1321     {
1322         //TODO this is part of a workaround for ticket #6514
1323         //if index is out of the resource, return false.
1324         return ICUResourceBundleReader.RES_GET_TYPE(getTableResource(index)) == ALIAS;
1325     }
1326
1327     /**
1328      * 
1329      * @internal
1330      * @deprecated This API is ICU internal only and part of a workaround see ticket #6514.
1331      * @author Brian Rower
1332      */
1333     public boolean isAlias()
1334     {
1335         //TODO this is part of a workaround for ticket #6514
1336         return ICUResourceBundleReader.RES_GET_TYPE(resource) == ALIAS;
1337     }
1338
1339     /**
1340      * Determines if the object with the specified key 
1341      * is an alias. If it is, returns true
1342      * 
1343      * @returns True if the resource with 'key' is an alias, false otherwise.
1344      * 
1345      * @internal
1346      * @deprecated This API is ICU internal only and part of a workaround see ticket #6514.
1347      * @author Brian Rower
1348      */
1349     public boolean isAlias(String k)
1350     {
1351         //TODO this is part of a workaround for ticket #6514
1352         //this only applies to tables
1353         return ICUResourceBundleReader.RES_GET_TYPE(getTableResource(k)) == ALIAS;
1354     }
1355
1356     /**
1357      * This method can be used to retrieve the underlying alias path (aka where the alias points to)
1358      * This method was written to allow conversion from ICU back to LDML format.
1359      * 
1360      * @param index The index where the alias path points to.
1361      * @return The alias path.
1362      * @author Brian Rower
1363      * @internal
1364      * @deprecated This API is ICU internal only.
1365      * @author Brian Rower
1366      */
1367     public String getAliasPath(int index)
1368     {
1369         return getAliasValue(getTableResource(index));
1370     }
1371
1372     /**
1373      * 
1374      * @internal
1375      * @deprecated This API is ICU internal only
1376      * @author Brian Rower
1377      */
1378     public String getAliasPath()
1379     {
1380         //TODO cannot allow alias path to end up in public API
1381         return getAliasValue(resource);
1382     }
1383
1384     /**
1385      * 
1386      * @internal
1387      * @deprecated This API is ICU internal only
1388      * @author Brian Rower
1389      */
1390     public String getAliasPath(String k)
1391     {
1392         //TODO cannot allow alias path to end up in public API
1393         return getAliasValue(getTableResource(k));
1394     }
1395     
1396     /*
1397      * Helper method for getKeysSafe
1398      */
1399     protected String getKey(int index) {
1400         return null;
1401     }
1402
1403     /**
1404      * Returns an Enumeration of the keys belonging to this table or array.
1405      * This method differs from the getKeys() method by not following alias paths. This method exposes 
1406      * underlying alias's. For all general purposes of the ICU resource bundle please use getKeys().
1407      * 
1408      * @return Keys in this table or array.
1409      * @internal
1410      * @deprecated This API is ICU internal only and a workaround see ticket #6514.
1411      * @author Brian Rower
1412      */
1413     public Enumeration<String> getKeysSafe()
1414     {
1415         //TODO this is part of a workaround for ticket #6514
1416         //the safeness only applies to tables, so use the other method if it's not a table
1417         if(!ICUResourceBundleReader.URES_IS_TABLE(resource))
1418         {
1419             return getKeys();
1420         }
1421         List<String> v = new ArrayList<String>();
1422         int size = getSize();
1423         for(int index = 0; index < size; index++)
1424         {
1425             String curKey = getKey(index); 
1426             v.add(curKey);
1427         }
1428
1429         //TODO we should use Iterator or List as the return type
1430         // instead of Enumeration
1431
1432         return Collections.enumeration(v);
1433     }
1434
1435     // This is the worker function for the public getKeys().
1436     // TODO: Now that UResourceBundle uses handleKeySet(), this function is obsolete.
1437     // It is also not inherited from ResourceBundle, and it is not implemented
1438     // by ResourceBundleWrapper despite its documentation requiring all subclasses to
1439     // implement it.
1440     // Consider deprecating UResourceBundle.handleGetKeys(), and consider making it always return null.
1441     protected Enumeration<String> handleGetKeys() {
1442         return Collections.enumeration(handleKeySet());
1443     }
1444
1445     protected boolean isTopLevelResource() {
1446         return resPath.length() == 0;
1447     }
1448 }