]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/classes/core/src/com/ibm/icu/impl/ICUResourceBundle.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / classes / core / src / com / ibm / icu / impl / ICUResourceBundle.java
1 /*\r
2  * *****************************************************************************\r
3  * Copyright (C) 2005-2010, International Business Machines Corporation and    *\r
4  * others. All Rights Reserved.                                                *\r
5  * *****************************************************************************\r
6  */\r
7 \r
8 package com.ibm.icu.impl;\r
9 \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
30 \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
37 \r
38 public  class ICUResourceBundle extends UResourceBundle {\r
39     /**\r
40      * The data path to be used with getBundleInstance API\r
41      */\r
42     protected static final String ICU_DATA_PATH = "com/ibm/icu/impl/";\r
43     /**\r
44      * The data path to be used with getBundleInstance API\r
45      */\r
46     public static final String ICU_BUNDLE = "data/icudt" + VersionInfo.ICU_DATA_VERSION_PATH;\r
47 \r
48     /**\r
49      * The base name of ICU data to be used with getBundleInstance API\r
50      */\r
51     public static final String ICU_BASE_NAME = ICU_DATA_PATH + ICU_BUNDLE;\r
52 \r
53     /**\r
54      * The base name of collation data to be used with getBundleInstance API\r
55      */\r
56     public static final String ICU_COLLATION_BASE_NAME = ICU_BASE_NAME + "/coll";\r
57 \r
58     /**\r
59      * The base name of rbbi data to be used with getData API\r
60      */\r
61     public static final String ICU_BRKITR_NAME = "/brkitr";\r
62 \r
63     /**\r
64      * The base name of rbbi data to be used with getBundleInstance API\r
65      */\r
66     public static final String ICU_BRKITR_BASE_NAME = ICU_BASE_NAME + ICU_BRKITR_NAME;\r
67 \r
68     /**\r
69      * The base name of rbnf data to be used with getBundleInstance API\r
70      */\r
71     public static final String ICU_RBNF_BASE_NAME = ICU_BASE_NAME + "/rbnf";\r
72 \r
73     /**\r
74      * The base name of transliterator data to be used with getBundleInstance API\r
75      */\r
76     public static final String ICU_TRANSLIT_BASE_NAME = ICU_BASE_NAME + "/translit";\r
77 \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
82 \r
83     /**\r
84      * The actual path of the resource\r
85      */\r
86     protected String resPath;\r
87 \r
88     /**\r
89      * The class loader constant to be used with getBundleInstance API\r
90      */\r
91     public static final ClassLoader ICU_DATA_CLASS_LOADER;\r
92     static {\r
93         ClassLoader loader = ICUData.class.getClassLoader();\r
94         if (loader == null) {\r
95             loader = Utility.getFallbackClassLoader();\r
96         }\r
97         ICU_DATA_CLASS_LOADER = loader;\r
98     }\r
99 \r
100     /**\r
101      * The name of the resource containing the installed locales\r
102      */\r
103     protected static final String INSTALLED_LOCALES = "InstalledLocales";\r
104 \r
105     public static final int FROM_FALLBACK = 1, FROM_ROOT = 2, FROM_DEFAULT = 3, FROM_LOCALE = 4;\r
106 \r
107     private int loadingStatus = -1;\r
108 \r
109     public void setLoadingStatus(int newStatus) {\r
110         loadingStatus = newStatus;\r
111     }\r
112     /**\r
113      * Returns the loading status of a particular resource.\r
114      *\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
118      */\r
119     public int getLoadingStatus() {\r
120         return loadingStatus;\r
121     }\r
122 \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
129         } else {\r
130             setLoadingStatus(FROM_FALLBACK);\r
131         }\r
132      }\r
133 \r
134     /**\r
135      * Returns the respath of this bundle\r
136      * @return the respath of the bundle\r
137      */\r
138     public String getResPath(){\r
139         return resPath;\r
140     }\r
141 \r
142     /**\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
155      */\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
168 \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
173         }\r
174 \r
175         // Check top level locale first\r
176         ICUResourceBundle r = null;\r
177 \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
185                     break;\r
186                 }\r
187             }\r
188         }\r
189         // determine in which locale (if any) the currently relevant 'default' is\r
190         do {\r
191             try {\r
192                 ICUResourceBundle irb = (ICUResourceBundle) r.get(resName);\r
193                 defStr = irb.getString(DEFAULT_TAG);\r
194                 if (lookForDefault == true) {\r
195                     kwVal = defStr;\r
196                     lookForDefault = false;\r
197                 }\r
198                 defLoc = r.getULocale();\r
199             } catch (MissingResourceException t) {\r
200                 // Ignore error and continue search.\r
201             }\r
202             if (defLoc == null) {\r
203                 r = (ICUResourceBundle) r.getParent();\r
204                 defDepth++;\r
205             }\r
206         } while ((r != null) && (defLoc == null));\r
207 \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
212         do {\r
213             try {\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
224                 }\r
225             } catch (MissingResourceException t) {\r
226                 // Ignore error,\r
227             }\r
228             if (fullBase == null) {\r
229                 r = (ICUResourceBundle) r.getParent();\r
230                 resDepth++;\r
231             }\r
232         } while ((r != null) && (fullBase == null));\r
233 \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
241             resDepth = 0;\r
242             // determine in which locale (if any) the named resource is located\r
243             do {\r
244                 try {\r
245                     ICUResourceBundle irb = (ICUResourceBundle)r.get(resName);\r
246                     UResourceBundle urb = irb.get(kwVal);\r
247 \r
248                     // if we didn't fail before this..\r
249                     fullBase = r.getULocale();\r
250 \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
255                     }\r
256 \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
263                     }\r
264                 } catch (MissingResourceException t) {\r
265                     // Ignore error, continue search.\r
266                 }\r
267                 if (fullBase == null) {\r
268                     r = (ICUResourceBundle) r.getParent();\r
269                     resDepth++;\r
270                 }\r
271             } while ((r != null) && (fullBase == null));\r
272         }\r
273 \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
278         }\r
279 \r
280         if (omitDefault\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
284         } else {\r
285             return new ULocale(fullBase.toString() + "@" + keyword + "=" + kwVal);\r
286         }\r
287     }\r
288 \r
289     /**\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
295      */\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
299         int i;\r
300 \r
301         for (i = 0; i < locales.length; i++) {\r
302             try {\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
311                         keywords.add(s);\r
312                     }\r
313                 }\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
318             }\r
319         }\r
320         return keywords.toArray(new String[0]);\r
321     }\r
322 \r
323     /**\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
332      *  }\r
333      * </code>\r
334      *\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
338      */\r
339     public ICUResourceBundle getWithFallback(String path) throws MissingResourceException {\r
340         ICUResourceBundle result = null;\r
341         ICUResourceBundle actualBundle = this;\r
342 \r
343         // now recurse to pick up sub levels of the items\r
344         result = findResourceWithFallback(path, actualBundle, null);\r
345 \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
350                 path, getKey());\r
351         }\r
352         return result;\r
353     }\r
354     \r
355     public ICUResourceBundle at(int index) {\r
356         return (ICUResourceBundle) handleGet(index, null, this);\r
357     }\r
358     \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
363         }\r
364         return null;\r
365     }\r
366     \r
367     @Override\r
368     public ICUResourceBundle findTopLevel(int index) {\r
369         return (ICUResourceBundle) super.findTopLevel(index);\r
370     }\r
371     \r
372     @Override\r
373     public ICUResourceBundle findTopLevel(String aKey) {\r
374         return (ICUResourceBundle) super.findTopLevel(aKey);\r
375     }\r
376     \r
377     /**\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
382      */\r
383     public ICUResourceBundle findWithFallback(String path) {\r
384         return findResourceWithFallback(path, this, null);\r
385     }\r
386 \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
390     }\r
391 \r
392     /**\r
393      * Return a set of the locale names supported by a collection of resource\r
394      * bundles.\r
395      *\r
396      * @param bundlePrefix the prefix of the resource bundles to use.\r
397      */\r
398     public static Set<String> getAvailableLocaleNameSet(String bundlePrefix, ClassLoader loader) {\r
399         return getAvailEntry(bundlePrefix, loader).getLocaleNameSet();\r
400     }\r
401 \r
402     /**\r
403      * Return a set of all the locale names supported by a collection of\r
404      * resource bundles.\r
405      */\r
406     public static Set<String> getFullLocaleNameSet() {\r
407         return getFullLocaleNameSet(ICU_BASE_NAME, ICU_DATA_CLASS_LOADER);\r
408     }\r
409 \r
410     /**\r
411      * Return a set of all the locale names supported by a collection of\r
412      * resource bundles.\r
413      *\r
414      * @param bundlePrefix the prefix of the resource bundles to use.\r
415      */\r
416     public static Set<String> getFullLocaleNameSet(String bundlePrefix, ClassLoader loader) {\r
417         return getAvailEntry(bundlePrefix, loader).getFullLocaleNameSet();\r
418     }\r
419 \r
420     /**\r
421      * Return a set of the locale names supported by a collection of resource\r
422      * bundles.\r
423      */\r
424     public static Set<String> getAvailableLocaleNameSet() {\r
425         return getAvailableLocaleNameSet(ICU_BASE_NAME, ICU_DATA_CLASS_LOADER);\r
426     }\r
427 \r
428     /**\r
429      * Get the set of Locales installed in the specified bundles.\r
430      * @return the list of available locales\r
431      */\r
432     public static final ULocale[] getAvailableULocales(String baseName, ClassLoader loader) {\r
433         return getAvailEntry(baseName, loader).getULocaleList();\r
434     }\r
435 \r
436     /**\r
437      * Get the set of ULocales installed the base bundle.\r
438      * @return the list of available locales\r
439      */\r
440     public static final ULocale[] getAvailableULocales() {\r
441         return getAvailableULocales(ICU_BASE_NAME, ICU_DATA_CLASS_LOADER);\r
442     }\r
443 \r
444     /**\r
445      * Get the set of Locales installed in the specified bundles.\r
446      * @return the list of available locales\r
447      */\r
448     public static final Locale[] getAvailableLocales(String baseName, ClassLoader loader) {\r
449         return getAvailEntry(baseName, loader).getLocaleList();\r
450     }\r
451 \r
452    /**\r
453      * Get the set of Locales installed the base bundle.\r
454      * @return the list of available locales\r
455      */\r
456     public static final Locale[] getAvailableLocales() {\r
457         return getAvailEntry(ICU_BASE_NAME, ICU_DATA_CLASS_LOADER).getLocaleList();\r
458     }\r
459 \r
460     /**\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
466      */\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
473                 list.add(loc);\r
474                 uniqueSet.add(loc);\r
475             }\r
476         }\r
477         return list.toArray(new Locale[list.size()]);\r
478     }\r
479 \r
480     /**\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
484      *\r
485      * @return the locale of this resource bundle\r
486      */\r
487     public Locale getLocale() {\r
488         return getULocale().toLocale();\r
489     }\r
490 \r
491 \r
492     // ========== privates ==========\r
493     private static final String ICU_RESOURCE_INDEX = "res_index";\r
494 \r
495     private static final String DEFAULT_TAG = "default";\r
496 \r
497     // Flag for enabling/disabling debugging code\r
498     private static final boolean DEBUG = ICUDebug.enabled("localedata");\r
499 \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
510 \r
511         bundle = (ICUResourceBundle)bundle.get(INSTALLED_LOCALES);\r
512         int length = bundle.getSize();\r
513         int i = 0;\r
514         ULocale[] locales = new ULocale[length];\r
515         UResourceBundleIterator iter = bundle.getIterator();\r
516         iter.reset();\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
521             } else {\r
522                 locales[i++] = new ULocale(locstr);\r
523             }\r
524         }\r
525         bundle = null;\r
526         return locales;\r
527     }\r
528 \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
532     }\r
533 \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
539         int i = 0;\r
540         String[] locales = new String[length];\r
541         UResourceBundleIterator iter = bundle.getIterator();\r
542         iter.reset();\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
547             } else {\r
548                 locales[i++] = locstr;\r
549             }\r
550         }\r
551         bundle = null;\r
552         return locales;\r
553     }\r
554 \r
555     private static final List<String> createFullLocaleNameArray(\r
556             final String baseName, final ClassLoader root) {\r
557 \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
564                         ? baseName\r
565                         : baseName + "/";\r
566 \r
567                     List<String> resList = null;\r
568 \r
569                     // scan available locale resources under the base url first\r
570                     try {\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
586                                                     lst.add(locstr);\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
590                                                     lst.add(locstr);\r
591                                                 } else if (locstr.equalsIgnoreCase("root")) {\r
592                                                     // root locale is a special case\r
593                                                     lst.add(ULocale.ROOT.toString());\r
594                                                 }\r
595                                             }\r
596                                         }\r
597                                     };\r
598                                 handler.guide(v, false);\r
599 \r
600                                 if (resList == null) {\r
601                                     resList = new ArrayList<String>(lst);\r
602                                 } else {\r
603                                     resList.addAll(lst);\r
604                                 }\r
605                             } else {\r
606                                 if (DEBUG) System.out.println("handler for " + url + " is null");\r
607                             }\r
608                         }\r
609                     } catch (IOException e) {\r
610                         if (DEBUG) System.out.println("ouch: " + e.getMessage());\r
611                         resList = null;\r
612                     }\r
613 \r
614                     if (resList == null) {\r
615                         // look for prebuilt indices next\r
616                         try {\r
617                             InputStream s = root.getResourceAsStream(bn + ICU_RESOURCE_INDEX + ".txt");\r
618                             if (s != null) {\r
619                                 resList = new ArrayList<String>();\r
620                                 BufferedReader br = new BufferedReader(new InputStreamReader(s, "ASCII"));\r
621                                 String line;\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
626                                         } else {\r
627                                             resList.add(line);\r
628                                         }\r
629                                     }\r
630                                 }\r
631                             }\r
632                         } catch (IOException e) {\r
633                             // swallow it\r
634                         }\r
635                     }\r
636 \r
637                     return resList;\r
638                 }\r
639             });\r
640 \r
641         return list;\r
642     }\r
643 \r
644     private static Set<String> createFullLocaleNameSet(String baseName, ClassLoader loader) {\r
645         List<String> list = createFullLocaleNameArray(baseName, loader);\r
646         if(list == null){\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
650         }\r
651         HashSet<String> set = new HashSet<String>();\r
652         set.addAll(list);\r
653         return Collections.unmodifiableSet(set);\r
654     }\r
655 \r
656     private static Set<String> createLocaleNameSet(String baseName, ClassLoader loader) {\r
657         try {\r
658             String[] locales = createLocaleNameArray(baseName, loader);\r
659 \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
664             if (DEBUG) {\r
665                 System.out.println("couldn't find index for bundleName: " + baseName);\r
666                 Thread.dumpStack();\r
667             }\r
668         }\r
669         return Collections.emptySet();\r
670     }\r
671 \r
672     /**\r
673      * Holds the prefix, and lazily creates the Locale[] list or the locale name\r
674      * Set as needed.\r
675      */\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
683 \r
684         AvailEntry(String prefix, ClassLoader loader) {\r
685             this.prefix = prefix;\r
686             this.loader = loader;\r
687         }\r
688 \r
689         ULocale[] getULocaleList() {\r
690             if (ulocales == null) {\r
691                 ulocales = createULocaleList(prefix, loader);\r
692             }\r
693             return ulocales;\r
694         }\r
695         Locale[] getLocaleList() {\r
696             if (locales == null) {\r
697               locales = createLocaleList(prefix, loader);\r
698             }\r
699             return locales;\r
700         }\r
701         Set<String> getLocaleNameSet() {\r
702             if (nameSet == null) {\r
703               nameSet = createLocaleNameSet(prefix, loader);\r
704             }\r
705             return nameSet;\r
706         }\r
707         Set<String> getFullLocaleNameSet() {\r
708             if (fullNameSet == null) {\r
709               fullNameSet = createFullLocaleNameSet(prefix, loader);\r
710             }\r
711             return fullNameSet;\r
712         }\r
713     }\r
714 \r
715     /**\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
719      */\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
727             }\r
728         }\r
729 \r
730         if (ae == null) {\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
736             } else {\r
737                 lcache.put(key, ae);\r
738             }\r
739         }\r
740 \r
741         return ae;\r
742     }\r
743 \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
749         }\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
754                 if (sub != null) {\r
755                     current = sub;\r
756                     break;\r
757                 }\r
758             } else {\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
763                     if (sub == null) {\r
764                         break;\r
765                     }\r
766                     current = sub;\r
767                 }\r
768                 if (sub != null) {\r
769                     //we found it\r
770                     break;\r
771                 }\r
772             }\r
773             if (((ICUResourceBundle)actualBundle).resPath.length() != 0) {\r
774                 path = ((ICUResourceBundle)actualBundle).resPath + "/" + path;\r
775             }\r
776             // if not try the parent bundle\r
777             actualBundle = ((ICUResourceBundle) actualBundle).getParent();\r
778 \r
779         }\r
780         if(sub != null){\r
781             sub.setLoadingStatus(((ICUResourceBundle)requested).getLocaleID());\r
782         }\r
783         return sub;\r
784     }\r
785     public boolean equals(Object other) {\r
786         if (this == other) {\r
787             return true;\r
788         }\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
793                 return true;\r
794             }\r
795         }\r
796         return false;\r
797     }\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
802         if(b==null){\r
803             throw new MissingResourceException("Could not find the bundle "+ baseName+"/"+ localeID+".res","","");\r
804         }\r
805         return b;\r
806     }\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
814         }\r
815         String fullName = getFullName(baseName, localeName);\r
816         ICUResourceBundle b = (ICUResourceBundle)loadFromCache(root, fullName, defaultLocale);\r
817 \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
824         //\r
825         final String rootLocale = (baseName.indexOf('.')==-1) ? "root" : "";\r
826         final String defaultID = defaultLocale.toString();\r
827 \r
828         if(localeName.equals("")){\r
829             localeName = rootLocale;\r
830         }\r
831         if(DEBUG) System.out.println("Creating "+fullName+ " currently b is "+b);\r
832         if (b == null) {\r
833             b = ICUResourceBundle.createBundle(baseName, localeName, root);\r
834 \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
839             }\r
840 \r
841             // fallback to locale ID parent\r
842             if(b == null){\r
843                 int i = localeName.lastIndexOf('_');\r
844                 if (i != -1) {\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
849                     }\r
850                 }else{\r
851                     if(defaultID.indexOf(localeName)==-1){\r
852                         b = (ICUResourceBundle)instantiateBundle(baseName, defaultID, root, disableFallback);\r
853                         if(b!=null){\r
854                             b.setLoadingStatus(ICUResourceBundle.FROM_DEFAULT);\r
855                         }\r
856                     }else if(rootLocale.length()!=0){\r
857                         b = ICUResourceBundle.createBundle(baseName, rootLocale, root);\r
858                         if(b!=null){\r
859                             b.setLoadingStatus(ICUResourceBundle.FROM_ROOT);\r
860                         }\r
861                     }\r
862                 }\r
863             }else{\r
864                 UResourceBundle parent = null;\r
865                 localeName = b.getLocaleID();\r
866                 int i = localeName.lastIndexOf('_');\r
867 \r
868                 b = (ICUResourceBundle)addToCache(root, fullName, defaultLocale, b);\r
869 \r
870                 boolean ParentIsRoot = false;\r
871                 if (b.getTableResource("%%ParentIsRoot") != RES_BOGUS) {\r
872                     ParentIsRoot = true;\r
873                 }\r
874 \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
879                 }\r
880 \r
881                 if (!b.equals(parent)){\r
882                     b.setParent(parent);\r
883                 }\r
884             }\r
885         }\r
886         return b;\r
887     }\r
888     UResourceBundle get(String aKey, HashMap<String, String> table, UResourceBundle requested) {\r
889         ICUResourceBundle obj = (ICUResourceBundle)handleGet(aKey, table, requested);\r
890         if (obj == null) {\r
891             obj = (ICUResourceBundle)getParent();\r
892             if (obj != null) {\r
893                 //call the get method to recursively fetch the resource\r
894                 obj = (ICUResourceBundle)obj.get(aKey, table, requested);\r
895             }\r
896             if (obj == null) {\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
901             }\r
902         }\r
903         obj.setLoadingStatus(((ICUResourceBundle)requested).getLocaleID());\r
904         return obj;\r
905     }\r
906 \r
907     private static final String ICU_RESOURCE_SUFFIX = ".res";\r
908     /**\r
909      * Gets the full name of the resource with suffix.\r
910      */\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
915             }\r
916             return localeName+ICU_RESOURCE_SUFFIX;\r
917         }else{\r
918             if(baseName.indexOf('.')==-1){\r
919                 if(baseName.charAt(baseName.length()-1)!= '/'){\r
920                     return baseName+"/"+localeName+ICU_RESOURCE_SUFFIX;\r
921                 }else{\r
922                     return baseName+localeName+ICU_RESOURCE_SUFFIX;   \r
923                 }\r
924             }else{\r
925                 baseName = baseName.replace('.','/');\r
926                 if(localeName.length()==0){\r
927                     return baseName+ICU_RESOURCE_SUFFIX;   \r
928                 }else{\r
929                     return baseName+"_"+localeName+ICU_RESOURCE_SUFFIX;\r
930                 }\r
931             }\r
932         }\r
933     }\r
934 \r
935     protected String localeID;\r
936     protected String baseName;\r
937     protected ULocale ulocale;\r
938     protected ClassLoader loader;\r
939 \r
940     /**\r
941      * Access to the bits and bytes of the resource bundle.\r
942      * Hides low-level details.\r
943      */\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
949 \r
950     /**\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
957      */\r
958     public static final int RES_BOGUS = 0xffffffff;\r
959 \r
960     /**\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
965      */\r
966     public static final int ALIAS = 3;\r
967 \r
968     /** Resource type constant for tables with 32-bit count, key offsets and values. */\r
969     public static final int TABLE32 = 4;\r
970 \r
971     /**\r
972      * Resource type constant for tables with 16-bit count, key offsets and values.\r
973      * All values are STRING_V2 strings.\r
974      */\r
975     public static final int TABLE16 = 5;\r
976 \r
977     /** Resource type constant for 16-bit Unicode strings in formatVersion 2. */\r
978     public static final int STRING_V2 = 6;\r
979 \r
980     /**\r
981      * Resource type constant for arrays with 16-bit count and values.\r
982      * All values are STRING_V2 strings.\r
983      */\r
984     public static final int ARRAY16 = 9;\r
985 \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
991             return 0;\r
992         }\r
993         public boolean equals(Object rhs) {\r
994             return this == rhs;\r
995         }\r
996     };\r
997     \r
998    /**\r
999     *\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
1004     */\r
1005     public static ICUResourceBundle createBundle(String baseName, String localeID, \r
1006             ClassLoader root) {\r
1007         \r
1008         String resKey = Integer.toHexString(root.hashCode()) + baseName + localeID;\r
1009         ICUResourceBundle b = cache.get(resKey);\r
1010         if (b == null) {\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
1015                 b = NULL_BUNDLE;\r
1016             } else {\r
1017                 b = getBundle(reader, baseName, localeID, root);\r
1018             }\r
1019             cache.put(resKey, b);\r
1020         }\r
1021         return b == NULL_BUNDLE ? null : b;\r
1022     }\r
1023 \r
1024     protected String getLocaleID() {\r
1025         return localeID;\r
1026     }\r
1027 \r
1028     protected String getBaseName() {\r
1029         return baseName;\r
1030     }\r
1031 \r
1032     public ULocale getULocale() {\r
1033         return ulocale;\r
1034     }\r
1035 \r
1036     public UResourceBundle getParent() {\r
1037         return (UResourceBundle) parent;\r
1038     }\r
1039 \r
1040     protected void setParent(ResourceBundle parent) {\r
1041         this.parent = parent;\r
1042     }\r
1043 \r
1044     public String getKey() {\r
1045         return key;\r
1046     }\r
1047 \r
1048     private static final int[] gPublicTypes = new int[] {\r
1049         STRING,\r
1050         BINARY,\r
1051         TABLE,\r
1052         ALIAS,\r
1053 \r
1054         TABLE,      /* TABLE32 */\r
1055         TABLE,      /* TABLE16 */\r
1056         STRING,     /* STRING_V2 */\r
1057         INT,\r
1058 \r
1059         ARRAY,\r
1060         ARRAY,      /* ARRAY16 */\r
1061         NONE,\r
1062         NONE,\r
1063 \r
1064         NONE,\r
1065         NONE,\r
1066         INT_VECTOR,\r
1067         NONE\r
1068     };\r
1069 \r
1070     public int getType() {\r
1071         return gPublicTypes[ICUResourceBundleReader.RES_GET_TYPE(resource)];\r
1072     }\r
1073 \r
1074     /**\r
1075      * Get the noFallback flag specified in the loaded bundle.\r
1076      * @return The noFallback flag.\r
1077      */\r
1078     private boolean getNoFallback() {\r
1079         return reader.getNoFallback();\r
1080     }\r
1081 \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
1089         } else {\r
1090             throw new IllegalStateException("Invalid format error");\r
1091         }\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
1099         }\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
1103         } else {\r
1104             return bundle;\r
1105         }\r
1106     }\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
1111         this.key = key;\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
1120         }\r
1121     }\r
1122 \r
1123     private String getAliasValue(int res) {\r
1124         String result = reader.getAlias(res);\r
1125         return result != null ? result : "";\r
1126     }\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
1132 \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
1142         }\r
1143         if (table.get(rpath) != null) {\r
1144             throw new IllegalArgumentException(\r
1145                     "Circular references in the resource bundles");\r
1146         }\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
1152             if (j < 0) {\r
1153                 locale = rpath.substring(i + 1);\r
1154             } else {\r
1155                 locale = rpath.substring(i + 1, j);\r
1156                 keyPath = rpath.substring(j + 1, rpath.length());\r
1157             }\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
1164                 if(idx>-1){\r
1165                     bundleName = ICU_BASE_NAME+RES_PATH_SEP_STR+bundleName.substring(idx+1,bundleName.length());\r
1166                     loaderToUse = ICU_DATA_CLASS_LOADER;\r
1167                 }\r
1168             }\r
1169         } else {\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
1173             if (i != -1) {\r
1174                 locale = rpath.substring(0, i);\r
1175             } else {\r
1176                 locale = keyPath;\r
1177                 keyPath = null;//keyPath.substring(i, keyPath.length());\r
1178             }\r
1179             bundleName = baseName;\r
1180         }\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
1191             }\r
1192         }else{\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
1197             } else {\r
1198                 bundle = (ICUResourceBundle) getBundleInstance(bundleName, locale,\r
1199                          loaderToUse, false);\r
1200             }\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
1208                         break;\r
1209                     }\r
1210                     current = sub;\r
1211                 }\r
1212             } else {\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
1217             }\r
1218             if (sub != null) {\r
1219                 sub.resPath = rpath;\r
1220             }\r
1221         }\r
1222         if (sub == null) {\r
1223             throw new MissingResourceException(localeID, baseName, _key);\r
1224         }\r
1225         return sub;\r
1226     }\r
1227 \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
1232 \r
1233     protected void createLookupCache() {\r
1234         lookup = new SimpleCache<Object, UResourceBundle>(ICUCache.WEAK, Math.max(getSize()*2, MAX_INITIAL_LOOKUP_SIZE));\r
1235     }\r
1236 \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
1241         }\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
1250             }\r
1251         }\r
1252         return res;\r
1253     }\r
1254 \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
1261         }\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
1269             }\r
1270         }\r
1271         return res;\r
1272     }\r
1273 \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
1277         return null;\r
1278     }\r
1279 \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
1283         return null;\r
1284     }\r
1285 \r
1286 \r
1287      // TODO Below is a set of workarounds created for org.unicode.cldr.icu.ICU2LDMLWriter\r
1288      /* \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
1292       */\r
1293     \r
1294     /**\r
1295      * Returns the resource handle for the given key within the calling resource table.\r
1296      * \r
1297      * @internal\r
1298      * @deprecated This API is ICU internal only and a workaround see ticket #6514.\r
1299      * @author Brian Rower\r
1300      */\r
1301     protected int getTableResource(String resKey) {\r
1302         return RES_BOGUS;\r
1303     }\r
1304     protected int getTableResource(int index) {\r
1305         return RES_BOGUS;\r
1306     }\r
1307 \r
1308     /**\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
1311      * \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
1314      * \r
1315      * @internal\r
1316      * @deprecated This API is ICU internal only and part of a work around see ticket #6514\r
1317      * @author Brian Rower\r
1318      */\r
1319     public boolean isAlias(int index)\r
1320     {\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
1324     }\r
1325 \r
1326     /**\r
1327      * \r
1328      * @internal\r
1329      * @deprecated This API is ICU internal only and part of a workaround see ticket #6514.\r
1330      * @author Brian Rower\r
1331      */\r
1332     public boolean isAlias()\r
1333     {\r
1334         //TODO this is part of a workaround for ticket #6514\r
1335         return ICUResourceBundleReader.RES_GET_TYPE(resource) == ALIAS;\r
1336     }\r
1337 \r
1338     /**\r
1339      * Determines if the object with the specified key \r
1340      * is an alias. If it is, returns true\r
1341      * \r
1342      * @returns True if the resource with 'key' is an alias, false otherwise.\r
1343      * \r
1344      * @internal\r
1345      * @deprecated This API is ICU internal only and part of a workaround see ticket #6514.\r
1346      * @author Brian Rower\r
1347      */\r
1348     public boolean isAlias(String k)\r
1349     {\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
1353     }\r
1354 \r
1355     /**\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
1358      * \r
1359      * @param index The index where the alias path points to.\r
1360      * @return The alias path.\r
1361      * @author Brian Rower\r
1362      * @internal\r
1363      * @deprecated This API is ICU internal only.\r
1364      * @author Brian Rower\r
1365      */\r
1366     public String getAliasPath(int index)\r
1367     {\r
1368         return getAliasValue(getTableResource(index));\r
1369     }\r
1370 \r
1371     /**\r
1372      * \r
1373      * @internal\r
1374      * @deprecated This API is ICU internal only\r
1375      * @author Brian Rower\r
1376      */\r
1377     public String getAliasPath()\r
1378     {\r
1379         //TODO cannot allow alias path to end up in public API\r
1380         return getAliasValue(resource);\r
1381     }\r
1382 \r
1383     /**\r
1384      * \r
1385      * @internal\r
1386      * @deprecated This API is ICU internal only\r
1387      * @author Brian Rower\r
1388      */\r
1389     public String getAliasPath(String k)\r
1390     {\r
1391         //TODO cannot allow alias path to end up in public API\r
1392         return getAliasValue(getTableResource(k));\r
1393     }\r
1394     \r
1395     /*\r
1396      * Helper method for getKeysSafe\r
1397      */\r
1398     protected String getKey(int index) {\r
1399         return null;\r
1400     }\r
1401 \r
1402     /**\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
1406      * \r
1407      * @return Keys in this table or array.\r
1408      * @internal\r
1409      * @deprecated This API is ICU internal only and a workaround see ticket #6514.\r
1410      * @author Brian Rower\r
1411      */\r
1412     public Enumeration<String> getKeysSafe()\r
1413     {\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
1417         {\r
1418             return getKeys();\r
1419         }\r
1420         Vector<String> v = new Vector<String>();\r
1421         int size = getSize();\r
1422         for(int index = 0; index < size; index++)\r
1423         {\r
1424             String curKey = getKey(index); \r
1425             v.add(curKey);\r
1426         }\r
1427         return v.elements();\r
1428     }\r
1429 \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
1434     // implement it.\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
1438     }\r
1439 \r
1440     protected boolean isTopLevelResource() {\r
1441         return resPath.length() == 0;\r
1442     }\r
1443 }\r