]> granicus.if.org Git - icu/commitdiff
ICU-10940 make ICUResourceBundleImpl smaller, remove lookup cache in favor of newer...
authorMarkus Scherer <markus.icu@gmail.com>
Tue, 24 Jun 2014 20:34:12 +0000 (20:34 +0000)
committerMarkus Scherer <markus.icu@gmail.com>
Tue, 24 Jun 2014 20:34:12 +0000 (20:34 +0000)
X-SVN-Rev: 35936

icu4j/main/classes/collate/src/com/ibm/icu/impl/coll/CollationLoader.java
icu4j/main/classes/core/src/com/ibm/icu/impl/ICUResourceBundle.java
icu4j/main/classes/core/src/com/ibm/icu/impl/ICUResourceBundleImpl.java
icu4j/main/classes/core/src/com/ibm/icu/impl/ICUResourceBundleReader.java
icu4j/main/classes/core/src/com/ibm/icu/util/UResourceBundle.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/ICUResourceBundleTest.java

index 9db8a8111dc7b62623a93b9020ab488f15ed1ee6..636f5e27e36b364f5a9a052a7b40390ecec35817 100644 (file)
@@ -85,12 +85,8 @@ public final class CollationLoader {
         return rules;
     }
 
-    private static final UResourceBundle getWithFallback(UResourceBundle table, String entryName) {
-        try {
-            return ((ICUResourceBundle)table).getWithFallback(entryName);
-        } catch(MissingResourceException e) {
-            return null;
-        }
+    private static final UResourceBundle findWithFallback(UResourceBundle table, String entryName) {
+        return ((ICUResourceBundle)table).findWithFallback(entryName);
     }
 
     public static CollationTailoring loadTailoring(ULocale locale, Output<ULocale> outValidLocale) {
@@ -127,7 +123,7 @@ public final class CollationLoader {
         // There are zero or more tailorings in the collations table.
         UResourceBundle collations;
         try {
-            collations = ((ICUResourceBundle)bundle).get("collations");
+            collations = bundle.get("collations");
             if (collations == null) {
                 return root;
             }
@@ -139,12 +135,9 @@ public final class CollationLoader {
         String type = locale.getKeywordValue("collation");
         String defaultType = "standard";
 
-        try {
-            String defT = ((ICUResourceBundle)collations).getStringWithFallback("default");
-            if (defT != null) {
-                defaultType = defT;
-            }
-        } catch(MissingResourceException ignored) {
+        String defT = ((ICUResourceBundle)collations).findStringWithFallback("default");
+        if (defT != null) {
+            defaultType = defT;
         }
 
         if (type == null || type.equals("default")) {
@@ -159,27 +152,27 @@ public final class CollationLoader {
         // ICU4C, but not used by ICU4J
 
         // boolean typeFallback = false;
-        UResourceBundle data = getWithFallback(collations, type);
+        UResourceBundle data = findWithFallback(collations, type);
         if (data == null &&
                 type.length() > 6 && type.startsWith("search")) {
             // fall back from something like "searchjl" to "search"
             // typeFallback = true;
             type = "search";
-            data = getWithFallback(collations, type);
+            data = findWithFallback(collations, type);
         }
 
         if (data == null && !type.equals(defaultType)) {
             // fall back to the default type
             // typeFallback = true;
             type = defaultType;
-            data = getWithFallback(collations, type);
+            data = findWithFallback(collations, type);
         }
 
         if (data == null && !type.equals("standard")) {
             // fall back to the "standard" type
             // typeFallback = true;
             type = "standard";
-            data = getWithFallback(collations, type);
+            data = findWithFallback(collations, type);
         }
 
         if (data == null) {
@@ -213,7 +206,7 @@ public final class CollationLoader {
 
         // Try to fetch the optional rules string.
         try {
-            String s = ((ICUResourceBundle)data).getString("Sequence");
+            String s = data.getString("Sequence");
             if (s != null) {
                 t.rules = s;
             }
@@ -236,12 +229,9 @@ public final class CollationLoader {
             // Opening a bundle for the actual locale should always succeed.
             UResourceBundle actualBundle = UResourceBundle.getBundleInstance(
                     ICUResourceBundle.ICU_COLLATION_BASE_NAME, actualLocale);
-            try {
-                String defT = ((ICUResourceBundle)actualBundle).getStringWithFallback("collations/default");
-                if (defT != null) {
-                    defaultType = defT;
-                }
-            } catch(MissingResourceException ignored) {
+            defT = ((ICUResourceBundle)actualBundle).findStringWithFallback("collations/default");
+            if (defT != null) {
+                defaultType = defT;
             }
         }
 
index 5ea67c3f4ec87fc7762cf24c92bf203a1a415b03..b011d321f192157bb758530fd7b05ee27f37bf2f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * *****************************************************************************
- * Copyright (C) 2005-2014, International Business Machines Corporation and    *
- * others. All Rights Reserved.                                                *
+ * Copyright (C) 2005-2014, International Business Machines Corporation and
+ * others. All Rights Reserved.
  * *****************************************************************************
  */
 
@@ -23,13 +23,12 @@ import java.util.Locale;
 import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 import java.util.Set;
-import java.util.StringTokenizer;
 
 import com.ibm.icu.impl.URLHandler.URLVisitor;
-import com.ibm.icu.util.Output;
 import com.ibm.icu.util.ULocale;
 import com.ibm.icu.util.UResourceBundle;
 import com.ibm.icu.util.UResourceBundleIterator;
+import com.ibm.icu.util.UResourceTypeMismatchException;
 import com.ibm.icu.util.VersionInfo;
 
 public  class ICUResourceBundle extends UResourceBundle {
@@ -79,11 +78,6 @@ public  class ICUResourceBundle extends UResourceBundle {
 
     private static final String NO_INHERITANCE_MARKER = "\u2205\u2205\u2205";
 
-    /**
-     * The actual path of the resource
-     */
-    protected String resPath;
-
     /**
      * The class loader constant to be used with getBundleInstance API
      */
@@ -131,13 +125,37 @@ public  class ICUResourceBundle extends UResourceBundle {
      }
 
     /**
-     * Returns the respath of this bundle
-     * @return the respath of the bundle
+     * Fields for a whole bundle, rather than any specific resource in the bundle.
+     * Corresponds roughly to ICU4C/source/common/uresimp.h struct UResourceDataEntry.
      */
-    public String getResPath(){
-        return resPath;
+    protected static final class WholeBundle {
+        WholeBundle(String baseName, String localeID, ClassLoader loader,
+                ICUResourceBundleReader reader) {
+            this.baseName = baseName;
+            this.localeID = localeID;
+            this.ulocale = new ULocale(localeID);
+            this.loader = loader;
+            this.reader = reader;
+        }
+
+        String baseName;
+        String localeID;
+        ULocale ulocale;
+        ClassLoader loader;
+
+        /**
+         * Access to the bits and bytes of the resource bundle.
+         * Hides low-level details.
+         */
+        ICUResourceBundleReader reader;
+
+        // TODO: Remove topLevelKeys when we upgrade to Java 6 where ResourceBundle caches the keySet().
+        Set<String> topLevelKeys;
     }
 
+    WholeBundle wholeBundle;
+    private ICUResourceBundle container;
+
     /**
      * Returns a functionally equivalent locale, considering keywords as well, for the specified keyword.
      * @param baseName resource specifier
@@ -339,7 +357,7 @@ public  class ICUResourceBundle extends UResourceBundle {
         ICUResourceBundle actualBundle = this;
 
         // now recurse to pick up sub levels of the items
-        ICUResourceBundle result = findResourceWithFallback(path, actualBundle, null, null);
+        ICUResourceBundle result = findResourceWithFallback(path, actualBundle, null);
 
         if (result == null) {
             throw new MissingResourceException(
@@ -384,21 +402,17 @@ public  class ICUResourceBundle extends UResourceBundle {
      * @return the resource, or null
      */
     public ICUResourceBundle findWithFallback(String path) {
-        return findResourceWithFallback(path, this, null, null);
+        return findResourceWithFallback(path, this, null);
     }
     public String findStringWithFallback(String path) {
-        Output<String> outString = new Output<String>();
-        findResourceWithFallback(path, this, null, outString);
-        return outString.value;
+        return findStringWithFallback(path, this, null);
     }
 
     // will throw type mismatch exception if the resource is not a string
     public String getStringWithFallback(String path) throws MissingResourceException {
         // Optimized form of getWithFallback(path).getString();
         ICUResourceBundle actualBundle = this;
-        Output<String> outString = new Output<String>();
-        findResourceWithFallback(path, actualBundle, null, outString);
-        String result = outString.value;
+        String result = findStringWithFallback(path, actualBundle, null);
 
         if (result == null) {
             throw new MissingResourceException(
@@ -796,64 +810,229 @@ public  class ICUResourceBundle extends UResourceBundle {
         return GET_AVAILABLE_CACHE.getInstance(key, loader);
     }
 
-    protected static final ICUResourceBundle findResourceWithFallback(String path,
-            UResourceBundle actualBundle, UResourceBundle requested,
-            Output<String> outString) {
+    private static final ICUResourceBundle findResourceWithFallback(String path,
+            UResourceBundle actualBundle, UResourceBundle requested) {
+        if (path.length() == 0) {
+            return null;
+        }
         ICUResourceBundle sub = null;
         if (requested == null) {
             requested = actualBundle;
         }
 
         ICUResourceBundle base = (ICUResourceBundle) actualBundle;
-        String basePath = ((ICUResourceBundle)actualBundle).resPath.length() > 0 ?
-                ((ICUResourceBundle)actualBundle).resPath : "";
-
-        while (base != null) {
-            if (path.indexOf('/') == -1) { // skip the tokenizer
-                if (outString != null && base instanceof ICUResourceBundleImpl.ResourceTable) {
-                    String s = ((ICUResourceBundleImpl.ResourceTable)base).getStringOrNull(path);
-                    if (s != null) {
-                        outString.value = s;
-                        return null;
-                    }
+        // Collect existing and parsed key objects into an array of keys,
+        // rather than assembling and parsing paths.
+        int depth = base.getResDepth();
+        int numPathKeys = countPathKeys(path);
+        assert numPathKeys > 0;
+        String[] keys = new String[depth + numPathKeys];
+        getResPathKeys(path, numPathKeys, keys, depth);
+
+        for (;;) {  // Iterate over the parent bundles.
+            for (;;) {  // Iterate over the keys on the requested path, within a bundle.
+                String subKey = keys[depth++];
+                sub = (ICUResourceBundle) base.handleGet(subKey, null, requested);
+                if (sub == null) {
+                    --depth;
+                    break;
                 }
-                sub = (ICUResourceBundle) base.handleGet(path, null, requested);
-                if (sub != null) {
-                    if (outString != null && sub.getType() == STRING) {
-                        outString.value = sub.getString();  // string from alias handling
-                        return null;
+                if (depth == keys.length) {
+                    // We found it.
+                    sub.setLoadingStatus(((ICUResourceBundle)requested).getLocaleID());
+                    return sub;
+                }
+                base = sub;
+            }
+            // Try the parent bundle of the last-found resource.
+            ICUResourceBundle nextBase = (ICUResourceBundle)base.getParent();
+            if (nextBase == null) {
+                return null;
+            }
+            // If we followed an alias, then we may have switched bundle (locale) and key path.
+            // Set the lower parts of the path according to the last-found resource.
+            // This relies on a resource found via alias to have its original location information,
+            // rather than the location of the alias.
+            int baseDepth = base.getResDepth();
+            if (depth != baseDepth) {
+                String[] newKeys = new String[baseDepth + (keys.length - depth)];
+                System.arraycopy(keys, depth, newKeys, baseDepth, keys.length - depth);
+                keys = newKeys;
+            }
+            base.getResPathKeys(keys, baseDepth);
+            base = nextBase;
+            depth = 0;  // getParent() returned a top level table resource.
+        }
+    }
+
+    /**
+     * Like findResourceWithFallback(...).getString() but with minimal creation of intermediate
+     * ICUResourceBundle objects.
+     */
+    private static final String findStringWithFallback(String path,
+            UResourceBundle actualBundle, UResourceBundle requested) {
+        if (path.length() == 0) {
+            return null;
+        }
+        if (!(actualBundle instanceof ICUResourceBundleImpl.ResourceContainer)) {
+            return null;
+        }
+        if (requested == null) {
+            requested = actualBundle;
+        }
+
+        ICUResourceBundle base = (ICUResourceBundle) actualBundle;
+        ICUResourceBundleReader reader = base.wholeBundle.reader;
+        int res = RES_BOGUS;
+
+        // Collect existing and parsed key objects into an array of keys,
+        // rather than assembling and parsing paths.
+        int baseDepth = base.getResDepth();
+        int depth = baseDepth;
+        int numPathKeys = countPathKeys(path);
+        assert numPathKeys > 0;
+        String[] keys = new String[depth + numPathKeys];
+        getResPathKeys(path, numPathKeys, keys, depth);
+
+        for (;;) {  // Iterate over the parent bundles.
+            for (;;) {  // Iterate over the keys on the requested path, within a bundle.
+                ICUResourceBundleReader.Container readerContainer;
+                if (res == RES_BOGUS) {
+                    int type = base.getType();
+                    if (type == TABLE || type == ARRAY) {
+                        readerContainer = ((ICUResourceBundleImpl.ResourceContainer)base).value;
+                    } else {
+                        break;
+                    }
+                } else {
+                    int type = ICUResourceBundleReader.RES_GET_TYPE(res);
+                    if (ICUResourceBundleReader.URES_IS_TABLE(type)) {
+                        readerContainer = reader.getTable(res);
+                    } else if (ICUResourceBundleReader.URES_IS_ARRAY(type)) {
+                        readerContainer = reader.getArray(res);
+                    } else {
+                        res = RES_BOGUS;
+                        break;
                     }
+                }
+                String subKey = keys[depth++];
+                res = readerContainer.getResource(reader, subKey);
+                if (res == RES_BOGUS) {
+                    --depth;
                     break;
                 }
-            } else {
-                ICUResourceBundle currentBase = base;
-                StringTokenizer st = new StringTokenizer(path, "/");
-                while (st.hasMoreTokens()) {
-                    String subKey = st.nextToken();
-                    sub = ICUResourceBundle.findResourceWithFallback(subKey, currentBase, requested,
-                            st.hasMoreTokens() ? null : outString);
-                    if (sub == null) {
-                        if (outString != null && outString.value != null) {
-                            return null;
+                ICUResourceBundle sub;
+                if (ICUResourceBundleReader.RES_GET_TYPE(res) == ALIAS) {
+                    base.getResPathKeys(keys, baseDepth);
+                    sub = getAliasedResource(base, keys, depth, subKey, res, null, requested);
+                } else {
+                    sub = null;
+                }
+                if (depth == keys.length) {
+                    // We found it.
+                    if (sub != null) {
+                        return sub.getString();  // string from alias handling
+                    } else {
+                        String s = reader.getString(res);
+                        if (s == null) {
+                            throw new UResourceTypeMismatchException("");
                         }
-                        break;
+                        return s;
                     }
-                    currentBase = sub;
                 }
                 if (sub != null) {
-                    //we found it
-                    break;
+                    base = sub;
+                    reader = base.wholeBundle.reader;
+                    res = RES_BOGUS;
+                    // If we followed an alias, then we may have switched bundle (locale) and key path.
+                    // Reserve space for the lower parts of the path according to the last-found resource.
+                    // This relies on a resource found via alias to have its original location information,
+                    // rather than the location of the alias.
+                    baseDepth = base.getResDepth();
+                    if (depth != baseDepth) {
+                        String[] newKeys = new String[baseDepth + (keys.length - depth)];
+                        System.arraycopy(keys, depth, newKeys, baseDepth, keys.length - depth);
+                        keys = newKeys;
+                        depth = baseDepth;
+                    }
                 }
             }
-            // if not try the parent bundle - note, getParent() returns the bundle root
-            base = (ICUResourceBundle)base.getParent();
-            path = basePath.length() > 0 ? basePath + "/" + path : path;
-            basePath = "";
+            // Try the parent bundle of the last-found resource.
+            ICUResourceBundle nextBase = (ICUResourceBundle)base.getParent();
+            if (nextBase == null) {
+                return null;
+            }
+            // We probably have not yet set the lower parts of the key path.
+            base.getResPathKeys(keys, baseDepth);
+            base = nextBase;
+            reader = base.wholeBundle.reader;
+            depth = baseDepth = 0;  // getParent() returned a top level table resource.
         }
-        if(sub != null){
-            sub.setLoadingStatus(((ICUResourceBundle)requested).getLocaleID());
+    }
+
+    private int getResDepth() {
+        return (container == null) ? 0 : container.getResDepth() + 1;
+    }
+
+    /**
+     * Fills some of the keys array with the keys on the path to this resource object.
+     * Writes the top-level key into index 0 and increments from there.
+     *
+     * @param keys
+     * @param depth must be {@link #getResDepth()}
+     */
+    private void getResPathKeys(String[] keys, int depth) {
+        ICUResourceBundle b = this;
+        while (depth > 0) {
+            keys[--depth] = b.key;
+            b = b.container;
+            assert (depth == 0) == (b.container == null);
+        }
+    }
+
+    private static int countPathKeys(String path) {
+        if (path.length() == 0) {
+            return 0;
+        }
+        int num = 1;
+        for (int i = 0; i < path.length(); ++i) {
+            if (path.charAt(i) == RES_PATH_SEP_CHAR) {
+                ++num;
+            }
+        }
+        return num;
+    }
+
+    /**
+     * Fills some of the keys array (from start) with the num keys from the path string.
+     *
+     * @param path path string
+     * @param num must be {@link #countPathKeys(String)}
+     * @param keys
+     * @param start index where the first path key is stored
+     */
+    private static void getResPathKeys(String path, int num, String[] keys, int start) {
+        if (num == 0) {
+            return;
+        }
+        if (num == 1) {
+            keys[start] = path;
+            return;
+        }
+        int i = 0;
+        for (;;) {
+            int j = path.indexOf(RES_PATH_SEP_CHAR, i);
+            assert j >= i;
+            keys[start++] = path.substring(i, j);
+            if (num == 2) {
+                assert path.indexOf(RES_PATH_SEP_CHAR, j + 1) < 0;
+                keys[start] = path.substring(j + 1);
+                break;
+            } else {
+                i = j + 1;
+                --num;
+            }
         }
-        return sub;
     }
 
     public boolean equals(Object other) {
@@ -884,7 +1063,7 @@ public  class ICUResourceBundle extends UResourceBundle {
         }
         return b;
     }
-    //  recursively build bundle .. over-ride super class method.
+    //  recursively build bundle
     protected synchronized static UResourceBundle instantiateBundle(String baseName, String localeID,
                                                                     ClassLoader root, boolean disableFallback){
         ULocale defaultLocale = ULocale.getDefault();
@@ -947,8 +1126,8 @@ public  class ICUResourceBundle extends UResourceBundle {
 
                 b = (ICUResourceBundle)addToCache(root, fullName, defaultLocale, b);
 
-                if (b.getTableResource("%%Parent") != RES_BOGUS) {
-                    String parentLocaleName = b.getString("%%Parent");
+                String parentLocaleName = ((ICUResourceBundleImpl.ResourceTable)b).findString("%%Parent");
+                if (parentLocaleName != null) {
                     parent = instantiateBundle(baseName, parentLocaleName, root, disableFallback);
                 } else if (i != -1) {
                     parent = instantiateBundle(baseName, localeName.substring(0, i), root, disableFallback);
@@ -963,13 +1142,13 @@ public  class ICUResourceBundle extends UResourceBundle {
         }
         return b;
     }
-    UResourceBundle get(String aKey, HashMap<String, String> table, UResourceBundle requested) {
-        ICUResourceBundle obj = (ICUResourceBundle)handleGet(aKey, table, requested);
+    UResourceBundle get(String aKey, HashMap<String, String> aliasesVisited, UResourceBundle requested) {
+        ICUResourceBundle obj = (ICUResourceBundle)handleGet(aKey, aliasesVisited, requested);
         if (obj == null) {
             obj = (ICUResourceBundle)getParent();
             if (obj != null) {
                 //call the get method to recursively fetch the resource
-                obj = (ICUResourceBundle)obj.get(aKey, table, requested);
+                obj = (ICUResourceBundle)obj.get(aKey, aliasesVisited, requested);
             }
             if (obj == null) {
                 String fullName = ICUResourceBundleReader.getFullName(getBaseName(), getLocaleID());
@@ -982,20 +1161,8 @@ public  class ICUResourceBundle extends UResourceBundle {
         return obj;
     }
 
-    protected String localeID;
-    protected String baseName;
-    protected ULocale ulocale;
-    protected ClassLoader loader;
-
-    /**
-     * Access to the bits and bytes of the resource bundle.
-     * Hides low-level details.
-     */
-    protected ICUResourceBundleReader reader;
     /** Data member where the subclasses store the key. */
     protected String key;
-    /** Data member where the subclasses store the offset within resource data. */
-    protected int resource;
 
     /**
      * A resource word value that means "no resource".
@@ -1050,15 +1217,15 @@ public  class ICUResourceBundle extends UResourceBundle {
     }
 
     protected String getLocaleID() {
-        return localeID;
+        return wholeBundle.localeID;
     }
 
     protected String getBaseName() {
-        return baseName;
+        return wholeBundle.baseName;
     }
 
     public ULocale getULocale() {
-        return ulocale;
+        return wholeBundle.ulocale;
     }
 
     public UResourceBundle getParent() {
@@ -1073,113 +1240,96 @@ public  class ICUResourceBundle extends UResourceBundle {
         return key;
     }
 
-    private static final int[] gPublicTypes = new int[] {
-        STRING,
-        BINARY,
-        TABLE,
-        ALIAS,
-
-        TABLE,      /* TABLE32 */
-        TABLE,      /* TABLE16 */
-        STRING,     /* STRING_V2 */
-        INT,
-
-        ARRAY,
-        ARRAY,      /* ARRAY16 */
-        NONE,
-        NONE,
-
-        NONE,
-        NONE,
-        INT_VECTOR,
-        NONE
-    };
-
-    public int getType() {
-        return gPublicTypes[ICUResourceBundleReader.RES_GET_TYPE(resource)];
-    }
-
     /**
      * Get the noFallback flag specified in the loaded bundle.
      * @return The noFallback flag.
      */
     private boolean getNoFallback() {
-        return reader.getNoFallback();
+        return wholeBundle.reader.getNoFallback();
     }
 
     private static ICUResourceBundle getBundle(ICUResourceBundleReader reader,
                                                String baseName, String localeID,
                                                ClassLoader loader) {
-        ICUResourceBundleImpl bundle;
+        ICUResourceBundleImpl.ResourceTable rootTable;
         int rootRes = reader.getRootResource();
-        if(gPublicTypes[ICUResourceBundleReader.RES_GET_TYPE(rootRes)] == TABLE) {
-            bundle = new ICUResourceBundleImpl.ResourceTable(reader, null, "", rootRes, null);
+        if(ICUResourceBundleReader.URES_IS_TABLE(ICUResourceBundleReader.RES_GET_TYPE(rootRes))) {
+            WholeBundle wb = new WholeBundle(baseName, localeID, loader, reader);
+            rootTable = new ICUResourceBundleImpl.ResourceTable(wb, rootRes);
         } else {
             throw new IllegalStateException("Invalid format error");
         }
-        bundle.baseName = baseName;
-        bundle.localeID = localeID;
-        bundle.ulocale = new ULocale(localeID);
-        bundle.loader = loader;
-        UResourceBundle alias = bundle.handleGetImpl("%%ALIAS", null, bundle, null, null); // handleGet will cache the bundle with no parent set
-        if(alias != null) {
-            return (ICUResourceBundle)UResourceBundle.getBundleInstance(baseName, alias.getString());
+        String aliasString = rootTable.findString("%%ALIAS");
+        if(aliasString != null) {
+            return (ICUResourceBundle)UResourceBundle.getBundleInstance(baseName, aliasString);
         } else {
-            return bundle;
+            return rootTable;
         }
     }
+    /**
+     * Constructor for the root table of a bundle.
+     */
+    protected ICUResourceBundle(WholeBundle wholeBundle) {
+        this.wholeBundle = wholeBundle;
+    }
     // constructor for inner classes
-    protected ICUResourceBundle(ICUResourceBundleReader reader, String key, String resPath, int resource,
-                                ICUResourceBundle container) {
-        this.reader = reader;
+    protected ICUResourceBundle(ICUResourceBundle container, String key) {
         this.key = key;
-        this.resPath = resPath;
-        this.resource = resource;
-        if(container != null) {
-            baseName = container.baseName;
-            localeID = container.localeID;
-            ulocale = container.ulocale;
-            loader = container.loader;
-            this.parent = container.parent;
-        }
+        wholeBundle = container.wholeBundle;
+        this.container = (ICUResourceBundleImpl.ResourceContainer) container;
+        parent = container.parent;
     }
 
-    private String getAliasValue(int res) {
-        String result = reader.getAlias(res);
-        return result != null ? result : "";
-    }
     private static final char RES_PATH_SEP_CHAR = '/';
     private static final String RES_PATH_SEP_STR = "/";
     private static final String ICUDATA = "ICUDATA";
     private static final char HYPHEN = '-';
     private static final String LOCALE = "LOCALE";
 
-    protected ICUResourceBundle findResource(String key,
-                                             String resPath,
-                                             int _resource,
-                                             HashMap<String, String> table,
-                                             UResourceBundle requested) {
-        ClassLoader loaderToUse = loader;
+    /**
+     * Returns the resource object referred to from the alias _resource int's path string.
+     * Throws MissingResourceException if not found.
+     *
+     * If the alias path does not contain a key path:
+     * If keys != null then keys[:depth] is used.
+     * Otherwise the base key path plus the key parameter is used.
+     *
+     * @param base A direct or indirect container of the alias.
+     * @param keys The key path to the alias, or null. (const)
+     * @param depth The length of the key path, if keys != null.
+     * @param key The alias' own key within this current container, if keys == null.
+     * @param _resource The alias resource int.
+     * @param aliasesVisited Set of alias path strings already visited, for detecting loops.
+     *        We cannot change the type (e.g., to Set<String>) because it is used
+     *        in protected/@stable UResourceBundle methods.
+     * @param requested The original resource object from which the lookup started,
+     *        which is the starting point for "/LOCALE/..." aliases.
+     * @return the aliased resource object
+     */
+    protected static ICUResourceBundle getAliasedResource(
+            ICUResourceBundle base, String[] keys, int depth,
+            String key, int _resource,
+            HashMap<String, String> aliasesVisited,
+            UResourceBundle requested) {
+        WholeBundle wholeBundle = base.wholeBundle;
+        ClassLoader loaderToUse = wholeBundle.loader;
         String locale = null, keyPath = null;
         String bundleName;
-        String rpath = getAliasValue(_resource);
-        if (table == null) {
-            table = new HashMap<String, String>();
+        String rpath = wholeBundle.reader.getAlias(_resource);
+        if (aliasesVisited == null) {
+            aliasesVisited = new HashMap<String, String>();
         }
-        if (table.get(rpath) != null) {
+        if (aliasesVisited.get(rpath) != null) {
             throw new IllegalArgumentException(
                     "Circular references in the resource bundles");
         }
-        table.put(rpath, "");
+        aliasesVisited.put(rpath, "");
         if (rpath.indexOf(RES_PATH_SEP_CHAR) == 0) {
             int i = rpath.indexOf(RES_PATH_SEP_CHAR, 1);
             int j = rpath.indexOf(RES_PATH_SEP_CHAR, i + 1);
             bundleName = rpath.substring(1, i);
             if (j < 0) {
                 locale = rpath.substring(i + 1);
-                // if key path is not available,
-                // use the given key path
-                keyPath = resPath;
             } else {
                 locale = rpath.substring(i + 1, j);
                 keyPath = rpath.substring(j + 1, rpath.length());
@@ -1203,34 +1353,21 @@ public  class ICUResourceBundle extends UResourceBundle {
                 keyPath = rpath.substring(i + 1);
             } else {
                 locale = rpath;
-                // if key path is not available,
-                // use the given key path
-                keyPath = resPath;
             }
-            bundleName = baseName;
+            bundleName = wholeBundle.baseName;
         }
         ICUResourceBundle bundle = null;
         ICUResourceBundle sub = null;
         if(bundleName.equals(LOCALE)){
-            bundleName = baseName;
+            bundleName = wholeBundle.baseName;
             keyPath = rpath.substring(LOCALE.length() + 2/* prepending and appending / */, rpath.length());
-            locale = ((ICUResourceBundle)requested).getLocaleID();
 
             // Get the top bundle of the requested bundle
-            bundle = (ICUResourceBundle)getBundleInstance(bundleName, locale, loaderToUse, false);
-            if (bundle != null) {
-                sub = ICUResourceBundle.findResourceWithFallback(keyPath, bundle, null, null);
-                // TODO
-                // The resPath of the resolved bundle should reflect the resource path
-                // requested by caller. However, overwriting resPath here will affect cached
-                // resource instance. The resPath is exposed by ICUResourceBundle#getResPath,
-                // but there are no call sites in ICU (and ICUResourceBundle is an implementation
-                // class). We may create a safe clone to overwrite the resPath field, but
-                // it has no benefit at least for now. -Yoshito
-                //if (sub != null) {
-                //    sub.resPath = resPath;
-                //}
+            bundle = (ICUResourceBundle)requested;
+            while (bundle.container != null) {
+                bundle = bundle.container;
             }
+            sub = ICUResourceBundle.findResourceWithFallback(keyPath, bundle, null);
         }else{
             if (locale == null) {
                 // {dlf} must use requestor's class loader to get resources from same jar
@@ -1241,211 +1378,54 @@ public  class ICUResourceBundle extends UResourceBundle {
                          loaderToUse, false);
             }
 
-            StringTokenizer st = new StringTokenizer(keyPath, "/");
-            ICUResourceBundle current = bundle;
-            while (st.hasMoreTokens()) {
-                String subKey = st.nextToken();
-                sub = (ICUResourceBundle)current.get(subKey, table, requested);
-                if (sub == null) {
-                    break;
+            int numKeys;
+            if (keyPath != null) {
+                numKeys = countPathKeys(keyPath);
+                if (numKeys > 0) {
+                    keys = new String[numKeys];
+                    getResPathKeys(keyPath, numKeys, keys, 0);
+                }
+            } else if (keys != null) {
+                numKeys = depth;
+            } else {
+                depth = base.getResDepth();
+                numKeys = depth + 1;
+                keys = new String[numKeys];
+                base.getResPathKeys(keys, depth);
+                keys[depth] = key;
+            }
+            if (numKeys > 0) {
+                sub = bundle;
+                for (int i = 0; sub != null && i < numKeys; ++i) {
+                    sub = (ICUResourceBundle)sub.get(keys[i], aliasesVisited, requested);
                 }
-                current = sub;
             }
-            // TODO
-            // See the comments above.
-            //if (sub != null) {
-            //    sub.resPath = resPath;
-            //}
         }
         if (sub == null) {
-            throw new MissingResourceException(localeID, baseName, key);
+            throw new MissingResourceException(wholeBundle.localeID, wholeBundle.baseName, key);
         }
+        // TODO: If we know that sub is not cached,
+        // then we should set its container and key to the alias' location,
+        // so that it behaves as if its value had been copied into the alias location.
+        // However, findResourceWithFallback() must reroute its bundle and key path
+        // to where the alias data comes from.
         return sub;
     }
 
-    // Resource bundle lookup cache, which may be used by subclasses
-    // which have nested resources
-    protected ICUCache<Object, UResourceBundle> lookup;
-    private static final int MAX_INITIAL_LOOKUP_SIZE = 64;
-
-    protected void createLookupCache() {
-        lookup = new SimpleCache<Object, UResourceBundle>(ICUCache.WEAK, Math.max(getSize()*2, MAX_INITIAL_LOOKUP_SIZE));
-    }
-
-    protected UResourceBundle handleGet(String resKey, HashMap<String, String> table, UResourceBundle requested) {
-        UResourceBundle res = null;
-        if (lookup != null) {
-            res = lookup.get(resKey);
-        }
-        if (res == null) {
-            int[] index = new int[1];
-            boolean[] alias = new boolean[1];
-            res = handleGetImpl(resKey, table, requested, index, alias);
-            if (res != null && lookup != null && !alias[0]) {
-                // We do not want to cache a result from alias entry
-                lookup.put(resKey, res);
-                lookup.put(Integer.valueOf(index[0]), res);
-            }
-        }
-        return res;
-    }
-
-    protected UResourceBundle handleGet(int index, HashMap<String, String> table, UResourceBundle requested) {
-        UResourceBundle res = null;
-        Integer indexKey = null;
-        if (lookup != null) {
-            indexKey = Integer.valueOf(index);
-            res = lookup.get(indexKey);
-        } 
-        if (res == null) {
-            boolean[] alias = new boolean[1];
-            res = handleGetImpl(index, table, requested, alias);
-            if (res != null && lookup != null && !alias[0]) {
-                // We do not want to cache a result from alias entry
-                lookup.put(res.getKey(), res);
-                lookup.put(indexKey, res);
-            }
-        }
-        return res;
-    }
-
-    // Subclass which supports key based resource access to implement this method
-    protected UResourceBundle handleGetImpl(String resKey, HashMap<String, String> table, UResourceBundle requested,
-            int[] index, boolean[] isAlias) {
-        return null;
-    }
-
-    // Subclass which supports index based resource access to implement this method
-    protected UResourceBundle handleGetImpl(int index, HashMap<String, String> table, UResourceBundle requested,
-            boolean[] isAlias) {
-        return null;
-    }
-
-
-     // TODO Below is a set of workarounds created for org.unicode.cldr.icu.ICU2LDMLWriter
-     /* 
-      * Calling getKeys() on a table that has alias's can throw a NullPointerException if parent is not set, 
-      * see trac bug: 6514
-      * -Brian Rower - IBM - Sept. 2008
-      */
-    
-    /**
-     * Returns the resource handle for the given key within the calling resource table.
-     * 
-     * @author Brian Rower
-     */
-    protected int getTableResource(String resKey) {
-        return RES_BOGUS;
-    }
-    protected int getTableResource(int index) {
-        return RES_BOGUS;
-    }
-
-    /**
-     * Determines if the object at the specified index of the calling resource table
-     * is an alias. If it is, returns true
-     * 
-     * @param index The index of the resource to check
-     * @returns True if the resource at 'index' is an alias, false otherwise.
-     * 
-     * @author Brian Rower
-     */
-    public boolean isAlias(int index)
-    {
-        //TODO this is part of a workaround for ticket #6514
-        //if index is out of the resource, return false.
-        return ICUResourceBundleReader.RES_GET_TYPE(getTableResource(index)) == ALIAS;
-    }
-
-    /**
-     * @author Brian Rower
-     */
-    public boolean isAlias()
-    {
-        //TODO this is part of a workaround for ticket #6514
-        return ICUResourceBundleReader.RES_GET_TYPE(resource) == ALIAS;
-    }
-
-    /**
-     * Determines if the object with the specified key 
-     * is an alias. If it is, returns true
-     * 
-     * @returns True if the resource with 'key' is an alias, false otherwise.
-     * @author Brian Rower
-     */
-    public boolean isAlias(String k)
-    {
-        //TODO this is part of a workaround for ticket #6514
-        //this only applies to tables
-        return ICUResourceBundleReader.RES_GET_TYPE(getTableResource(k)) == ALIAS;
-    }
-
     /**
-     * This method can be used to retrieve the underlying alias path (aka where the alias points to)
-     * This method was written to allow conversion from ICU back to LDML format.
-     * 
-     * @param index The index where the alias path points to.
-     * @return The alias path.
-     * @author Brian Rower
+     * @internal
+     * @deprecated This API is ICU internal only.
      */
-    public String getAliasPath(int index)
-    {
-        return getAliasValue(getTableResource(index));
+    public final Set<String> getTopLevelKeySet() {
+        return wholeBundle.topLevelKeys;
     }
 
     /**
-     * @author Brian Rower
+     * @internal
+     * @deprecated This API is ICU internal only.
      */
-    public String getAliasPath()
-    {
-        //TODO cannot allow alias path to end up in public API
-        return getAliasValue(resource);
-    }
-
-    /**
-     * @author Brian Rower
-     */
-    public String getAliasPath(String k)
-    {
-        //TODO cannot allow alias path to end up in public API
-        return getAliasValue(getTableResource(k));
-    }
-    
-    /*
-     * Helper method for getKeysSafe
-     */
-    protected String getKey(int index) {
-        return null;
-    }
-
-    /**
-     * Returns an Enumeration of the keys belonging to this table or array.
-     * This method differs from the getKeys() method by not following alias paths. This method exposes 
-     * underlying alias's. For all general purposes of the ICU resource bundle please use getKeys().
-     * 
-     * @return Keys in this table or array.
-     * @author Brian Rower
-     */
-    public Enumeration<String> getKeysSafe()
-    {
-        //TODO this is part of a workaround for ticket #6514
-        //the safeness only applies to tables, so use the other method if it's not a table
-        if(!ICUResourceBundleReader.URES_IS_TABLE(resource))
-        {
-            return getKeys();
-        }
-        List<String> v = new ArrayList<String>();
-        int size = getSize();
-        for(int index = 0; index < size; index++)
-        {
-            String curKey = getKey(index); 
-            v.add(curKey);
-        }
-
-        //TODO we should use Iterator or List as the return type
-        // instead of Enumeration
-
-        return Collections.enumeration(v);
+    public final void setTopLevelKeySet(Set<String> keySet) {
+        wholeBundle.topLevelKeys = keySet;
     }
 
     // This is the worker function for the public getKeys().
@@ -1459,6 +1439,6 @@ public  class ICUResourceBundle extends UResourceBundle {
     }
 
     protected boolean isTopLevelResource() {
-        return resPath.length() == 0;
+        return container == null;
     }
 }
index db2a7b20a484c8dd3fef13143cae73819d069e32..ac7864e9543adef534a8da3bedc6d877bc118625 100644 (file)
@@ -15,41 +15,35 @@ import com.ibm.icu.util.UResourceBundle;
 import com.ibm.icu.util.UResourceTypeMismatchException;
 
 class ICUResourceBundleImpl extends ICUResourceBundle {
-    protected ICUResourceBundleImpl(ICUResourceBundleReader reader, String key, String resPath, int resource,
-                                    ICUResourceBundleImpl container) {
-        super(reader, key, resPath, resource, container);
+    protected ICUResourceBundleImpl(ICUResourceBundleImpl container, String key) {
+        super(container, key);
+    }
+    ICUResourceBundleImpl(WholeBundle wholeBundle) {
+        super(wholeBundle);
     }
     protected final ICUResourceBundle createBundleObject(String _key,
                                                          int _resource,
-                                                         HashMap<String, String> table,
-                                                         UResourceBundle requested,
-                                                         boolean[] isAlias) {
-        if (isAlias != null) {
-            isAlias[0] = false;
-        }
-        String _resPath = resPath + "/" + _key;
+                                                         HashMap<String, String> aliasesVisited,
+                                                         UResourceBundle requested) {
         switch(ICUResourceBundleReader.RES_GET_TYPE(_resource)) {
         case STRING :
         case STRING_V2:
-            return new ICUResourceBundleImpl.ResourceString(reader, _key, _resPath, _resource, this);
+            return new ICUResourceBundleImpl.ResourceString(this, _key, _resource);
         case BINARY:
-            return new ICUResourceBundleImpl.ResourceBinary(reader, _key, _resPath, _resource, this);
+            return new ICUResourceBundleImpl.ResourceBinary(this, _key, _resource);
         case ALIAS:
-            if (isAlias != null) {
-                isAlias[0] = true;
-            }
-            return findResource(_key, _resPath, _resource, table, requested);
+            return getAliasedResource(this, null, 0, _key, _resource, aliasesVisited, requested);
         case INT:
-            return new ICUResourceBundleImpl.ResourceInt(reader, _key, _resPath, _resource, this);
+            return new ICUResourceBundleImpl.ResourceInt(this, _key, _resource);
         case INT_VECTOR:
-            return new ICUResourceBundleImpl.ResourceIntVector(reader, _key, _resPath, _resource, this);
+            return new ICUResourceBundleImpl.ResourceIntVector(this, _key, _resource);
         case ARRAY:
         case ARRAY16:
-            return new ICUResourceBundleImpl.ResourceArray(reader, _key, _resPath, _resource, this);
+            return new ICUResourceBundleImpl.ResourceArray(this, _key, _resource);
         case TABLE:
         case TABLE16:
         case TABLE32:
-            return new ICUResourceBundleImpl.ResourceTable(reader, _key, _resPath, _resource, this);
+            return new ICUResourceBundleImpl.ResourceTable(this, _key, _resource);
         default :
             throw new IllegalStateException("The resource type is unknown");
         }
@@ -58,41 +52,53 @@ class ICUResourceBundleImpl extends ICUResourceBundle {
     // Scalar values ------------------------------------------------------- ***
 
     private static final class ResourceBinary extends ICUResourceBundleImpl {
+        private int resource;
+        public int getType() {
+            return BINARY;
+        }
         public ByteBuffer getBinary() {
-            return reader.getBinary(resource);
+            return wholeBundle.reader.getBinary(resource);
         }
         public byte [] getBinary(byte []ba) {
-            return reader.getBinary(resource, ba);
+            return wholeBundle.reader.getBinary(resource, ba);
         }
-        ResourceBinary(ICUResourceBundleReader reader, String key, String resPath, int resource,
-                       ICUResourceBundleImpl container) {
-            super(reader, key, resPath, resource, container);
+        ResourceBinary(ICUResourceBundleImpl container, String key, int resource) {
+            super(container, key);
+            this.resource = resource;
         }
     }
     private static final class ResourceInt extends ICUResourceBundleImpl {
+        private int resource;
+        public int getType() {
+            return INT;
+        }
         public int getInt() {
             return ICUResourceBundleReader.RES_GET_INT(resource);
         }
         public int getUInt() {
             return ICUResourceBundleReader.RES_GET_UINT(resource);
         }
-        ResourceInt(ICUResourceBundleReader reader, String key, String resPath, int resource,
-                    ICUResourceBundleImpl container) {
-            super(reader, key, resPath, resource, container);
+        ResourceInt(ICUResourceBundleImpl container, String key, int resource) {
+            super(container, key);
+            this.resource = resource;
         }
     }
     private static final class ResourceString extends ICUResourceBundleImpl {
+        private int resource;
         private String value;
+        public int getType() {
+            return STRING;
+        }
         public String getString() {
             if (value != null) {
                 return value;
             }
-            return reader.getString(resource);
+            return wholeBundle.reader.getString(resource);
         }
-        ResourceString(ICUResourceBundleReader reader, String key, String resPath, int resource,
-                       ICUResourceBundleImpl container) {
-            super(reader, key, resPath, resource, container);
-            String s = reader.getString(resource);
+        ResourceString(ICUResourceBundleImpl container, String key, int resource) {
+            super(container, key);
+            this.resource = resource;
+            String s = wholeBundle.reader.getString(resource);
             // Allow the reader cache's SoftReference to do its job.
             if (s.length() < ICUResourceBundleReader.LARGE_SIZE / 2) {
                 value = s;
@@ -100,18 +106,22 @@ class ICUResourceBundleImpl extends ICUResourceBundle {
         }
     }
     private static final class ResourceIntVector extends ICUResourceBundleImpl {
+        private int resource;
+        public int getType() {
+            return INT_VECTOR;
+        }
         public int[] getIntVector() {
-            return reader.getIntVector(resource);
+            return wholeBundle.reader.getIntVector(resource);
         }
-        ResourceIntVector(ICUResourceBundleReader reader, String key, String resPath, int resource,
-                          ICUResourceBundleImpl container) {
-            super(reader, key, resPath, resource, container);
+        ResourceIntVector(ICUResourceBundleImpl container, String key, int resource) {
+            super(container, key);
+            this.resource = resource;
         }
     }
 
     // Container values ---------------------------------------------------- ***
 
-    private static class ResourceContainer extends ICUResourceBundleImpl {
+    static abstract class ResourceContainer extends ICUResourceBundleImpl {
         protected ICUResourceBundleReader.Container value;
 
         public int getSize() {
@@ -119,34 +129,41 @@ class ICUResourceBundleImpl extends ICUResourceBundle {
         }
         @Override
         public String getString(int index) {
-            int res = value.getContainerResource(reader, index);
+            int res = value.getContainerResource(wholeBundle.reader, index);
             if (res == RES_BOGUS) {
                 throw new IndexOutOfBoundsException();
             }
-            String s = reader.getString(res);
+            String s = wholeBundle.reader.getString(res);
             if (s != null) {
                 return s;
             }
             return super.getString(index);
         }
         protected int getContainerResource(int index) {
-            return value.getContainerResource(reader, index);
+            return value.getContainerResource(wholeBundle.reader, index);
         }
-        protected UResourceBundle createBundleObject(int index, String resKey, HashMap<String, String> table,
-                                                     UResourceBundle requested, boolean[] isAlias) {
+        protected UResourceBundle createBundleObject(int index, String resKey, HashMap<String, String> aliasesVisited,
+                                                     UResourceBundle requested) {
             int item = getContainerResource(index);
             if (item == RES_BOGUS) {
                 throw new IndexOutOfBoundsException();
             }
-            return createBundleObject(resKey, item, table, requested, isAlias);
+            return createBundleObject(resKey, item, aliasesVisited, requested);
+        }
+
+        ResourceContainer(ICUResourceBundleImpl container, String key) {
+            super(container, key);
         }
-        ResourceContainer(ICUResourceBundleReader reader, String key, String resPath, int resource,
-                          ICUResourceBundleImpl container) {
-            super(reader, key, resPath, resource, container);
+        ResourceContainer(WholeBundle wholeBundle) {
+            super(wholeBundle);
         }
     }
     private static class ResourceArray extends ResourceContainer {
+        public int getType() {
+            return ARRAY;
+        }
         protected String[] handleGetStringArray() {
+            ICUResourceBundleReader reader = wholeBundle.reader;
             int length = value.getSize();
             String[] strings = new String[length];
             for (int i = 0; i < length; ++i) {
@@ -161,34 +178,31 @@ class ICUResourceBundleImpl extends ICUResourceBundle {
         public String[] getStringArray() {
             return handleGetStringArray();
         }
-        protected UResourceBundle handleGetImpl(String indexStr, HashMap<String, String> table,
-                                                UResourceBundle requested,
-                                                int[] index, boolean[] isAlias) {
-            int i = indexStr.length() > 0 ? Integer.valueOf(indexStr).intValue() : -1;
-            if(index != null) {
-                index[0] = i;
-            }
-            if (i < 0) {
-                throw new UResourceTypeMismatchException("Could not get the correct value for index: "+ indexStr);
-            }
-            return createBundleObject(i, indexStr, table, requested, isAlias);
+        @Override
+        protected UResourceBundle handleGet(String indexStr, HashMap<String, String> aliasesVisited,
+                                            UResourceBundle requested) {
+            int i = Integer.parseInt(indexStr);
+            return createBundleObject(i, indexStr, aliasesVisited, requested);
         }
-        protected UResourceBundle handleGetImpl(int index, HashMap<String, String> table,
-                                                UResourceBundle requested, boolean[] isAlias) {
-            return createBundleObject(index, Integer.toString(index), table, requested, isAlias);
+        @Override
+        protected UResourceBundle handleGet(int index, HashMap<String, String> aliasesVisited,
+                                            UResourceBundle requested) {
+            return createBundleObject(index, Integer.toString(index), aliasesVisited, requested);
         }
-        ResourceArray(ICUResourceBundleReader reader, String key, String resPath, int resource,
-                      ICUResourceBundleImpl container) {
-            super(reader, key, resPath, resource, container);
-            value = reader.getArray(resource);
-            createLookupCache(); // Use bundle cache to access array entries
+        ResourceArray(ICUResourceBundleImpl container, String key, int resource) {
+            super(container, key);
+            value = wholeBundle.reader.getArray(resource);
         }
     }
     static class ResourceTable extends ResourceContainer {
+        public int getType() {
+            return TABLE;
+        }
         protected String getKey(int index) {
-            return ((ICUResourceBundleReader.Table)value).getKey(reader, index);
+            return ((ICUResourceBundleReader.Table)value).getKey(wholeBundle.reader, index);
         }
         protected Set<String> handleKeySet() {
+            ICUResourceBundleReader reader = wholeBundle.reader;
             TreeSet<String> keySet = new TreeSet<String>();
             ICUResourceBundleReader.Table table = (ICUResourceBundleReader.Table)value;
             for (int i = 0; i < table.getSize(); ++i) {
@@ -196,31 +210,23 @@ class ICUResourceBundleImpl extends ICUResourceBundle {
             }
             return keySet;
         }
-        protected int getTableResource(String resKey) {
-            return ((ICUResourceBundleReader.Table)value).getTableResource(reader, resKey);
-        }
-        protected int getTableResource(int index) {
-            return getContainerResource(index);
-        }
-        protected UResourceBundle handleGetImpl(String resKey, HashMap<String, String> table,
-                                                UResourceBundle requested,
-                                                int[] index, boolean[] isAlias) {
-            int i = ((ICUResourceBundleReader.Table)value).findTableItem(reader, resKey);
-            if(index != null) {
-                index[0] = i;
-            }
+        @Override
+        protected UResourceBundle handleGet(String resKey, HashMap<String, String> aliasesVisited,
+                                            UResourceBundle requested) {
+            int i = ((ICUResourceBundleReader.Table)value).findTableItem(wholeBundle.reader, resKey);
             if (i < 0) {
                 return null;
             }
-            return createBundleObject(i, resKey, table, requested, isAlias);
+            return createBundleObject(resKey, getContainerResource(i), aliasesVisited, requested);
         }
-        protected UResourceBundle handleGetImpl(int index, HashMap<String, String> table,
-                                                UResourceBundle requested, boolean[] isAlias) {
-            String itemKey = ((ICUResourceBundleReader.Table)value).getKey(reader, index);
+        @Override
+        protected UResourceBundle handleGet(int index, HashMap<String, String> aliasesVisited,
+                                            UResourceBundle requested) {
+            String itemKey = ((ICUResourceBundleReader.Table)value).getKey(wholeBundle.reader, index);
             if (itemKey == null) {
                 throw new IndexOutOfBoundsException();
             }
-            return createBundleObject(index, itemKey, table, requested, isAlias);
+            return createBundleObject(itemKey, getContainerResource(index), aliasesVisited, requested);
         }
         @Override
         protected Object handleGetObject(String key) {
@@ -228,6 +234,7 @@ class ICUResourceBundleImpl extends ICUResourceBundle {
             // It would be even better if we could override getString(key)/getStringArray(key),
             // so that we know the expected object type,
             // but those are final in java.util.ResourceBundle.
+            ICUResourceBundleReader reader = wholeBundle.reader;
             int index = ((ICUResourceBundleReader.Table)value).findTableItem(reader, key);
             if (index >= 0) {
                 int res = value.getContainerResource(reader, index);
@@ -261,18 +268,24 @@ class ICUResourceBundleImpl extends ICUResourceBundle {
         /**
          * Returns a String if found, or null if not found or if the key item is not a string.
          */
-        String getStringOrNull(String key) {
+        String findString(String key) {
+            ICUResourceBundleReader reader = wholeBundle.reader;
             int index = ((ICUResourceBundleReader.Table)value).findTableItem(reader, key);
             if (index < 0) {
                 return null;
             }
             return reader.getString(value.getContainerResource(reader, index));
         }
-        ResourceTable(ICUResourceBundleReader reader, String key, String resPath, int resource,
-                      ICUResourceBundleImpl container) {
-            super(reader, key, resPath, resource, container);
-            value = reader.getTable(resource);
-            createLookupCache(); // Use bundle cache to access table entries
+        ResourceTable(ICUResourceBundleImpl container, String key, int resource) {
+            super(container, key);
+            value = wholeBundle.reader.getTable(resource);
+        }
+        /**
+         * Constructor for the root table of a bundle.
+         */
+        ResourceTable(WholeBundle wholeBundle, int rootRes) {
+            super(wholeBundle);
+            value = wholeBundle.reader.getTable(rootRes);
         }
     }
 }
index 72591fdae1854aec09370f9a478d422ef366b03e..6c921db88629b8e74d75dcbad580be51ab1339d5 100644 (file)
@@ -489,7 +489,7 @@ public final class ICUResourceBundleReader {
     static int RES_GET_UINT(int res) {
         return res & 0x0fffffff;
     }
-    private static boolean URES_IS_ARRAY(int type) {
+    static boolean URES_IS_ARRAY(int type) {
         return type == UResourceBundle.ARRAY || type == ICUResourceBundle.ARRAY16;
     }
     static boolean URES_IS_TABLE(int type) {
@@ -843,6 +843,9 @@ public final class ICUResourceBundleReader {
             }
             return reader.getInt(itemsOffset + 4 * index);
         }
+        int getResource(ICUResourceBundleReader reader, String resKey) {
+            return getContainerResource(reader, Integer.parseInt(resKey));
+        }
         Container() {
         }
     }
@@ -905,7 +908,8 @@ public final class ICUResourceBundleReader {
             }
             return URESDATA_ITEM_NOT_FOUND;  /* not found or table is empty. */
         }
-        int getTableResource(ICUResourceBundleReader reader, String resKey) {
+        @Override
+        int getResource(ICUResourceBundleReader reader, String resKey) {
             return getContainerResource(reader, findTableItem(reader, resKey));
         }
         Table() {
index 97a77f8660972b15282509adb38be852f1ceeacb..f1abc35128cbe9c813e61d6e41ac69bd4764fafd 100644 (file)
@@ -823,6 +823,19 @@ public abstract class UResourceBundle extends ResourceBundle {
      */
     @Deprecated
     public Set<String> keySet() {
+        // TODO: Java 6 ResourceBundle has keySet() which calls handleKeySet()
+        // and caches the results.
+        // When we upgrade to Java 6, we still need to check for isTopLevelResource().
+        // Keep the else branch as is. The if body should just return super.keySet().
+        // Remove then-redundant caching of the keys.
+        Set<String> keys = null;
+        ICUResourceBundle icurb = null;
+        if(isTopLevelResource() && this instanceof ICUResourceBundle) {
+            // We do not cache the top-level keys in this base class so that
+            // not every string/int/binary... resource has to have a keys cache field.
+            icurb = (ICUResourceBundle)this;
+            keys = icurb.getTopLevelKeySet();
+        }
         if(keys == null) {
             if(isTopLevelResource()) {
                 TreeSet<String> newKeySet;
@@ -841,6 +854,9 @@ public abstract class UResourceBundle extends ResourceBundle {
                 }
                 newKeySet.addAll(handleKeySet());
                 keys = Collections.unmodifiableSet(newKeySet);
+                if(icurb != null) {
+                    icurb.setTopLevelKeySet(keys);
+                }
             } else {
                 return handleKeySet();
             }
@@ -848,7 +864,6 @@ public abstract class UResourceBundle extends ResourceBundle {
         return keys;
     }
 
-    private Set<String> keys = null;
     /**
      * Returns a Set of the keys contained <i>only</i> in this ResourceBundle.
      * This does not include further keys from parent bundles.
@@ -969,14 +984,14 @@ public abstract class UResourceBundle extends ResourceBundle {
      * {@icu} 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 aliasesVisited 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<String, String> table
+    protected UResourceBundle handleGet(String aKey, HashMap<String, String> aliasesVisited
                                         UResourceBundle requested) {
         return null;
     }
@@ -985,14 +1000,14 @@ public abstract class UResourceBundle extends ResourceBundle {
      * {@icu} 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 aliasesVisited 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<String, String> table
+    protected UResourceBundle handleGet(int index, HashMap<String, String> aliasesVisited
                                         UResourceBundle requested) {
         return null;
     }
index f838098c3e669e8870e54cf21aa1a08e82fcdf6e..0168f3f058527394462ae8538d12c923a59d95b4 100644 (file)
@@ -1,7 +1,7 @@
 /**
  *******************************************************************************
- * Copyright (C) 2001-2013, International Business Machines Corporation and    *
- * others. All Rights Reserved.                                                *
+ * Copyright (C) 2001-2014, International Business Machines Corporation and
+ * others. All Rights Reserved.
  *******************************************************************************
  */
 package com.ibm.icu.dev.test.util;
@@ -39,7 +39,6 @@ public final class ICUResourceBundleTest extends TestFmwk {
     public static void main(String args[]) throws Exception {
         ICUResourceBundleTest test = new ICUResourceBundleTest();
         test.run(args);
-
     }
     public void TestGetResources(){
         try{
@@ -681,20 +680,20 @@ public final class ICUResourceBundleTest extends TestFmwk {
         ULocale[] locales = ULocale.getAvailableLocales();
         for (int i = 0; i < locales.length; ++i) {
             if (!hasLocalizedCountryFor(ULocale.ENGLISH, locales[i]) && (locales[i].getLanguage().compareTo("ti") != 0)){ // TODO: restore test for ti_* when cldrbug 3058 is fixed
-                 errln("Could not get localized country for "+ locales[i]);
+                 errln("Could not get English localized country for " + locales[i]);
             }
             if(!hasLocalizedLanguageFor(ULocale.ENGLISH, locales[i])){
-                errln("Could not get localized language for "+ locales[i]);
+                errln("Could not get English localized language for " + locales[i]);
             }
             if(!hasLocalizedCountryFor(locales[i], locales[i]) &&
                     (locales[i].getLanguage().compareTo("ti") != 0) && // TODO: restore test for ti_* when cldrbug 3058 is fixed
                     (locales[i].getBaseName().compareTo("nl_CW") != 0) && // TODO: restore test for nl_CW when cldrbug 4306 is fixed
                     (locales[i].getBaseName().compareTo("nl_SX") != 0) ){ // TODO: restore test for nl_SX when cldrbug 4306 is fixed
-                errln("Could not get localized country for "+ locales[i]);
+                errln("Could not get native localized country for " + locales[i]);
                 hasLocalizedCountryFor(locales[i], locales[i]);
             }
             if(!hasLocalizedLanguageFor(locales[i], locales[i]) && (locales[i].getLanguage().compareTo("nmg") != 0)){
-                errln("Could not get localized language for "+ locales[i]);
+                errln("Could not get native localized language for " + locales[i]);
             }
 
             logln(locales[i] + "\t" + locales[i].getDisplayName(ULocale.ENGLISH) + "\t" + locales[i].getDisplayName(locales[i]));
@@ -1141,9 +1140,6 @@ public final class ICUResourceBundleTest extends TestFmwk {
         if (rb7.getKey() != null) {
             errln("getKey() call should have returned null.");
         }
-        if (((ICUResourceBundle)rb1).getResPath() == null) {
-            errln("Error calling getResPath().");
-        }
         if (((ICUResourceBundle)rb1).findTopLevel(0) == null) {
             errln("Error calling findTopLevel().");
         }