-//##header\r
-/*\r
- *******************************************************************************\r
- * Copyright (C) 2004-2009, International Business Machines Corporation and *\r
- * others. All Rights Reserved. *\r
- *******************************************************************************\r
- */\r
-\r
-package com.ibm.icu.util;\r
-\r
-import java.lang.ref.SoftReference;\r
-import java.util.Enumeration;\r
-import java.util.HashMap;\r
-import java.util.Locale;\r
-import java.util.Map;\r
-import java.util.MissingResourceException;\r
-import java.util.ResourceBundle;\r
-import java.util.Vector;\r
-\r
-import com.ibm.icu.impl.ICUCache;\r
-import com.ibm.icu.impl.ICUResourceBundle;\r
-import com.ibm.icu.impl.ICUResourceBundleReader;\r
-import com.ibm.icu.impl.ResourceBundleWrapper;\r
-import com.ibm.icu.impl.SimpleCache;\r
-import com.ibm.icu.util.ULocale;\r
-\r
-//#if defined(FOUNDATION10) || defined(J2SE13) || defined(ECLIPSE_FRAGMENT)\r
-//##import com.ibm.icu.impl.ByteBuffer;\r
-//#else\r
-import java.nio.ByteBuffer;\r
-//#endif\r
-\r
-/**\r
- * A class representing a collection of resource information pertaining to a given\r
- * locale. A resource bundle provides a way of accessing locale- specific information in\r
- * a data file. You create a resource bundle that manages the resources for a given\r
- * locale and then ask it for individual resources.\r
- * <P>\r
- * In ResourceBundle class, an object is created\r
- * and the sub items are fetched using getString, getObject methods.\r
- * In UResourceBundle,each individual element of a resource is a resource by itself.\r
- *\r
- * <P>\r
- * Resource bundles in ICU are currently defined using text files which conform to the following\r
- * <a href="http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt">BNF definition</a>.\r
- * More on resource bundle concepts and syntax can be found in the\r
- * <a href="http://www.icu-project.org/userguide/ResourceManagement.html">Users Guide</a>.\r
- * <P>\r
- *\r
- * The packaging of ICU *.res files can be of two types\r
- * ICU4C:\r
- * <pre>\r
- * root.res\r
- * |\r
- * --------\r
- * | |\r
- * fr.res en.res\r
- * |\r
- * --------\r
- * | |\r
- * fr_CA.res fr_FR.res\r
- * </pre>\r
- * JAVA/JDK:\r
- * <pre>\r
- * LocaleElements.res\r
- * |\r
- * -------------------\r
- * | |\r
- * LocaleElements_fr.res LocaleElements_en.res\r
- * |\r
- * ---------------------------\r
- * | |\r
- * LocaleElements_fr_CA.res LocaleElements_fr_FR.res\r
- * </pre>\r
- * Depending on the organization of your resources, the syntax to getBundleInstance will change.\r
- * To open ICU style organization use:\r
- * <pre>\r
- * UResourceBundle bundle = UResourceBundle.getBundleInstance("com/mycompany/resources", "en_US", myClassLoader);\r
- * </pre>\r
- * To open Java/JDK style organization use:\r
- * <pre>\r
- * UResourceBundle bundle = UResourceBundle.getBundleInstance("com.mycompany.resources.LocaleElements", "en_US", myClassLoader);\r
- * </pre>\r
- * <note>\r
- * Please use pass a class loader for loading non-ICU resources. Java security does not\r
- * allow loading of resources across jar files. You must provide your class loader\r
- * to load the resources\r
- * </note>\r
- * @stable ICU 3.0\r
- * @author ram\r
- */\r
-public abstract class UResourceBundle extends ResourceBundle{\r
-\r
-\r
- /**\r
- * Creates a resource bundle using the specified base name and locale.\r
- * ICU_DATA_CLASS is used as the default root.\r
- * @param baseName the base name of the resource bundle, a fully qualified class name\r
- * @param localeName the locale for which a resource bundle is desired\r
- * @exception MissingResourceException\r
- * if no resource bundle for the specified base name can be found\r
- * @return a resource bundle for the given base name and locale\r
- * @stable ICU 3.0\r
- */\r
- public static UResourceBundle getBundleInstance(String baseName, String localeName){\r
- return getBundleInstance(baseName, localeName, ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);\r
- }\r
-\r
- /**\r
- * Creates a resource bundle using the specified base name, locale, and class root.\r
- *\r
- * @param baseName the base name of the resource bundle, a fully qualified class name\r
- * @param localeName the locale for which a resource bundle is desired\r
- * @param root the class object from which to load the resource bundle\r
- * @exception MissingResourceException\r
- * if no resource bundle for the specified base name can be found\r
- * @return a resource bundle for the given base name and locale\r
- * @stable ICU 3.0\r
- */\r
- public static UResourceBundle getBundleInstance(String baseName, String localeName, ClassLoader root){\r
- return getBundleInstance(baseName, localeName, root, false);\r
- }\r
-\r
- /**\r
- * Creates a resource bundle using the specified base name, locale, and class root.\r
- *\r
- * @param baseName the base name of the resource bundle, a fully qualified class name\r
- * @param localeName the locale for which a resource bundle is desired\r
- * @param root the class object from which to load the resource bundle\r
- * @param disableFallback Option to disable locale inheritence.\r
- * If true the fallback chain will not be built.\r
- * @exception MissingResourceException\r
- * if no resource bundle for the specified base name can be found\r
- * @return a resource bundle for the given base name and locale\r
- * @stable ICU 3.0\r
- *\r
- */\r
- protected static UResourceBundle getBundleInstance(String baseName, String localeName, ClassLoader root, boolean disableFallback) {\r
- return instantiateBundle(baseName, localeName, root, disableFallback);\r
- }\r
-\r
- /**\r
- * Sole constructor. (For invocation by subclass constructors, typically\r
- * implicit.) This is public for compatibility with Java, whose compiler\r
- * will generate public default constructors for an abstract class.\r
- * @stable ICU 3.0\r
- */\r
- public UResourceBundle() {\r
- }\r
-\r
- /**\r
- * Creates a UResourceBundle for the locale specified, from which users can extract resources by using\r
- * their corresponding keys.\r
- * @param locale specifies the locale for which we want to open the resource.\r
- * If null the bundle for default locale is opened.\r
- * @return a resource bundle for the given locale\r
- * @stable ICU 3.0\r
- */\r
- public static UResourceBundle getBundleInstance(ULocale locale) {\r
- if (locale==null) {\r
- locale = ULocale.getDefault();\r
- }\r
- return getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale.toString(), ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);\r
- }\r
-\r
- /**\r
- * Creates a UResourceBundle for the default locale and specified base name,\r
- * from which users can extract resources by using their corresponding keys.\r
- * @param baseName specifies the locale for which we want to open the resource.\r
- * If null the bundle for default locale is opened.\r
- * @return a resource bundle for the given base name and default locale\r
- * @stable ICU 3.0\r
- */\r
- public static UResourceBundle getBundleInstance(String baseName) {\r
- if (baseName == null) {\r
- baseName = ICUResourceBundle.ICU_BASE_NAME;\r
- }\r
- ULocale uloc = ULocale.getDefault();\r
- return getBundleInstance(baseName, uloc.toString(), ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);\r
- }\r
-\r
- /**\r
- * Creates a UResourceBundle for the specified locale and specified base name,\r
- * from which users can extract resources by using their corresponding keys.\r
- * @param baseName specifies the locale for which we want to open the resource.\r
- * If null the bundle for default locale is opened.\r
- * @param locale specifies the locale for which we want to open the resource.\r
- * If null the bundle for default locale is opened.\r
- * @return a resource bundle for the given base name and locale\r
- * @stable ICU 3.0\r
- */\r
-\r
- public static UResourceBundle getBundleInstance(String baseName, Locale locale) {\r
- if (baseName == null) {\r
- baseName = ICUResourceBundle.ICU_BASE_NAME;\r
- }\r
- ULocale uloc = locale == null ? ULocale.getDefault() : ULocale.forLocale(locale);\r
-\r
- return getBundleInstance(baseName, uloc.toString(), ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);\r
- }\r
-\r
- /**\r
- * Creates a UResourceBundle, from which users can extract resources by using\r
- * their corresponding keys.\r
- * @param baseName string containing the name of the data package.\r
- * If null the default ICU package name is used.\r
- * @param locale specifies the locale for which we want to open the resource.\r
- * If null the bundle for default locale is opened.\r
- * @return a resource bundle for the given base name and locale\r
- * @stable ICU 3.0\r
- */\r
- public static UResourceBundle getBundleInstance(String baseName, ULocale locale) {\r
- if (baseName == null) {\r
- baseName = ICUResourceBundle.ICU_BASE_NAME;\r
- }\r
- if (locale == null) {\r
- locale = ULocale.getDefault();\r
- }\r
- return getBundleInstance(baseName, locale.toString(), ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);\r
- }\r
-\r
- /**\r
- * Creates a UResourceBundle for the specified locale and specified base name,\r
- * from which users can extract resources by using their corresponding keys.\r
- * @param baseName specifies the locale for which we want to open the resource.\r
- * If null the bundle for default locale is opened.\r
- * @param locale specifies the locale for which we want to open the resource.\r
- * If null the bundle for default locale is opened.\r
- * @param loader the loader to use\r
- * @return a resource bundle for the given base name and locale\r
- * @stable ICU 3.8\r
- */\r
- public static UResourceBundle getBundleInstance(String baseName, Locale locale, ClassLoader loader) {\r
- if (baseName == null) {\r
- baseName = ICUResourceBundle.ICU_BASE_NAME;\r
- }\r
- ULocale uloc = locale == null ? ULocale.getDefault() : ULocale.forLocale(locale);\r
- return getBundleInstance(baseName, uloc.toString(), loader, false);\r
- }\r
-\r
- /**\r
- * Creates a UResourceBundle, from which users can extract resources by using\r
- * their corresponding keys.<br><br>\r
- * Note: Please use this API for loading non-ICU resources. Java security does not\r
- * allow loading of resources across jar files. You must provide your class loader\r
- * to load the resources\r
- * @param baseName string containing the name of the data package.\r
- * If null the default ICU package name is used.\r
- * @param locale specifies the locale for which we want to open the resource.\r
- * If null the bundle for default locale is opened.\r
- * @param loader the loader to use\r
- * @return a resource bundle for the given base name and locale\r
- * @stable ICU 3.8\r
- */\r
- public static UResourceBundle getBundleInstance(String baseName, ULocale locale, ClassLoader loader) {\r
- if (baseName == null) {\r
- baseName = ICUResourceBundle.ICU_BASE_NAME;\r
- }\r
- if (locale == null) {\r
- locale = ULocale.getDefault();\r
- }\r
- return getBundleInstance(baseName, locale.toString(), loader, false);\r
- }\r
-\r
- /**\r
- * Returns the RFC 3066 conformant locale id of this resource bundle.\r
- * This method can be used after a call to getBundleInstance() to\r
- * determine whether the resource bundle returned really\r
- * corresponds to the requested locale or is a fallback.\r
- *\r
- * @return the locale of this resource bundle\r
- * @stable ICU 3.0\r
- */\r
- public abstract ULocale getULocale();\r
-\r
- /**\r
- * Gets the localeID\r
- * @return The string representation of the localeID\r
- * @stable ICU 3.0\r
- */\r
- protected abstract String getLocaleID();\r
- /**\r
- * Gets the base name of the resource bundle\r
- * @return The string representation of the base name\r
- * @stable ICU 3.0\r
- */\r
- protected abstract String getBaseName();\r
-\r
- /**\r
- * Gets the parent bundle\r
- * @return The parent bundle\r
- * @stable ICU 3.0\r
- */\r
- protected abstract UResourceBundle getParent();\r
-\r
-\r
- /**\r
- * Get the locale of this bundle\r
- * @return the locale of this resource bundle\r
- * @stable ICU 3.0\r
- */\r
- public Locale getLocale(){\r
- return getULocale().toLocale();\r
- }\r
-\r
- // Cache for ResourceBundle instantiation\r
- private static ICUCache BUNDLE_CACHE = new SimpleCache();\r
-\r
- /**\r
- * @internal\r
- */\r
- public static void resetBundleCache()\r
- {\r
- /*\r
- * A HACK!!!!!\r
- * Currently if a resourcebundle with fallback turned ON is added to the cache\r
- * and then a getBundleInstance() is called for a bundle with fallback turned OFF\r
- * it will actually search the cache for any bundle of the same locale\r
- * regaurdless of fallback status. This method has been created so that if\r
- * The calling method KNOWS that instances of the other fallback state may be in the \r
- * cache, the calling method may call this method to clear out the cache.\r
- *\r
- */\r
- //TODO figure a way around this method(see method comment)\r
- BUNDLE_CACHE = new SimpleCache();\r
- }\r
- \r
- private static void addToCache(ResourceCacheKey key, UResourceBundle b) {\r
- BUNDLE_CACHE.put(key, b);\r
- }\r
-\r
- /**\r
- * Method used by subclasses to add the a particular resource bundle object to the managed cache\r
- * @internal revisit for ICU 3.6\r
- * @deprecated This API is ICU internal only.\r
- */\r
- protected static void addToCache(ClassLoader cl, String fullName, ULocale defaultLocale, UResourceBundle b){\r
- synchronized(cacheKey){\r
- cacheKey.setKeyValues(cl, fullName, defaultLocale);\r
- addToCache((ResourceCacheKey)cacheKey.clone(), b);\r
- }\r
- }\r
- /**\r
- * Method used by sub classes to load a resource bundle object from the managed cache\r
- * @internal revisit for ICU 3.6\r
- * @deprecated This API is ICU internal only.\r
- */\r
- protected static UResourceBundle loadFromCache(ClassLoader cl, String fullName, ULocale defaultLocale){\r
- synchronized(cacheKey){\r
- cacheKey.setKeyValues(cl, fullName, defaultLocale);\r
- return loadFromCache(cacheKey);\r
- }\r
- }\r
- private static UResourceBundle loadFromCache(ResourceCacheKey key) {\r
- return (UResourceBundle)BUNDLE_CACHE.get(key);\r
- }\r
-\r
- /**\r
- * Key used for cached resource bundles. The key checks\r
- * the resource name, the class root, and the default\r
- * locale to determine if the resource is a match to the\r
- * requested one. The root may be null, but the\r
- * searchName and the default locale must have a non-null value.\r
- * Note that the default locale may change over time, and\r
- * lookup should always be based on the current default\r
- * locale (if at all).\r
- */\r
- private static final class ResourceCacheKey implements Cloneable {\r
- private SoftReference loaderRef;\r
- private String searchName;\r
- private ULocale defaultLocale;\r
- private int hashCodeCache;\r
- ///CLOVER:OFF\r
- public boolean equals(Object other) {\r
- if (this == other) {\r
- return true;\r
- }\r
- try {\r
- final ResourceCacheKey otherEntry = (ResourceCacheKey) other;\r
- //quick check to see if they are not equal\r
- if (hashCodeCache != otherEntry.hashCodeCache) {\r
- return false;\r
- }\r
- //are the names the same?\r
- if (!searchName.equals(otherEntry.searchName)) {\r
- return false;\r
- }\r
- // are the default locales the same?\r
- if (defaultLocale == null) {\r
- if (otherEntry.defaultLocale != null) {\r
- return false;\r
- }\r
- } else {\r
- if (!defaultLocale.equals(otherEntry.defaultLocale)) {\r
- return false;\r
- }\r
- }\r
- //are refs (both non-null) or (both null)?\r
- if (loaderRef == null) {\r
- return otherEntry.loaderRef == null;\r
- } else {\r
- return (otherEntry.loaderRef != null)\r
- && (loaderRef.get() == otherEntry.loaderRef.get());\r
- }\r
- } catch (NullPointerException e) {\r
- return false;\r
- } catch (ClassCastException e) {\r
- return false;\r
- }\r
- }\r
- public int hashCode() {\r
- return hashCodeCache;\r
- }\r
- public Object clone() {\r
- try {\r
- return super.clone();\r
- } catch (CloneNotSupportedException e) {\r
- //this should never happen\r
- throw new IllegalStateException();\r
- }\r
- }\r
- ///CLOVER:ON\r
- private synchronized void setKeyValues(ClassLoader root, String searchName, ULocale defaultLocale) {\r
- this.searchName = searchName;\r
- hashCodeCache = searchName.hashCode();\r
- this.defaultLocale = defaultLocale;\r
- if (defaultLocale != null) {\r
- hashCodeCache ^= defaultLocale.hashCode();\r
- }\r
- if (root == null) {\r
- this.loaderRef = null;\r
- } else {\r
- loaderRef = new SoftReference(root);\r
- hashCodeCache ^= root.hashCode();\r
- }\r
- }\r
- /*private void clear() {\r
- setKeyValues(null, "", null);\r
- }*/\r
- }\r
-\r
- private static final ResourceCacheKey cacheKey = new ResourceCacheKey();\r
-\r
- private static final int ROOT_MISSING = 0;\r
- private static final int ROOT_ICU = 1;\r
- private static final int ROOT_JAVA = 2;\r
-\r
- private static SoftReference ROOT_CACHE;\r
-\r
- private static int getRootType(String baseName, ClassLoader root)\r
- {\r
- Map m = null;\r
- Integer rootType;\r
-\r
- if (ROOT_CACHE != null) {\r
- m = (Map) ROOT_CACHE.get();\r
- }\r
-\r
- if (m == null) {\r
- m = new HashMap();\r
- ROOT_CACHE = new SoftReference(m);\r
- }\r
-\r
- rootType = (Integer) m.get(baseName);\r
-\r
- if (rootType == null) {\r
- String rootLocale = (baseName.indexOf('.')==-1) ? "root" : "";\r
- int rt = ROOT_MISSING; // value set on success\r
- try{\r
- ICUResourceBundle.getBundleInstance(baseName, rootLocale, root, true);\r
- rt = ROOT_ICU;\r
- }catch(MissingResourceException ex){\r
- try{\r
- ResourceBundleWrapper.getBundleInstance(baseName, rootLocale, root, true);\r
- rt = ROOT_JAVA;\r
- }catch(MissingResourceException e){\r
- //throw away the exception\r
- }\r
- }\r
-\r
- rootType = new Integer(rt);\r
- m.put(baseName, rootType);\r
- }\r
-\r
- return rootType.intValue();\r
- }\r
-\r
- private static void setRootType(String baseName, int rootType)\r
- {\r
- Integer rt = new Integer(rootType);\r
- Map m = null;\r
-\r
- if (ROOT_CACHE != null) {\r
- m = (Map) ROOT_CACHE.get();\r
- } else {\r
- m = new HashMap();\r
- ROOT_CACHE = new SoftReference(m);\r
- }\r
-\r
- m.put(baseName, rt);\r
- }\r
-\r
- /**\r
- * Loads a new resource bundle for the give base name, locale and class loader.\r
- * Optionally will disable loading of fallback bundles.\r
- * @param baseName the base name of the resource bundle, a fully qualified class name\r
- * @param localeName the locale for which a resource bundle is desired\r
- * @param root the class object from which to load the resource bundle\r
- * @param disableFallback disables loading of fallback lookup chain\r
- * @exception MissingResourceException\r
- * if no resource bundle for the specified base name can be found\r
- * @return a resource bundle for the given base name and locale\r
- * @stable ICU 3.0\r
- */\r
- protected static UResourceBundle instantiateBundle(String baseName, String localeName,\r
- ClassLoader root, boolean disableFallback){\r
- UResourceBundle b = null;\r
- int rootType = getRootType(baseName, root);\r
-\r
- ULocale defaultLocale = ULocale.getDefault();\r
-\r
- switch (rootType)\r
- {\r
- case ROOT_ICU:\r
- if(disableFallback) {\r
- String fullName = ICUResourceBundleReader.getFullName(baseName, localeName);\r
- synchronized(cacheKey){\r
- cacheKey.setKeyValues(root, fullName, defaultLocale);\r
- b = loadFromCache(cacheKey);\r
- }\r
-\r
- if (b == null) {\r
- b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, disableFallback);\r
- //cacheKey.setKeyValues(root, fullName, defaultLocale);\r
- addToCache(cacheKey, b);\r
- }\r
- } else {\r
- b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, disableFallback);\r
- }\r
-\r
- return b;\r
-\r
- case ROOT_JAVA:\r
- return ResourceBundleWrapper.getBundleInstance(baseName, localeName, root, disableFallback);\r
-\r
- default:\r
- try{\r
- b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, disableFallback);\r
- setRootType(baseName, ROOT_ICU);\r
- }catch(MissingResourceException ex){\r
- b = ResourceBundleWrapper.getBundleInstance(baseName, localeName, root, disableFallback);\r
- setRootType(baseName, ROOT_JAVA);\r
- }\r
- return b;\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Returns a binary data from a binary resource.\r
- *\r
- * @return a pointer to a chuck of unsigned bytes which live in a memory mapped/DLL file.\r
- * @see #getIntVector\r
- * @see #getInt\r
- * @throws MissingResourceException\r
- * @throws UResourceTypeMismatchException\r
- * @stable ICU 3.8\r
- */\r
- public ByteBuffer getBinary() {\r
- throw new UResourceTypeMismatchException("");\r
- }\r
-\r
- /**\r
- * Returns a string from a string resource type\r
- *\r
- * @return a string\r
- * @see #getBinary()\r
- * @see #getIntVector\r
- * @see #getInt\r
- * @throws MissingResourceException\r
- * @throws UResourceTypeMismatchException\r
- * @stable ICU 3.8\r
- */\r
- public String getString() {\r
- throw new UResourceTypeMismatchException("");\r
- }\r
-\r
- /**\r
- * Returns a string array from a array resource type\r
- *\r
- * @return a string\r
- * @see #getString()\r
- * @see #getIntVector\r
- * @throws MissingResourceException\r
- * @throws UResourceTypeMismatchException\r
- * @stable ICU 3.8\r
- */\r
- public String[] getStringArray() {\r
- throw new UResourceTypeMismatchException("");\r
- }\r
-\r
- /**\r
- * Returns a binary data from a binary resource.\r
- *\r
- * @param ba The byte array to write the bytes to. A null variable is OK.\r
- * @return an array bytes containing the binary data from the resource.\r
- * @see #getIntVector\r
- * @see #getInt\r
- * @throws MissingResourceException\r
- * @throws UResourceTypeMismatchException\r
- * @stable ICU 3.8\r
- */\r
- public byte[] getBinary(byte[] ba) {\r
- throw new UResourceTypeMismatchException("");\r
- }\r
-\r
- /**\r
- * Returns a 32 bit integer array from a resource.\r
- *\r
- * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL file.\r
- * @see #getBinary()\r
- * @see #getInt\r
- * @throws MissingResourceException\r
- * @throws UResourceTypeMismatchException\r
- * @stable ICU 3.8\r
- */\r
- public int[] getIntVector() {\r
- throw new UResourceTypeMismatchException("");\r
- }\r
-\r
- /**\r
- * Returns a signed integer from a resource.\r
- *\r
- * @return an integer value\r
- * @see #getIntVector\r
- * @see #getBinary()\r
- * @throws MissingResourceException\r
- * @throws UResourceTypeMismatchException\r
- * @stable ICU 3.8\r
- */\r
- public int getInt() {\r
- throw new UResourceTypeMismatchException("");\r
- }\r
-\r
- /**\r
- * Returns a unsigned integer from a resource.\r
- * This integer is originally 28 bit and the sign gets propagated.\r
- *\r
- * @return an integer value\r
- * @see #getIntVector\r
- * @see #getBinary()\r
- * @throws MissingResourceException\r
- * @throws UResourceTypeMismatchException\r
- * @stable ICU 3.8\r
- */\r
- public int getUInt() {\r
- throw new UResourceTypeMismatchException("");\r
- }\r
-\r
- /**\r
- * Returns a resource in a given resource that has a given key.\r
- *\r
- * @param aKey a key associated with the wanted resource\r
- * @return a resource bundle object representing the resource\r
- * @throws MissingResourceException\r
- * @stable ICU 3.8\r
- */\r
- public UResourceBundle get(String aKey) {\r
- UResourceBundle obj = handleGet(aKey, null, this);\r
- if (obj == null) {\r
- UResourceBundle res = this;\r
- while ((res = res.getParent()) != null && obj == null) {\r
- //call the get method to recursively fetch the resource\r
- obj = res.handleGet(aKey, null, this);\r
- }\r
- if (obj == null) {\r
- String fullName = ICUResourceBundleReader.getFullName(\r
- getBaseName(), getLocaleID());\r
- throw new MissingResourceException(\r
- "Can't find resource for bundle " + fullName + ", key "\r
- + aKey, this.getClass().getName(), aKey);\r
- }\r
- }\r
- ((ICUResourceBundle)obj).setLoadingStatus(getLocaleID());\r
- return obj;\r
- }\r
-\r
- /**\r
- * Returns the string in a given resource at the specified index.\r
- *\r
- * @param index an index to the wanted string.\r
- * @return a string which lives in the resource.\r
- * @throws IndexOutOfBoundsException\r
- * @throws UResourceTypeMismatchException\r
- * @stable ICU 3.8\r
- */\r
- public String getString(int index) {\r
- ICUResourceBundle temp = (ICUResourceBundle)get(index);\r
- if (temp.getType() == STRING) {\r
- return temp.getString();\r
- }\r
- throw new UResourceTypeMismatchException("");\r
- }\r
-\r
- /**\r
- * Returns the resource in a given resource at the specified index.\r
- *\r
- * @param index an index to the wanted resource.\r
- * @return the sub resource UResourceBundle object\r
- * @throws IndexOutOfBoundsException\r
- * @throws MissingResourceException\r
- * @stable ICU 3.8\r
- */\r
- public UResourceBundle get(int index) {\r
- UResourceBundle obj = handleGet(index, null, this);\r
- if (obj == null) {\r
- obj = (ICUResourceBundle) getParent();\r
- if (obj != null) {\r
- obj = obj.get(index);\r
- }\r
- if (obj == null)\r
- throw new MissingResourceException(\r
- "Can't find resource for bundle "\r
- + this.getClass().getName() + ", key "\r
- + getKey(), this.getClass().getName(), getKey());\r
- }\r
- ((ICUResourceBundle)obj).setLoadingStatus(getLocaleID());\r
- return obj;\r
- }\r
- /**\r
- * Returns the keys in this bundle as an enumeration\r
- * @return an enumeration containing key strings\r
- * @stable ICU 3.8\r
- */\r
- public Enumeration getKeys() {\r
- initKeysVector();\r
- return keys.elements();\r
- }\r
-\r
- private Vector keys = null;\r
- private synchronized void initKeysVector(){\r
- if(keys!=null){\r
- return;\r
- }\r
- //ICUResourceBundle current = this;\r
- keys = new Vector();\r
- Enumeration e = this.handleGetKeys();\r
- while(e.hasMoreElements()){\r
- String elem = (String)e.nextElement();\r
- if(!keys.contains(elem)){\r
- keys.add(elem);\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Returns the size of a resource. Size for scalar types is always 1,\r
- * and for vector/table types is the number of child resources.\r
- * <br><b><font color='red'>Warning: </font></b> Integer array is treated as a scalar type. There are no\r
- * APIs to access individual members of an integer array. It\r
- * is always returned as a whole.\r
- * @return number of resources in a given resource.\r
- * @stable ICU 3.8\r
- */\r
- public int getSize() {\r
- return size;\r
- }\r
-\r
- /**\r
- * Returns the type of a resource.\r
- * Available types are {@link #INT INT}, {@link #ARRAY ARRAY},\r
- * {@link #BINARY BINARY}, {@link #INT_VECTOR INT_VECTOR},\r
- * {@link #STRING STRING}, {@link #TABLE TABLE}.\r
- *\r
- * @return type of the given resource.\r
- * @stable ICU 3.8\r
- */\r
- public int getType() {\r
- int type = ICUResourceBundle.RES_GET_TYPE(resource);\r
- if(type==TABLE32){\r
- return TABLE; //Mask the table32's real type\r
- }\r
- return type;\r
- }\r
-\r
- /**\r
- * Return the version number associated with this UResourceBundle as an\r
- * VersionInfo object.\r
- * @return VersionInfo object containing the version of the bundle\r
- * @stable ICU 3.8\r
- */\r
- public VersionInfo getVersion() {\r
- return null;\r
- }\r
-\r
- /**\r
- * Returns the iterator which iterates over this\r
- * resource bundle\r
- * @return UResourceBundleIterator that iterates over the resources in the bundle\r
- * @stable ICU 3.8\r
- */\r
- public UResourceBundleIterator getIterator() {\r
- return new UResourceBundleIterator(this);\r
- }\r
- /**\r
- * Returns the key associated with a given resource. Not all the resources have a key - only\r
- * those that are members of a table.\r
- * @return a key associated to this resource, or null if it doesn't have a key\r
- * @stable ICU 3.8\r
- */\r
- public String getKey() {\r
- return key;\r
- }\r
- /**\r
- * Resource type constant for "no resource".\r
- * @stable ICU 3.8\r
- */\r
- public static final int NONE = -1;\r
-\r
- /**\r
- * Resource type constant for strings.\r
- * @stable ICU 3.8\r
- */\r
- public static final int STRING = 0;\r
-\r
- /**\r
- * Resource type constant for binary data.\r
- * @stable ICU 3.8\r
- */\r
- public static final int BINARY = 1;\r
-\r
- /**\r
- * Resource type constant for tables of key-value pairs.\r
- * @stable ICU 3.8\r
- */\r
- public static final int TABLE = 2;\r
-\r
- /**\r
- * Resource type constant for aliases;\r
- * internally stores a string which identifies the actual resource\r
- * storing the data (can be in a different resource bundle).\r
- * Resolved internally before delivering the actual resource through the API.\r
- * @internal ICU 3.8\r
- * @deprecated This API is ICU internal only.\r
- */\r
- protected static final int ALIAS = 3;\r
-\r
- /**\r
- * Internal use only.\r
- * Alternative resource type constant for tables of key-value pairs.\r
- * Never returned by getType().\r
- * @internal ICU 3.8\r
- * @deprecated This API is ICU internal only.\r
- */\r
- protected static final int TABLE32 = 4;\r
-\r
- /**\r
- * Resource type constant for a single 28-bit integer, interpreted as\r
- * signed or unsigned by the getInt() function.\r
- * @see #getInt\r
- * @stable ICU 3.8\r
- */\r
- public static final int INT = 7;\r
-\r
- /**\r
- * Resource type constant for arrays of resources.\r
- * @stable ICU 3.8\r
- */\r
- public static final int ARRAY = 8;\r
-\r
- /**\r
- * Resource type constant for vectors of 32-bit integers.\r
- * @see #getIntVector\r
- * @stable ICU 3.8\r
- */\r
- public static final int INT_VECTOR = 14;\r
-\r
- //====== protected members ==============\r
- /**\r
- * Data member where the subclasses store the key\r
- * @internal\r
- * @deprecated This API is ICU internal only.\r
- */\r
- protected String key;\r
- /**\r
- * Data member where the subclasses store the size of resources\r
- * @internal\r
- * @deprecated This API is ICU internal only.\r
- */\r
- protected int size = 1;\r
- /**\r
- * Data member where the subclasses store the offset within resource data\r
- * @internal\r
- * @deprecated This API is ICU internal only.\r
- */\r
- protected long resource = RES_BOGUS;\r
- /**\r
- * Data member where the subclasses store whether the resource is top level\r
- * @internal\r
- * @deprecated This API is ICU internal only.\r
- */\r
- protected boolean isTopLevel = false;\r
-\r
- private static final long RES_BOGUS = 0xffffffff;\r
-\r
- /**\r
- * Actual worker method for fetching a resource based on the given key.\r
- * Sub classes must override this method if they support resources with keys.\r
- * @param aKey the key string of the resource to be fetched\r
- * @param table hashtable object to hold references of resources already seen\r
- * @param requested the original resource bundle object on which the get method was invoked.\r
- * The requested bundle and the bundle on which this method is invoked\r
- * are the same, except in the cases where aliases are involved.\r
- * @return UResourceBundle a resource associated with the key\r
- * @stable ICU 3.8\r
- */\r
- protected UResourceBundle handleGet(String aKey, HashMap table, UResourceBundle requested) {\r
- return null;\r
- }\r
-\r
- /**\r
- * Actual worker method for fetching a resource based on the given index.\r
- * Sub classes must override this method if they support arrays of resources.\r
- * @param index the index of the resource to be fetched\r
- * @param table hashtable object to hold references of resources already seen\r
- * @param requested the original resource bundle object on which the get method was invoked.\r
- * The requested bundle and the bundle on which this method is invoked\r
- * are the same, except in the cases where aliases are involved.\r
- * @return UResourceBundle a resource associated with the index\r
- * @stable ICU 3.8\r
- */\r
- protected UResourceBundle handleGet(int index, HashMap table, UResourceBundle requested) {\r
- return null;\r
- }\r
-\r
- /**\r
- * Actual worker method for fetching the array of strings in a resource.\r
- * Sub classes must override this method if they support arrays of strings.\r
- * @return String[] An array of strings containing strings\r
- * @stable ICU 3.8\r
- */\r
- protected String[] handleGetStringArray() {\r
- return null;\r
- }\r
-\r
- /**\r
- * Actual worker method for fetching the keys of resources contained in the resource.\r
- * Sub classes must override this method if they support keys and associated resources.\r
- *\r
- * @return Enumeration An enumeration of all the keys in this resource.\r
- * @stable ICU 3.8\r
- */\r
- protected Enumeration handleGetKeys(){\r
- Vector resKeys = new Vector();\r
- UResourceBundle item = null;\r
- for (int i = 0; i < size; i++) {\r
- item = get(i);\r
- resKeys.add(item.getKey());\r
- }\r
- return resKeys.elements();\r
- }\r
-\r
- /**\r
- * {@inheritDoc}\r
- * @stable ICU 3.8\r
- */\r
- // this method is declared in ResourceBundle class\r
- // so cannot change the signature\r
- // Override this method\r
- protected Object handleGetObject(String aKey) {\r
- return handleGetObjectImpl(aKey, this);\r
- }\r
-\r
- /**\r
- * Override the superclass method\r
- */\r
- // To facilitate XPath style aliases we need a way to pass the reference\r
- // to requested locale. The only way I could figure out is to implement\r
- // the look up logic here. This has a disadvantage that if the client\r
- // loads an ICUResourceBundle, calls ResourceBundle.getObject method\r
- // with a key that does not exist in the bundle then the lookup is\r
- // done twice before throwing a MissingResourceExpection.\r
- private Object handleGetObjectImpl(String aKey, UResourceBundle requested) {\r
- Object obj = resolveObject(aKey, requested);\r
- if (obj == null) {\r
- UResourceBundle parentBundle = getParent();\r
- if (parentBundle != null) {\r
- obj = parentBundle.handleGetObjectImpl(aKey, requested);\r
- }\r
- if (obj == null)\r
- throw new MissingResourceException(\r
- "Can't find resource for bundle "\r
- + this.getClass().getName() + ", key " + aKey,\r
- this.getClass().getName(), aKey);\r
- }\r
- return obj;\r
- }\r
-\r
- // Routine for figuring out the type of object to be returned\r
- // string or string array\r
- private Object resolveObject(String aKey, UResourceBundle requested) {\r
- if (getType() == STRING) {\r
- return getString();\r
- }\r
- UResourceBundle obj = handleGet(aKey, null, requested);\r
- if (obj != null) {\r
- if (obj.getType() == STRING) {\r
- return obj.getString();\r
- }\r
- try {\r
- if (obj.getType() == ARRAY) {\r
- return obj.handleGetStringArray();\r
- }\r
- } catch (UResourceTypeMismatchException ex) {\r
- return obj;\r
- }\r
- }\r
- return obj;\r
- }\r
-\r
- /**\r
- * This method is for setting the loading status of the resource.\r
- * The status is analogous to the warning status in ICU4C.\r
- * @internal ICU 3.8\r
- * @deprecated This API is ICU internal only.\r
- */\r
- protected abstract void setLoadingStatus(int newStatus);\r
-}\r
+//##header J2SE15
+/*
+ *******************************************************************************
+ * Copyright (C) 2004-2009, International Business Machines Corporation and *
+ * others. All Rights Reserved. *
+ *******************************************************************************
+ */
+
+package com.ibm.icu.util;
+
+import java.lang.ref.SoftReference;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.Vector;
+
+import com.ibm.icu.impl.ICUCache;
+import com.ibm.icu.impl.ICUResourceBundle;
+import com.ibm.icu.impl.ICUResourceBundleReader;
+import com.ibm.icu.impl.ResourceBundleWrapper;
+import com.ibm.icu.impl.SimpleCache;
+import com.ibm.icu.util.ULocale;
+
+//#if defined(FOUNDATION10) || defined(J2SE13) || defined(ECLIPSE_FRAGMENT)
+//##import com.ibm.icu.impl.ByteBuffer;
+//#else
+import java.nio.ByteBuffer;
+//#endif
+
+/**
+ * A class representing a collection of resource information pertaining to a given
+ * locale. A resource bundle provides a way of accessing locale- specific information in
+ * a data file. You create a resource bundle that manages the resources for a given
+ * locale and then ask it for individual resources.
+ * <P>
+ * In ResourceBundle class, an object is created
+ * and the sub items are fetched using getString, getObject methods.
+ * In UResourceBundle,each individual element of a resource is a resource by itself.
+ *
+ * <P>
+ * Resource bundles in ICU are currently defined using text files which conform to the following
+ * <a href="http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt">BNF definition</a>.
+ * More on resource bundle concepts and syntax can be found in the
+ * <a href="http://www.icu-project.org/userguide/ResourceManagement.html">Users Guide</a>.
+ * <P>
+ *
+ * The packaging of ICU *.res files can be of two types
+ * ICU4C:
+ * <pre>
+ * root.res
+ * |
+ * --------
+ * | |
+ * fr.res en.res
+ * |
+ * --------
+ * | |
+ * fr_CA.res fr_FR.res
+ * </pre>
+ * JAVA/JDK:
+ * <pre>
+ * LocaleElements.res
+ * |
+ * -------------------
+ * | |
+ * LocaleElements_fr.res LocaleElements_en.res
+ * |
+ * ---------------------------
+ * | |
+ * LocaleElements_fr_CA.res LocaleElements_fr_FR.res
+ * </pre>
+ * Depending on the organization of your resources, the syntax to getBundleInstance will change.
+ * To open ICU style organization use:
+ * <pre>
+ * UResourceBundle bundle = UResourceBundle.getBundleInstance("com/mycompany/resources", "en_US", myClassLoader);
+ * </pre>
+ * To open Java/JDK style organization use:
+ * <pre>
+ * UResourceBundle bundle = UResourceBundle.getBundleInstance("com.mycompany.resources.LocaleElements", "en_US", myClassLoader);
+ * </pre>
+ * <note>
+ * Please use pass a class loader for loading non-ICU resources. Java security does not
+ * allow loading of resources across jar files. You must provide your class loader
+ * to load the resources
+ * </note>
+ * @stable ICU 3.0
+ * @author ram
+ */
+public abstract class UResourceBundle extends ResourceBundle{
+
+
+ /**
+ * Creates a resource bundle using the specified base name and locale.
+ * ICU_DATA_CLASS is used as the default root.
+ * @param baseName the base name of the resource bundle, a fully qualified class name
+ * @param localeName the locale for which a resource bundle is desired
+ * @exception MissingResourceException
+ * if no resource bundle for the specified base name can be found
+ * @return a resource bundle for the given base name and locale
+ * @stable ICU 3.0
+ */
+ public static UResourceBundle getBundleInstance(String baseName, String localeName){
+ return getBundleInstance(baseName, localeName, ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);
+ }
+
+ /**
+ * Creates a resource bundle using the specified base name, locale, and class root.
+ *
+ * @param baseName the base name of the resource bundle, a fully qualified class name
+ * @param localeName the locale for which a resource bundle is desired
+ * @param root the class object from which to load the resource bundle
+ * @exception MissingResourceException
+ * if no resource bundle for the specified base name can be found
+ * @return a resource bundle for the given base name and locale
+ * @stable ICU 3.0
+ */
+ public static UResourceBundle getBundleInstance(String baseName, String localeName, ClassLoader root){
+ return getBundleInstance(baseName, localeName, root, false);
+ }
+
+ /**
+ * Creates a resource bundle using the specified base name, locale, and class root.
+ *
+ * @param baseName the base name of the resource bundle, a fully qualified class name
+ * @param localeName the locale for which a resource bundle is desired
+ * @param root the class object from which to load the resource bundle
+ * @param disableFallback Option to disable locale inheritence.
+ * If true the fallback chain will not be built.
+ * @exception MissingResourceException
+ * if no resource bundle for the specified base name can be found
+ * @return a resource bundle for the given base name and locale
+ * @stable ICU 3.0
+ *
+ */
+ protected static UResourceBundle getBundleInstance(String baseName, String localeName, ClassLoader root, boolean disableFallback) {
+ return instantiateBundle(baseName, localeName, root, disableFallback);
+ }
+
+ /**
+ * Sole constructor. (For invocation by subclass constructors, typically
+ * implicit.) This is public for compatibility with Java, whose compiler
+ * will generate public default constructors for an abstract class.
+ * @stable ICU 3.0
+ */
+ public UResourceBundle() {
+ }
+
+ /**
+ * Creates a UResourceBundle for the locale specified, from which users can extract resources by using
+ * their corresponding keys.
+ * @param locale specifies the locale for which we want to open the resource.
+ * If null the bundle for default locale is opened.
+ * @return a resource bundle for the given locale
+ * @stable ICU 3.0
+ */
+ public static UResourceBundle getBundleInstance(ULocale locale) {
+ if (locale==null) {
+ locale = ULocale.getDefault();
+ }
+ return getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale.toString(), ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);
+ }
+
+ /**
+ * Creates a UResourceBundle for the default locale and specified base name,
+ * from which users can extract resources by using their corresponding keys.
+ * @param baseName specifies the locale for which we want to open the resource.
+ * If null the bundle for default locale is opened.
+ * @return a resource bundle for the given base name and default locale
+ * @stable ICU 3.0
+ */
+ public static UResourceBundle getBundleInstance(String baseName) {
+ if (baseName == null) {
+ baseName = ICUResourceBundle.ICU_BASE_NAME;
+ }
+ ULocale uloc = ULocale.getDefault();
+ return getBundleInstance(baseName, uloc.toString(), ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);
+ }
+
+ /**
+ * Creates a UResourceBundle for the specified locale and specified base name,
+ * from which users can extract resources by using their corresponding keys.
+ * @param baseName specifies the locale for which we want to open the resource.
+ * If null the bundle for default locale is opened.
+ * @param locale specifies the locale for which we want to open the resource.
+ * If null the bundle for default locale is opened.
+ * @return a resource bundle for the given base name and locale
+ * @stable ICU 3.0
+ */
+
+ public static UResourceBundle getBundleInstance(String baseName, Locale locale) {
+ if (baseName == null) {
+ baseName = ICUResourceBundle.ICU_BASE_NAME;
+ }
+ ULocale uloc = locale == null ? ULocale.getDefault() : ULocale.forLocale(locale);
+
+ return getBundleInstance(baseName, uloc.toString(), ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);
+ }
+
+ /**
+ * Creates a UResourceBundle, from which users can extract resources by using
+ * their corresponding keys.
+ * @param baseName string containing the name of the data package.
+ * If null the default ICU package name is used.
+ * @param locale specifies the locale for which we want to open the resource.
+ * If null the bundle for default locale is opened.
+ * @return a resource bundle for the given base name and locale
+ * @stable ICU 3.0
+ */
+ public static UResourceBundle getBundleInstance(String baseName, ULocale locale) {
+ if (baseName == null) {
+ baseName = ICUResourceBundle.ICU_BASE_NAME;
+ }
+ if (locale == null) {
+ locale = ULocale.getDefault();
+ }
+ return getBundleInstance(baseName, locale.toString(), ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);
+ }
+
+ /**
+ * Creates a UResourceBundle for the specified locale and specified base name,
+ * from which users can extract resources by using their corresponding keys.
+ * @param baseName specifies the locale for which we want to open the resource.
+ * If null the bundle for default locale is opened.
+ * @param locale specifies the locale for which we want to open the resource.
+ * If null the bundle for default locale is opened.
+ * @param loader the loader to use
+ * @return a resource bundle for the given base name and locale
+ * @stable ICU 3.8
+ */
+ public static UResourceBundle getBundleInstance(String baseName, Locale locale, ClassLoader loader) {
+ if (baseName == null) {
+ baseName = ICUResourceBundle.ICU_BASE_NAME;
+ }
+ ULocale uloc = locale == null ? ULocale.getDefault() : ULocale.forLocale(locale);
+ return getBundleInstance(baseName, uloc.toString(), loader, false);
+ }
+
+ /**
+ * Creates a UResourceBundle, from which users can extract resources by using
+ * their corresponding keys.<br><br>
+ * Note: Please use this API for loading non-ICU resources. Java security does not
+ * allow loading of resources across jar files. You must provide your class loader
+ * to load the resources
+ * @param baseName string containing the name of the data package.
+ * If null the default ICU package name is used.
+ * @param locale specifies the locale for which we want to open the resource.
+ * If null the bundle for default locale is opened.
+ * @param loader the loader to use
+ * @return a resource bundle for the given base name and locale
+ * @stable ICU 3.8
+ */
+ public static UResourceBundle getBundleInstance(String baseName, ULocale locale, ClassLoader loader) {
+ if (baseName == null) {
+ baseName = ICUResourceBundle.ICU_BASE_NAME;
+ }
+ if (locale == null) {
+ locale = ULocale.getDefault();
+ }
+ return getBundleInstance(baseName, locale.toString(), loader, false);
+ }
+
+ /**
+ * Returns the RFC 3066 conformant locale id of this resource bundle.
+ * This method can be used after a call to getBundleInstance() to
+ * determine whether the resource bundle returned really
+ * corresponds to the requested locale or is a fallback.
+ *
+ * @return the locale of this resource bundle
+ * @stable ICU 3.0
+ */
+ public abstract ULocale getULocale();
+
+ /**
+ * Gets the localeID
+ * @return The string representation of the localeID
+ * @stable ICU 3.0
+ */
+ protected abstract String getLocaleID();
+ /**
+ * Gets the base name of the resource bundle
+ * @return The string representation of the base name
+ * @stable ICU 3.0
+ */
+ protected abstract String getBaseName();
+
+ /**
+ * Gets the parent bundle
+ * @return The parent bundle
+ * @stable ICU 3.0
+ */
+ protected abstract UResourceBundle getParent();
+
+
+ /**
+ * Get the locale of this bundle
+ * @return the locale of this resource bundle
+ * @stable ICU 3.0
+ */
+ public Locale getLocale(){
+ return getULocale().toLocale();
+ }
+
+ // Cache for ResourceBundle instantiation
+ private static ICUCache BUNDLE_CACHE = new SimpleCache();
+
+ /**
+ * @internal
+ */
+ public static void resetBundleCache()
+ {
+ /*
+ * A HACK!!!!!
+ * Currently if a resourcebundle with fallback turned ON is added to the cache
+ * and then a getBundleInstance() is called for a bundle with fallback turned OFF
+ * it will actually search the cache for any bundle of the same locale
+ * regaurdless of fallback status. This method has been created so that if
+ * The calling method KNOWS that instances of the other fallback state may be in the
+ * cache, the calling method may call this method to clear out the cache.
+ *
+ */
+ //TODO figure a way around this method(see method comment)
+ BUNDLE_CACHE = new SimpleCache();
+ }
+
+ private static void addToCache(ResourceCacheKey key, UResourceBundle b) {
+ BUNDLE_CACHE.put(key, b);
+ }
+
+ /**
+ * Method used by subclasses to add the a particular resource bundle object to the managed cache
+ * @internal revisit for ICU 3.6
+ * @deprecated This API is ICU internal only.
+ */
+ protected static void addToCache(ClassLoader cl, String fullName, ULocale defaultLocale, UResourceBundle b){
+ synchronized(cacheKey){
+ cacheKey.setKeyValues(cl, fullName, defaultLocale);
+ addToCache((ResourceCacheKey)cacheKey.clone(), b);
+ }
+ }
+ /**
+ * Method used by sub classes to load a resource bundle object from the managed cache
+ * @internal revisit for ICU 3.6
+ * @deprecated This API is ICU internal only.
+ */
+ protected static UResourceBundle loadFromCache(ClassLoader cl, String fullName, ULocale defaultLocale){
+ synchronized(cacheKey){
+ cacheKey.setKeyValues(cl, fullName, defaultLocale);
+ return loadFromCache(cacheKey);
+ }
+ }
+ private static UResourceBundle loadFromCache(ResourceCacheKey key) {
+ return (UResourceBundle)BUNDLE_CACHE.get(key);
+ }
+
+ /**
+ * Key used for cached resource bundles. The key checks
+ * the resource name, the class root, and the default
+ * locale to determine if the resource is a match to the
+ * requested one. The root may be null, but the
+ * searchName and the default locale must have a non-null value.
+ * Note that the default locale may change over time, and
+ * lookup should always be based on the current default
+ * locale (if at all).
+ */
+ private static final class ResourceCacheKey implements Cloneable {
+ private SoftReference loaderRef;
+ private String searchName;
+ private ULocale defaultLocale;
+ private int hashCodeCache;
+ ///CLOVER:OFF
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ try {
+ final ResourceCacheKey otherEntry = (ResourceCacheKey) other;
+ //quick check to see if they are not equal
+ if (hashCodeCache != otherEntry.hashCodeCache) {
+ return false;
+ }
+ //are the names the same?
+ if (!searchName.equals(otherEntry.searchName)) {
+ return false;
+ }
+ // are the default locales the same?
+ if (defaultLocale == null) {
+ if (otherEntry.defaultLocale != null) {
+ return false;
+ }
+ } else {
+ if (!defaultLocale.equals(otherEntry.defaultLocale)) {
+ return false;
+ }
+ }
+ //are refs (both non-null) or (both null)?
+ if (loaderRef == null) {
+ return otherEntry.loaderRef == null;
+ } else {
+ return (otherEntry.loaderRef != null)
+ && (loaderRef.get() == otherEntry.loaderRef.get());
+ }
+ } catch (NullPointerException e) {
+ return false;
+ } catch (ClassCastException e) {
+ return false;
+ }
+ }
+ public int hashCode() {
+ return hashCodeCache;
+ }
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ //this should never happen
+ throw new IllegalStateException();
+ }
+ }
+ ///CLOVER:ON
+ private synchronized void setKeyValues(ClassLoader root, String searchName, ULocale defaultLocale) {
+ this.searchName = searchName;
+ hashCodeCache = searchName.hashCode();
+ this.defaultLocale = defaultLocale;
+ if (defaultLocale != null) {
+ hashCodeCache ^= defaultLocale.hashCode();
+ }
+ if (root == null) {
+ this.loaderRef = null;
+ } else {
+ loaderRef = new SoftReference(root);
+ hashCodeCache ^= root.hashCode();
+ }
+ }
+ /*private void clear() {
+ setKeyValues(null, "", null);
+ }*/
+ }
+
+ private static final ResourceCacheKey cacheKey = new ResourceCacheKey();
+
+ private static final int ROOT_MISSING = 0;
+ private static final int ROOT_ICU = 1;
+ private static final int ROOT_JAVA = 2;
+
+ private static SoftReference ROOT_CACHE;
+
+ private static int getRootType(String baseName, ClassLoader root)
+ {
+ Map m = null;
+ Integer rootType;
+
+ if (ROOT_CACHE != null) {
+ m = (Map) ROOT_CACHE.get();
+ }
+
+ if (m == null) {
+ m = new HashMap();
+ ROOT_CACHE = new SoftReference(m);
+ }
+
+ rootType = (Integer) m.get(baseName);
+
+ if (rootType == null) {
+ String rootLocale = (baseName.indexOf('.')==-1) ? "root" : "";
+ int rt = ROOT_MISSING; // value set on success
+ try{
+ ICUResourceBundle.getBundleInstance(baseName, rootLocale, root, true);
+ rt = ROOT_ICU;
+ }catch(MissingResourceException ex){
+ try{
+ ResourceBundleWrapper.getBundleInstance(baseName, rootLocale, root, true);
+ rt = ROOT_JAVA;
+ }catch(MissingResourceException e){
+ //throw away the exception
+ }
+ }
+
+ rootType = new Integer(rt);
+ m.put(baseName, rootType);
+ }
+
+ return rootType.intValue();
+ }
+
+ private static void setRootType(String baseName, int rootType)
+ {
+ Integer rt = new Integer(rootType);
+ Map m = null;
+
+ if (ROOT_CACHE != null) {
+ m = (Map) ROOT_CACHE.get();
+ } else {
+ m = new HashMap();
+ ROOT_CACHE = new SoftReference(m);
+ }
+
+ m.put(baseName, rt);
+ }
+
+ /**
+ * Loads a new resource bundle for the give base name, locale and class loader.
+ * Optionally will disable loading of fallback bundles.
+ * @param baseName the base name of the resource bundle, a fully qualified class name
+ * @param localeName the locale for which a resource bundle is desired
+ * @param root the class object from which to load the resource bundle
+ * @param disableFallback disables loading of fallback lookup chain
+ * @exception MissingResourceException
+ * if no resource bundle for the specified base name can be found
+ * @return a resource bundle for the given base name and locale
+ * @stable ICU 3.0
+ */
+ protected static UResourceBundle instantiateBundle(String baseName, String localeName,
+ ClassLoader root, boolean disableFallback){
+ UResourceBundle b = null;
+ int rootType = getRootType(baseName, root);
+
+ ULocale defaultLocale = ULocale.getDefault();
+
+ switch (rootType)
+ {
+ case ROOT_ICU:
+ if(disableFallback) {
+ String fullName = ICUResourceBundleReader.getFullName(baseName, localeName);
+ synchronized(cacheKey){
+ cacheKey.setKeyValues(root, fullName, defaultLocale);
+ b = loadFromCache(cacheKey);
+ }
+
+ if (b == null) {
+ b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, disableFallback);
+ //cacheKey.setKeyValues(root, fullName, defaultLocale);
+ addToCache(cacheKey, b);
+ }
+ } else {
+ b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, disableFallback);
+ }
+
+ return b;
+
+ case ROOT_JAVA:
+ return ResourceBundleWrapper.getBundleInstance(baseName, localeName, root, disableFallback);
+
+ default:
+ try{
+ b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, disableFallback);
+ setRootType(baseName, ROOT_ICU);
+ }catch(MissingResourceException ex){
+ b = ResourceBundleWrapper.getBundleInstance(baseName, localeName, root, disableFallback);
+ setRootType(baseName, ROOT_JAVA);
+ }
+ return b;
+ }
+ }
+
+
+ /**
+ * Returns a binary data from a binary resource.
+ *
+ * @return a pointer to a chuck of unsigned bytes which live in a memory mapped/DLL file.
+ * @see #getIntVector
+ * @see #getInt
+ * @throws MissingResourceException
+ * @throws UResourceTypeMismatchException
+ * @stable ICU 3.8
+ */
+ public ByteBuffer getBinary() {
+ throw new UResourceTypeMismatchException("");
+ }
+
+ /**
+ * Returns a string from a string resource type
+ *
+ * @return a string
+ * @see #getBinary()
+ * @see #getIntVector
+ * @see #getInt
+ * @throws MissingResourceException
+ * @throws UResourceTypeMismatchException
+ * @stable ICU 3.8
+ */
+ public String getString() {
+ throw new UResourceTypeMismatchException("");
+ }
+
+ /**
+ * Returns a string array from a array resource type
+ *
+ * @return a string
+ * @see #getString()
+ * @see #getIntVector
+ * @throws MissingResourceException
+ * @throws UResourceTypeMismatchException
+ * @stable ICU 3.8
+ */
+ public String[] getStringArray() {
+ throw new UResourceTypeMismatchException("");
+ }
+
+ /**
+ * Returns a binary data from a binary resource.
+ *
+ * @param ba The byte array to write the bytes to. A null variable is OK.
+ * @return an array bytes containing the binary data from the resource.
+ * @see #getIntVector
+ * @see #getInt
+ * @throws MissingResourceException
+ * @throws UResourceTypeMismatchException
+ * @stable ICU 3.8
+ */
+ public byte[] getBinary(byte[] ba) {
+ throw new UResourceTypeMismatchException("");
+ }
+
+ /**
+ * Returns a 32 bit integer array from a resource.
+ *
+ * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL file.
+ * @see #getBinary()
+ * @see #getInt
+ * @throws MissingResourceException
+ * @throws UResourceTypeMismatchException
+ * @stable ICU 3.8
+ */
+ public int[] getIntVector() {
+ throw new UResourceTypeMismatchException("");
+ }
+
+ /**
+ * Returns a signed integer from a resource.
+ *
+ * @return an integer value
+ * @see #getIntVector
+ * @see #getBinary()
+ * @throws MissingResourceException
+ * @throws UResourceTypeMismatchException
+ * @stable ICU 3.8
+ */
+ public int getInt() {
+ throw new UResourceTypeMismatchException("");
+ }
+
+ /**
+ * Returns a unsigned integer from a resource.
+ * This integer is originally 28 bit and the sign gets propagated.
+ *
+ * @return an integer value
+ * @see #getIntVector
+ * @see #getBinary()
+ * @throws MissingResourceException
+ * @throws UResourceTypeMismatchException
+ * @stable ICU 3.8
+ */
+ public int getUInt() {
+ throw new UResourceTypeMismatchException("");
+ }
+
+ /**
+ * Returns a resource in a given resource that has a given key.
+ *
+ * @param aKey a key associated with the wanted resource
+ * @return a resource bundle object representing the resource
+ * @throws MissingResourceException
+ * @stable ICU 3.8
+ */
+ public UResourceBundle get(String aKey) {
+ UResourceBundle obj = handleGet(aKey, null, this);
+ if (obj == null) {
+ UResourceBundle res = this;
+ while ((res = res.getParent()) != null && obj == null) {
+ //call the get method to recursively fetch the resource
+ obj = res.handleGet(aKey, null, this);
+ }
+ if (obj == null) {
+ String fullName = ICUResourceBundleReader.getFullName(
+ getBaseName(), getLocaleID());
+ throw new MissingResourceException(
+ "Can't find resource for bundle " + fullName + ", key "
+ + aKey, this.getClass().getName(), aKey);
+ }
+ }
+ ((ICUResourceBundle)obj).setLoadingStatus(getLocaleID());
+ return obj;
+ }
+
+ /**
+ * Returns the string in a given resource at the specified index.
+ *
+ * @param index an index to the wanted string.
+ * @return a string which lives in the resource.
+ * @throws IndexOutOfBoundsException
+ * @throws UResourceTypeMismatchException
+ * @stable ICU 3.8
+ */
+ public String getString(int index) {
+ ICUResourceBundle temp = (ICUResourceBundle)get(index);
+ if (temp.getType() == STRING) {
+ return temp.getString();
+ }
+ throw new UResourceTypeMismatchException("");
+ }
+
+ /**
+ * Returns the resource in a given resource at the specified index.
+ *
+ * @param index an index to the wanted resource.
+ * @return the sub resource UResourceBundle object
+ * @throws IndexOutOfBoundsException
+ * @throws MissingResourceException
+ * @stable ICU 3.8
+ */
+ public UResourceBundle get(int index) {
+ UResourceBundle obj = handleGet(index, null, this);
+ if (obj == null) {
+ obj = (ICUResourceBundle) getParent();
+ if (obj != null) {
+ obj = obj.get(index);
+ }
+ if (obj == null)
+ throw new MissingResourceException(
+ "Can't find resource for bundle "
+ + this.getClass().getName() + ", key "
+ + getKey(), this.getClass().getName(), getKey());
+ }
+ ((ICUResourceBundle)obj).setLoadingStatus(getLocaleID());
+ return obj;
+ }
+ /**
+ * Returns the keys in this bundle as an enumeration
+ * @return an enumeration containing key strings
+ * @stable ICU 3.8
+ */
+ public Enumeration getKeys() {
+ initKeysVector();
+ return keys.elements();
+ }
+
+ private Vector keys = null;
+ private synchronized void initKeysVector(){
+ if(keys!=null){
+ return;
+ }
+ //ICUResourceBundle current = this;
+ keys = new Vector();
+ Enumeration e = this.handleGetKeys();
+ while(e.hasMoreElements()){
+ String elem = (String)e.nextElement();
+ if(!keys.contains(elem)){
+ keys.add(elem);
+ }
+ }
+ }
+
+ /**
+ * Returns the size of a resource. Size for scalar types is always 1,
+ * and for vector/table types is the number of child resources.
+ * <br><b><font color='red'>Warning: </font></b> Integer array is treated as a scalar type. There are no
+ * APIs to access individual members of an integer array. It
+ * is always returned as a whole.
+ * @return number of resources in a given resource.
+ * @stable ICU 3.8
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * Returns the type of a resource.
+ * Available types are {@link #INT INT}, {@link #ARRAY ARRAY},
+ * {@link #BINARY BINARY}, {@link #INT_VECTOR INT_VECTOR},
+ * {@link #STRING STRING}, {@link #TABLE TABLE}.
+ *
+ * @return type of the given resource.
+ * @stable ICU 3.8
+ */
+ public int getType() {
+ int type = ICUResourceBundle.RES_GET_TYPE(resource);
+ if(type==TABLE32){
+ return TABLE; //Mask the table32's real type
+ }
+ return type;
+ }
+
+ /**
+ * Return the version number associated with this UResourceBundle as an
+ * VersionInfo object.
+ * @return VersionInfo object containing the version of the bundle
+ * @stable ICU 3.8
+ */
+ public VersionInfo getVersion() {
+ return null;
+ }
+
+ /**
+ * Returns the iterator which iterates over this
+ * resource bundle
+ * @return UResourceBundleIterator that iterates over the resources in the bundle
+ * @stable ICU 3.8
+ */
+ public UResourceBundleIterator getIterator() {
+ return new UResourceBundleIterator(this);
+ }
+ /**
+ * Returns the key associated with a given resource. Not all the resources have a key - only
+ * those that are members of a table.
+ * @return a key associated to this resource, or null if it doesn't have a key
+ * @stable ICU 3.8
+ */
+ public String getKey() {
+ return key;
+ }
+ /**
+ * Resource type constant for "no resource".
+ * @stable ICU 3.8
+ */
+ public static final int NONE = -1;
+
+ /**
+ * Resource type constant for strings.
+ * @stable ICU 3.8
+ */
+ public static final int STRING = 0;
+
+ /**
+ * Resource type constant for binary data.
+ * @stable ICU 3.8
+ */
+ public static final int BINARY = 1;
+
+ /**
+ * Resource type constant for tables of key-value pairs.
+ * @stable ICU 3.8
+ */
+ public static final int TABLE = 2;
+
+ /**
+ * Resource type constant for aliases;
+ * internally stores a string which identifies the actual resource
+ * storing the data (can be in a different resource bundle).
+ * Resolved internally before delivering the actual resource through the API.
+ * @internal ICU 3.8
+ * @deprecated This API is ICU internal only.
+ */
+ protected static final int ALIAS = 3;
+
+ /**
+ * Internal use only.
+ * Alternative resource type constant for tables of key-value pairs.
+ * Never returned by getType().
+ * @internal ICU 3.8
+ * @deprecated This API is ICU internal only.
+ */
+ protected static final int TABLE32 = 4;
+
+ /**
+ * Resource type constant for a single 28-bit integer, interpreted as
+ * signed or unsigned by the getInt() function.
+ * @see #getInt
+ * @stable ICU 3.8
+ */
+ public static final int INT = 7;
+
+ /**
+ * Resource type constant for arrays of resources.
+ * @stable ICU 3.8
+ */
+ public static final int ARRAY = 8;
+
+ /**
+ * Resource type constant for vectors of 32-bit integers.
+ * @see #getIntVector
+ * @stable ICU 3.8
+ */
+ public static final int INT_VECTOR = 14;
+
+ //====== protected members ==============
+ /**
+ * Data member where the subclasses store the key
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ protected String key;
+ /**
+ * Data member where the subclasses store the size of resources
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ protected int size = 1;
+ /**
+ * Data member where the subclasses store the offset within resource data
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ protected long resource = RES_BOGUS;
+ /**
+ * Data member where the subclasses store whether the resource is top level
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ protected boolean isTopLevel = false;
+
+ private static final long RES_BOGUS = 0xffffffff;
+
+ /**
+ * Actual worker method for fetching a resource based on the given key.
+ * Sub classes must override this method if they support resources with keys.
+ * @param aKey the key string of the resource to be fetched
+ * @param table hashtable object to hold references of resources already seen
+ * @param requested the original resource bundle object on which the get method was invoked.
+ * The requested bundle and the bundle on which this method is invoked
+ * are the same, except in the cases where aliases are involved.
+ * @return UResourceBundle a resource associated with the key
+ * @stable ICU 3.8
+ */
+ protected UResourceBundle handleGet(String aKey, HashMap table, UResourceBundle requested) {
+ return null;
+ }
+
+ /**
+ * Actual worker method for fetching a resource based on the given index.
+ * Sub classes must override this method if they support arrays of resources.
+ * @param index the index of the resource to be fetched
+ * @param table hashtable object to hold references of resources already seen
+ * @param requested the original resource bundle object on which the get method was invoked.
+ * The requested bundle and the bundle on which this method is invoked
+ * are the same, except in the cases where aliases are involved.
+ * @return UResourceBundle a resource associated with the index
+ * @stable ICU 3.8
+ */
+ protected UResourceBundle handleGet(int index, HashMap table, UResourceBundle requested) {
+ return null;
+ }
+
+ /**
+ * Actual worker method for fetching the array of strings in a resource.
+ * Sub classes must override this method if they support arrays of strings.
+ * @return String[] An array of strings containing strings
+ * @stable ICU 3.8
+ */
+ protected String[] handleGetStringArray() {
+ return null;
+ }
+
+ /**
+ * Actual worker method for fetching the keys of resources contained in the resource.
+ * Sub classes must override this method if they support keys and associated resources.
+ *
+ * @return Enumeration An enumeration of all the keys in this resource.
+ * @stable ICU 3.8
+ */
+ protected Enumeration handleGetKeys(){
+ Vector resKeys = new Vector();
+ UResourceBundle item = null;
+ for (int i = 0; i < size; i++) {
+ item = get(i);
+ resKeys.add(item.getKey());
+ }
+ return resKeys.elements();
+ }
+
+ /**
+ * {@inheritDoc}
+ * @stable ICU 3.8
+ */
+ // this method is declared in ResourceBundle class
+ // so cannot change the signature
+ // Override this method
+ protected Object handleGetObject(String aKey) {
+ return handleGetObjectImpl(aKey, this);
+ }
+
+ /**
+ * Override the superclass method
+ */
+ // To facilitate XPath style aliases we need a way to pass the reference
+ // to requested locale. The only way I could figure out is to implement
+ // the look up logic here. This has a disadvantage that if the client
+ // loads an ICUResourceBundle, calls ResourceBundle.getObject method
+ // with a key that does not exist in the bundle then the lookup is
+ // done twice before throwing a MissingResourceExpection.
+ private Object handleGetObjectImpl(String aKey, UResourceBundle requested) {
+ Object obj = resolveObject(aKey, requested);
+ if (obj == null) {
+ UResourceBundle parentBundle = getParent();
+ if (parentBundle != null) {
+ obj = parentBundle.handleGetObjectImpl(aKey, requested);
+ }
+ if (obj == null)
+ throw new MissingResourceException(
+ "Can't find resource for bundle "
+ + this.getClass().getName() + ", key " + aKey,
+ this.getClass().getName(), aKey);
+ }
+ return obj;
+ }
+
+ // Routine for figuring out the type of object to be returned
+ // string or string array
+ private Object resolveObject(String aKey, UResourceBundle requested) {
+ if (getType() == STRING) {
+ return getString();
+ }
+ UResourceBundle obj = handleGet(aKey, null, requested);
+ if (obj != null) {
+ if (obj.getType() == STRING) {
+ return obj.getString();
+ }
+ try {
+ if (obj.getType() == ARRAY) {
+ return obj.handleGetStringArray();
+ }
+ } catch (UResourceTypeMismatchException ex) {
+ return obj;
+ }
+ }
+ return obj;
+ }
+
+ /**
+ * This method is for setting the loading status of the resource.
+ * The status is analogous to the warning status in ICU4C.
+ * @internal ICU 3.8
+ * @deprecated This API is ICU internal only.
+ */
+ protected abstract void setLoadingStatus(int newStatus);
+}