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