]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/uresbund.cpp
ICU-57132.0.1.tar.gz
[apple/icu.git] / icuSources / common / uresbund.cpp
index 2219b740da39769d0379fb39f14ef2af41a7890c..62460f8e20da03ea990c553c60da5b3b53ebd489 100644 (file)
@@ -1,10 +1,10 @@
 /*
 ******************************************************************************
 /*
 ******************************************************************************
-* Copyright (C) 1997-2013, International Business Machines Corporation and
+* Copyright (C) 1997-2016, International Business Machines Corporation and
 * others. All Rights Reserved.
 ******************************************************************************
 *
 * others. All Rights Reserved.
 ******************************************************************************
 *
-* File URESBUND.C
+* File uresbund.cpp
 *
 * Modification History:
 *
 *
 * Modification History:
 *
@@ -34,7 +34,9 @@
 #include "ulocimp.h"
 #include "umutex.h"
 #include "putilimp.h"
 #include "ulocimp.h"
 #include "umutex.h"
 #include "putilimp.h"
+#include "uassert.h"
 
 
+using namespace icu;
 
 /*
 Static cache for already opened resource bundles - mostly for keeping fallback info
 
 /*
 Static cache for already opened resource bundles - mostly for keeping fallback info
@@ -42,6 +44,7 @@ TODO: This cache should probably be removed when the deprecated code is
       completely removed.
 */
 static UHashtable *cache = NULL;
       completely removed.
 */
 static UHashtable *cache = NULL;
+static icu::UInitOnce gCacheInitOnce;
 
 static UMutex resbMutex = U_MUTEX_INITIALIZER;
 
 
 static UMutex resbMutex = U_MUTEX_INITIALIZER;
 
@@ -51,7 +54,7 @@ static int32_t U_CALLCONV hashEntry(const UHashTok parm) {
     UHashTok namekey, pathkey;
     namekey.pointer = b->fName;
     pathkey.pointer = b->fPath;
     UHashTok namekey, pathkey;
     namekey.pointer = b->fName;
     pathkey.pointer = b->fPath;
-    return uhash_hashChars(namekey)+37*uhash_hashChars(pathkey);
+    return uhash_hashChars(namekey)+37u*uhash_hashChars(pathkey);
 }
 
 /* INTERNAL: compares two entries */
 }
 
 /* INTERNAL: compares two entries */
@@ -184,7 +187,7 @@ static int32_t ures_flushCache()
     do {
         deletedMore = FALSE;
         /*creates an enumeration to iterate through every element in the table */
     do {
         deletedMore = FALSE;
         /*creates an enumeration to iterate through every element in the table */
-        pos = -1;
+        pos = UHASH_FIRST;
         while ((e = uhash_nextElement(cache, &pos)) != NULL)
         {
             resB = (UResourceDataEntry *) e->value.pointer;
         while ((e = uhash_nextElement(cache, &pos)) != NULL)
         {
             resB = (UResourceDataEntry *) e->value.pointer;
@@ -219,7 +222,7 @@ static int32_t ures_flushCache()
 
 U_CAPI UBool U_EXPORT2 ures_dumpCacheContents(void) {
   UBool cacheNotEmpty = FALSE;
 
 U_CAPI UBool U_EXPORT2 ures_dumpCacheContents(void) {
   UBool cacheNotEmpty = FALSE;
-  int32_t pos = -1;
+  int32_t pos = UHASH_FIRST;
   const UHashElement *e;
   UResourceDataEntry *resB;
   
   const UHashElement *e;
   UResourceDataEntry *resB;
   
@@ -256,34 +259,22 @@ static UBool U_CALLCONV ures_cleanup(void)
 {
     if (cache != NULL) {
         ures_flushCache();
 {
     if (cache != NULL) {
         ures_flushCache();
-        if (cache != NULL && uhash_count(cache) == 0) {
-            uhash_close(cache);
-            cache = NULL;
-        }
+        uhash_close(cache);
+        cache = NULL;
     }
     }
-    return (cache == NULL);
+    gCacheInitOnce.reset();
+    return TRUE;
 }
 
 /** INTERNAL: Initializes the cache for resources */
 }
 
 /** INTERNAL: Initializes the cache for resources */
+static void createCache(UErrorCode &status) {
+    U_ASSERT(cache == NULL);
+    cache = uhash_open(hashEntry, compareEntries, NULL, &status);
+    ucln_common_registerCleanup(UCLN_COMMON_URES, ures_cleanup);
+}
+     
 static void initCache(UErrorCode *status) {
 static void initCache(UErrorCode *status) {
-    UBool makeCache = FALSE;
-    UMTX_CHECK(&resbMutex, (cache ==  NULL), makeCache);
-    if(makeCache) {
-        UHashtable *newCache = uhash_open(hashEntry, compareEntries, NULL, status);
-        if (U_FAILURE(*status)) {
-            return;
-        }
-        umtx_lock(&resbMutex);
-        if(cache == NULL) {
-            cache = newCache;
-            newCache = NULL;
-            ucln_common_registerCleanup(UCLN_COMMON_URES, ures_cleanup);
-        }
-        umtx_unlock(&resbMutex);
-        if(newCache != NULL) {
-            uhash_close(newCache);
-        }
-    }
+    umtx_initOnce(gCacheInitOnce, &createCache, *status);
 }
 
 /** INTERNAL: sets the name (locale) of the resource bundle to given name */
 }
 
 /** INTERNAL: sets the name (locale) of the resource bundle to given name */
@@ -386,6 +377,7 @@ static UResourceDataEntry *init_entry(const char *localeID, const char *path, UE
                     const int32_t *poolIndexes = r->fPool->fData.pRoot + 1;
                     if(r->fData.pRoot[1 + URES_INDEX_POOL_CHECKSUM] == poolIndexes[URES_INDEX_POOL_CHECKSUM]) {
                         r->fData.poolBundleKeys = (const char *)(poolIndexes + (poolIndexes[URES_INDEX_LENGTH] & 0xff));
                     const int32_t *poolIndexes = r->fPool->fData.pRoot + 1;
                     if(r->fData.pRoot[1 + URES_INDEX_POOL_CHECKSUM] == poolIndexes[URES_INDEX_POOL_CHECKSUM]) {
                         r->fData.poolBundleKeys = (const char *)(poolIndexes + (poolIndexes[URES_INDEX_LENGTH] & 0xff));
+                        r->fData.poolBundleStrings = r->fPool->fData.p16BitUnits;
                     } else {
                         r->fBogus = *status = U_INVALID_FORMAT_ERROR;
                     }
                     } else {
                         r->fBogus = *status = U_INVALID_FORMAT_ERROR;
                     }
@@ -455,7 +447,9 @@ getPoolEntry(const char *path, UErrorCode *status) {
 
 /* INTERNAL: */
 /*   CAUTION:  resbMutex must be locked when calling this function! */
 
 /* INTERNAL: */
 /*   CAUTION:  resbMutex must be locked when calling this function! */
-static UResourceDataEntry *findFirstExisting(const char* path, char* name, UBool *isRoot, UBool *hasChopped, UBool *isDefault, UErrorCode* status) {
+static UResourceDataEntry *
+findFirstExisting(const char* path, char* name,
+                  UBool *isRoot, UBool *hasChopped, UBool *isDefault, UErrorCode* status) {
     UResourceDataEntry *r = NULL;
     UBool hasRealData = FALSE;
     const char *defaultLoc = uloc_getDefault();
     UResourceDataEntry *r = NULL;
     UBool hasRealData = FALSE;
     const char *defaultLoc = uloc_getDefault();
@@ -511,15 +505,106 @@ U_CFUNC void ures_initStackObject(UResourceBundle* resB) {
   ures_setIsStackObject(resB, TRUE);
 }
 
   ures_setIsStackObject(resB, TRUE);
 }
 
-static UResourceDataEntry *entryOpen(const char* path, const char* localeID, UErrorCode* status) {
-    UErrorCode intStatus = U_ZERO_ERROR;
+static UBool  // returns U_SUCCESS(*status)
+loadParentsExceptRoot(UResourceDataEntry *&t1,
+                      char name[], int32_t nameCapacity,
+                      UBool usingUSRData, char usrDataPath[], UErrorCode *status) {
+    if (U_FAILURE(*status)) { return FALSE; }
+    UBool hasChopped = TRUE;
+    while (hasChopped && t1->fParent == NULL && !t1->fData.noFallback &&
+            res_getResource(&t1->fData,"%%ParentIsRoot") == RES_BOGUS) {
+        Resource parentRes = res_getResource(&t1->fData, "%%Parent");
+        if (parentRes != RES_BOGUS) {  // An explicit parent was found.
+            int32_t parentLocaleLen = 0;
+            const UChar *parentLocaleName = res_getString(&(t1->fData), parentRes, &parentLocaleLen);
+            if(parentLocaleName != NULL && 0 < parentLocaleLen && parentLocaleLen < nameCapacity) {
+                u_UCharsToChars(parentLocaleName, name, parentLocaleLen + 1);
+                if (uprv_strcmp(name, kRootLocaleName) == 0) {
+                    return TRUE;
+                }
+            }
+        }
+        // Insert regular parents.
+        UErrorCode parentStatus = U_ZERO_ERROR;
+        UResourceDataEntry *t2 = init_entry(name, t1->fPath, &parentStatus);
+        if (U_FAILURE(parentStatus)) {
+            *status = parentStatus;
+            return FALSE;
+        }
+        UResourceDataEntry *u2 = NULL;
+        UErrorCode usrStatus = U_ZERO_ERROR;
+        if (usingUSRData) {  // This code inserts user override data into the inheritance chain.
+            u2 = init_entry(name, usrDataPath, &usrStatus);
+        }
+
+        if (usingUSRData && U_SUCCESS(usrStatus) && u2->fBogus == U_ZERO_ERROR) {
+            t1->fParent = u2;
+            u2->fParent = t2;
+        } else {
+            t1->fParent = t2;
+            if (usingUSRData) {
+                // The USR override data wasn't found, set it to be deleted.
+                u2->fCountExisting = 0;
+            }
+        }
+        t1 = t2;
+        hasChopped = chopLocale(name);
+    }
+    return TRUE;
+}
+
+static UBool  // returns U_SUCCESS(*status)
+insertRootBundle(UResourceDataEntry *&t1, UErrorCode *status) {
+    if (U_FAILURE(*status)) { return FALSE; }
     UErrorCode parentStatus = U_ZERO_ERROR;
     UErrorCode parentStatus = U_ZERO_ERROR;
-    UErrorCode usrStatus = U_ZERO_ERROR;
+    UResourceDataEntry *t2 = init_entry(kRootLocaleName, t1->fPath, &parentStatus);
+    if (U_FAILURE(parentStatus)) {
+        *status = parentStatus;
+        return FALSE;
+    }
+    t1->fParent = t2;
+    t1 = t2;
+    return TRUE;
+}
+
+enum UResOpenType {
+    /**
+     * Open a resource bundle for the locale;
+     * if there is not even a base language bundle, then fall back to the default locale;
+     * if there is no bundle for that either, then load the root bundle.
+     *
+     * This is the default bundle loading behavior.
+     */
+    URES_OPEN_LOCALE_DEFAULT_ROOT,
+    // TODO: ICU ticket #11271 "consistent default locale across locale trees"
+    // Add an option to look at the main locale tree for whether to
+    // fall back to root directly (if the locale has main data) or
+    // fall back to the default locale first (if the locale does not even have main data).
+    /**
+     * Open a resource bundle for the locale;
+     * if there is not even a base language bundle, then load the root bundle;
+     * never fall back to the default locale.
+     *
+     * This is used for algorithms that have good pan-Unicode default behavior,
+     * such as case mappings, collation, and segmentation (BreakIterator).
+     */
+    URES_OPEN_LOCALE_ROOT,
+    /**
+     * Open a resource bundle for the exact bundle name as requested;
+     * no fallbacks, do not load parent bundles.
+     *
+     * This is used for supplemental (non-locale) data.
+     */
+    URES_OPEN_DIRECT
+};
+typedef enum UResOpenType UResOpenType;
+
+static UResourceDataEntry *entryOpen(const char* path, const char* localeID,
+                                     UResOpenType openType, UErrorCode* status) {
+    U_ASSERT(openType != URES_OPEN_DIRECT);
+    UErrorCode intStatus = U_ZERO_ERROR;
     UResourceDataEntry *r = NULL;
     UResourceDataEntry *t1 = NULL;
     UResourceDataEntry *r = NULL;
     UResourceDataEntry *t1 = NULL;
-    UResourceDataEntry *t2 = NULL;
-    UResourceDataEntry *u1 = NULL;
-    UResourceDataEntry *u2 = NULL;
     UBool isDefault = FALSE;
     UBool isRoot = FALSE;
     UBool hasRealData = FALSE;
     UBool isDefault = FALSE;
     UBool isRoot = FALSE;
     UBool hasRealData = FALSE;
@@ -559,7 +644,8 @@ static UResourceDataEntry *entryOpen(const char* path, const char* localeID, UEr
             t1 = r;
             hasRealData = TRUE;
             if ( usingUSRData ) {  /* This code inserts user override data into the inheritance chain */
             t1 = r;
             hasRealData = TRUE;
             if ( usingUSRData ) {  /* This code inserts user override data into the inheritance chain */
-               u1 = init_entry(t1->fName, usrDataPath, &usrStatus);
+                UErrorCode usrStatus = U_ZERO_ERROR;
+                UResourceDataEntry *u1 = init_entry(t1->fName, usrDataPath, &usrStatus);
                if ( u1 != NULL ) {
                  if(u1->fBogus == U_ZERO_ERROR) {
                    u1->fParent = t1;
                if ( u1 != NULL ) {
                  if(u1->fBogus == U_ZERO_ERROR) {
                    u1->fParent = t1;
@@ -570,48 +656,16 @@ static UResourceDataEntry *entryOpen(const char* path, const char* localeID, UEr
                  }
                }
             }
                  }
                }
             }
-            while (hasChopped && !isRoot && t1->fParent == NULL && !t1->fData.noFallback) {
-                if ( res_getResource(&t1->fData,"%%Parent") != RES_BOGUS) { /* An explicit parent was found */
-                    int32_t parentLocaleLen = 0;
-                    const UChar *parentLocaleName = res_getString(&(t1->fData), res_getResource(&t1->fData,"%%Parent") , &parentLocaleLen);
-                    if(parentLocaleName != NULL && parentLocaleLen > 0) {
-                        u_UCharsToChars(parentLocaleName, name, parentLocaleLen+1);
-                        if ( !uprv_strcmp(name,"root") ) { /* If parent is root, we just terminate the loop */
-                            hasChopped = FALSE;
-                            continue;
-                        }
-                    }
-                }
-                /* insert regular parents */
-                t2 = init_entry(name, t1->fPath, &parentStatus);
-                if ( usingUSRData ) {  /* This code inserts user override data into the inheritance chain */
-                    usrStatus = U_ZERO_ERROR;
-                    u2 = init_entry(name, usrDataPath, &usrStatus);
-                }
-                /* Check for null pointer. */
-                if (t2 == NULL || ( usingUSRData && u2 == NULL)) {
-                    *status = U_MEMORY_ALLOCATION_ERROR;
+            if (hasChopped && !isRoot) {
+                if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) {
                     goto finishUnlock;
                 }
                     goto finishUnlock;
                 }
-                
-                if ( usingUSRData && u2->fBogus == U_ZERO_ERROR ) {
-                    t1->fParent = u2;
-                    u2->fParent = t2;
-                } else {
-                    t1->fParent = t2;
-                    if(usingUSRData) {
-                        /* the USR override data wasn't found, set it to be deleted */
-                        u2->fCountExisting = 0;
-                    }
-                }
-                t1 = t2;
-                hasChopped = chopLocale(name);
             }
         }
 
         /* we could have reached this point without having any real data */
         /* if that is the case, we need to chain in the default locale   */
             }
         }
 
         /* we could have reached this point without having any real data */
         /* if that is the case, we need to chain in the default locale   */
-        if(r==NULL && !isDefault && !isRoot /*&& t1->fParent == NULL*/) {
+        if(r==NULL && openType == URES_OPEN_LOCALE_DEFAULT_ROOT && !isDefault && !isRoot) {
             /* insert default locale */
             uprv_strcpy(name, uloc_getDefault());
             r = findFirstExisting(path, name, &isRoot, &hasChopped, &isDefault, &intStatus);
             /* insert default locale */
             uprv_strcpy(name, uloc_getDefault());
             r = findFirstExisting(path, name, &isRoot, &hasChopped, &isDefault, &intStatus);
@@ -620,31 +674,11 @@ static UResourceDataEntry *entryOpen(const char* path, const char* localeID, UEr
                 t1 = r;
                 hasRealData = TRUE;
                 isDefault = TRUE;
                 t1 = r;
                 hasRealData = TRUE;
                 isDefault = TRUE;
-                while (hasChopped && t1->fParent == NULL) {
-                    if ( res_getResource(&t1->fData,"%%Parent") != RES_BOGUS) { /* An explicit parent was found */
-                        int32_t parentLocaleLen = 0;
-                        const UChar *parentLocaleName = res_getString(&(t1->fData), res_getResource(&t1->fData,"%%Parent") , &parentLocaleLen);
-                        if(parentLocaleName != NULL && parentLocaleLen > 0) {
-                            u_UCharsToChars(parentLocaleName, name, parentLocaleLen+1);
-                            if ( !uprv_strcmp(name,"root") ) { /* If parent is root, we just terminate the loop */
-                                hasChopped = FALSE;
-                                continue;
-                            }
-                        }
-                    }
-                    /* insert chopped defaults */
-                    t2 = init_entry(name, t1->fPath, &parentStatus);
-                    /* Check for null pointer. */
-                    if (t2 == NULL) {
-                        *status = U_MEMORY_ALLOCATION_ERROR;
+                // TODO: Why not if (usingUSRData) { ... } like in the non-default-locale code path?
+                if (hasChopped && !isRoot) {
+                    if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) {
                         goto finishUnlock;
                     }
                         goto finishUnlock;
                     }
-
-                    if ( res_getResource(&t1->fData,"%%ParentIsRoot") == RES_BOGUS) {
-                        t1->fParent = t2;
-                        t1 = t2;
-                    }
-                    hasChopped = chopLocale(name);
                 }
             } 
         }
                 }
             } 
         }
@@ -662,46 +696,89 @@ static UResourceDataEntry *entryOpen(const char* path, const char* localeID, UEr
                 *status = U_MISSING_RESOURCE_ERROR;
                 goto finishUnlock;
             }
                 *status = U_MISSING_RESOURCE_ERROR;
                 goto finishUnlock;
             }
-        } else if(!isRoot && uprv_strcmp(t1->fName, kRootLocaleName) != 0 && t1->fParent == NULL && !r->fData.noFallback) {
-            /* insert root locale */
-            t2 = init_entry(kRootLocaleName, t1->fPath, &parentStatus);
-            /* Check for null pointer. */
-            if (t2 == NULL) {
-                *status = U_MEMORY_ALLOCATION_ERROR;
+        } else if(!isRoot && uprv_strcmp(t1->fName, kRootLocaleName) != 0 &&
+                t1->fParent == NULL && !r->fData.noFallback) {
+            if (!insertRootBundle(t1, status)) {
                 goto finishUnlock;
             }
             if(!hasRealData) {
                 r->fBogus = U_USING_DEFAULT_WARNING;
             }
                 goto finishUnlock;
             }
             if(!hasRealData) {
                 r->fBogus = U_USING_DEFAULT_WARNING;
             }
-            hasRealData = (UBool)((t2->fBogus == U_ZERO_ERROR) || hasRealData);
-            t1->fParent = t2;
-            t1 = t2;
         }
 
         }
 
+        // TODO: Does this ever loop?
         while(r != NULL && !isRoot && t1->fParent != NULL) {
             t1->fParent->fCountExisting++;
             t1 = t1->fParent;
         while(r != NULL && !isRoot && t1->fParent != NULL) {
             t1->fParent->fCountExisting++;
             t1 = t1->fParent;
-            hasRealData = (UBool)((t1->fBogus == U_ZERO_ERROR) || hasRealData);
         }
     } /* umtx_lock */
 finishUnlock:
     umtx_unlock(&resbMutex);
 
     if(U_SUCCESS(*status)) {
         }
     } /* umtx_lock */
 finishUnlock:
     umtx_unlock(&resbMutex);
 
     if(U_SUCCESS(*status)) {
-        if(U_SUCCESS(parentStatus)) {
-            if(intStatus != U_ZERO_ERROR) {
-                *status = intStatus;  
-            }
-            return r;
-        } else {
-            *status = parentStatus;
-            return NULL;
+        if(intStatus != U_ZERO_ERROR) {
+            *status = intStatus;  
         }
         }
+        return r;
     } else {
         return NULL;
     }
 }
 
     } else {
         return NULL;
     }
 }
 
+/**
+ * Version of entryOpen() and findFirstExisting() for ures_openDirect(),
+ * with no fallbacks.
+ * Parent and root locale bundles are loaded if
+ * the requested bundle does not have the "nofallback" flag.
+ */
+static UResourceDataEntry *
+entryOpenDirect(const char* path, const char* localeID, UErrorCode* status) {
+    initCache(status);
+    if(U_FAILURE(*status)) {
+        return NULL;
+    }
+
+    umtx_lock(&resbMutex);
+    // findFirstExisting() without fallbacks.
+    UResourceDataEntry *r = init_entry(localeID, path, status);
+    if(U_SUCCESS(*status)) {
+        if(r->fBogus != U_ZERO_ERROR) {
+            r->fCountExisting--;
+            r = NULL;
+        }
+    } else {
+        r = NULL;
+    }
+
+    // Some code depends on the ures_openDirect() bundle to have a parent bundle chain,
+    // unless it is marked with "nofallback".
+    UResourceDataEntry *t1 = r;
+    if(r != NULL && uprv_strcmp(localeID, kRootLocaleName) != 0 &&  // not root
+            r->fParent == NULL && !r->fData.noFallback &&
+            uprv_strlen(localeID) < ULOC_FULLNAME_CAPACITY) {
+        char name[ULOC_FULLNAME_CAPACITY];
+        uprv_strcpy(name, localeID);
+        if(!chopLocale(name) || uprv_strcmp(name, kRootLocaleName) == 0 ||
+                loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), FALSE, NULL, status)) {
+            if(uprv_strcmp(t1->fName, kRootLocaleName) != 0 && t1->fParent == NULL) {
+                insertRootBundle(t1, status);
+            }
+        }
+        if(U_FAILURE(*status)) {
+            r = NULL;
+        }
+    }
+
+    if(r != NULL) {
+        // TODO: Does this ever loop?
+        while(t1->fParent != NULL) {
+            t1->fParent->fCountExisting++;
+            t1 = t1->fParent;
+        }
+    }
+    umtx_unlock(&resbMutex);
+    return r;
+}
 
 /**
  * Functions to create and destroy resource bundles.
 
 /**
  * Functions to create and destroy resource bundles.
@@ -1408,7 +1485,8 @@ U_CAPI const UChar* U_EXPORT2 ures_getNextString(UResourceBundle *resB, int32_t*
     case URES_BINARY:
     case URES_INT_VECTOR:
         *status = U_RESOURCE_TYPE_MISMATCH;
     case URES_BINARY:
     case URES_INT_VECTOR:
         *status = U_RESOURCE_TYPE_MISMATCH;
-    default: /*fall through*/
+        U_FALLTHROUGH;
+    default:
       return NULL;
     }
   }
       return NULL;
     }
   }
@@ -1671,13 +1749,20 @@ ures_getStringByKeyWithFallback(const UResourceBundle *resB,
     const UChar* retVal = NULL;
     ures_initStackObject(&stack);
     ures_getByKeyWithFallback(resB, inKey, &stack, status);
     const UChar* retVal = NULL;
     ures_initStackObject(&stack);
     ures_getByKeyWithFallback(resB, inKey, &stack, status);
-    retVal = ures_getString(&stack, len, status);
+    int32_t length;
+    retVal = ures_getString(&stack, &length, status);
     ures_close(&stack);
     ures_close(&stack);
-    if ( retVal != NULL && u_strlen(retVal) == 3 && retVal[0] == EMPTY_SET && retVal[1] == EMPTY_SET && retVal[2] == EMPTY_SET ) {
+    if (U_FAILURE(*status)) {
+        return NULL;
+    }
+    if (length == 3 && retVal[0] == EMPTY_SET && retVal[1] == EMPTY_SET && retVal[2] == EMPTY_SET ) {
         retVal = NULL;
         retVal = NULL;
-        *len = 0;
+        length = 0;
         *status = U_MISSING_RESOURCE_ERROR;
     }
         *status = U_MISSING_RESOURCE_ERROR;
     }
+    if (len != NULL) {
+        *len = length;
+    }
     return retVal;
 }
 
     return retVal;
 }
 
@@ -1735,8 +1820,8 @@ ures_getByKeyWithFallback(const UResourceBundle *resB,
         const char* key = inKey;
         if(res == RES_BOGUS) {
             UResourceDataEntry *dataEntry = resB->fData;
         const char* key = inKey;
         if(res == RES_BOGUS) {
             UResourceDataEntry *dataEntry = resB->fData;
-            char path[256];
-            char* myPath = path;
+            CharString path;
+            char *myPath = NULL;
             const char* resPath = resB->fResPath;
             int32_t len = resB->fResPathLen;
             while(res == RES_BOGUS && dataEntry->fParent != NULL) { /* Otherwise, we'll look in parents */
             const char* resPath = resB->fResPath;
             int32_t len = resB->fResPathLen;
             while(res == RES_BOGUS && dataEntry->fParent != NULL) { /* Otherwise, we'll look in parents */
@@ -1744,11 +1829,16 @@ ures_getByKeyWithFallback(const UResourceBundle *resB,
                 rootRes = dataEntry->fData.rootRes;
 
                 if(dataEntry->fBogus == U_ZERO_ERROR) {
                 rootRes = dataEntry->fData.rootRes;
 
                 if(dataEntry->fBogus == U_ZERO_ERROR) {
+                    path.clear();
                     if (len > 0) {
                     if (len > 0) {
-                        uprv_memcpy(path, resPath, len);
+                        path.append(resPath, len, *status);
+                    }
+                    path.append(inKey, *status);
+                    if (U_FAILURE(*status)) {
+                        ures_close(helper);
+                        return fillIn;
                     }
                     }
-                    uprv_strcpy(path+len, inKey);
-                    myPath = path;
+                    myPath = path.data();
                     key = inKey;
                     do {
                         res = res_findResource(&(dataEntry->fData), rootRes, &myPath, &key);
                     key = inKey;
                     do {
                         res = res_findResource(&(dataEntry->fData), rootRes, &myPath, &key);
@@ -1793,6 +1883,121 @@ ures_getByKeyWithFallback(const UResourceBundle *resB,
     return fillIn;
 }
 
     return fillIn;
 }
 
+namespace {
+
+void getAllContainerItemsWithFallback(
+        const UResourceBundle *bundle, ResourceDataValue &value,
+        ResourceArraySink *arraySink, ResourceTableSink *tableSink,
+        UErrorCode &errorCode) {
+    if (U_FAILURE(errorCode)) { return; }
+    // We recursively enumerate child-first,
+    // only storing parent items in the absence of child items.
+    // We store a placeholder value for the no-fallback/no-inheritance marker
+    // to prevent a parent item from being stored.
+    //
+    // It would be possible to recursively enumerate parent-first,
+    // overriding parent items with child items.
+    // When we see the no-fallback/no-inheritance marker,
+    // then we would remove the parent's item.
+    // We would deserialize parent values even though they are overridden in a child bundle.
+    UResType expectedType = arraySink != NULL ? URES_ARRAY : URES_TABLE;
+    if (ures_getType(bundle) == expectedType) {
+        value.pResData = &bundle->fResData;
+        if (arraySink != NULL) {
+            ures_getAllArrayItems(&bundle->fResData, bundle->fRes, value, *arraySink, errorCode);
+        } else /* tableSink != NULL */ {
+            ures_getAllTableItems(&bundle->fResData, bundle->fRes, value, *tableSink, errorCode);
+        }
+    }
+    UResourceDataEntry *entry = bundle->fData->fParent;
+    if (entry != NULL && U_SUCCESS(entry->fBogus)) {
+        // We might try to query the sink whether
+        // any fallback from the parent bundle is still possible.
+
+        // Turn the parent UResourceDataEntry into a UResourceBundle,
+        // much like in ures_openWithType().
+        // TODO: See if we can refactor ures_getByKeyWithFallback()
+        // and pull out an inner function that takes and returns a UResourceDataEntry
+        // so that we need not create UResourceBundle objects.
+        UResourceBundle parentBundle;
+        ures_initStackObject(&parentBundle);
+        parentBundle.fTopLevelData = parentBundle.fData = entry;
+        // TODO: What is the difference between bundle fData and fTopLevelData?
+        uprv_memcpy(&parentBundle.fResData, &entry->fData, sizeof(ResourceData));
+        // TODO: Try to replace bundle.fResData with just using bundle.fData->fData.
+        parentBundle.fHasFallback = !parentBundle.fResData.noFallback;
+        parentBundle.fIsTopLevel = TRUE;
+        parentBundle.fRes = parentBundle.fResData.rootRes;
+        parentBundle.fSize = res_countArrayItems(&(parentBundle.fResData), parentBundle.fRes);
+        parentBundle.fIndex = -1;
+        entryIncrease(entry);
+
+        // Look up the container item in the parent bundle.
+        UResourceBundle containerBundle;
+        ures_initStackObject(&containerBundle);
+        const UResourceBundle *rb;
+        if (bundle->fResPath == NULL || *bundle->fResPath == 0) {
+            rb = &parentBundle;
+        } else {
+            rb = ures_getByKeyWithFallback(&parentBundle, bundle->fResPath,
+                                           &containerBundle, &errorCode);
+        }
+        if (U_SUCCESS(errorCode) && ures_getType(rb) == expectedType) {
+            getAllContainerItemsWithFallback(rb, value,
+                                             arraySink, tableSink, errorCode);
+        }
+        ures_close(&containerBundle);
+        ures_close(&parentBundle);
+    }
+}
+
+void getAllContainerItemsWithFallback(
+        const UResourceBundle *bundle, const char *path,
+        ResourceArraySink *arraySink, ResourceTableSink *tableSink,
+        UErrorCode &errorCode) {
+    if (U_FAILURE(errorCode)) { return; }
+    if (path == NULL) {
+        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
+        return;
+    }
+    UResourceBundle stackBundle;
+    ures_initStackObject(&stackBundle);
+    const UResourceBundle *rb;
+    if (*path == 0) {
+        // empty path
+        rb = bundle;
+    } else {
+        rb = ures_getByKeyWithFallback(bundle, path, &stackBundle, &errorCode);
+        if (U_FAILURE(errorCode)) {
+            ures_close(&stackBundle);
+            return;
+        }
+    }
+    UResType expectedType = arraySink != NULL ? URES_ARRAY : URES_TABLE;
+    if (ures_getType(rb) != expectedType) {
+        errorCode = U_RESOURCE_TYPE_MISMATCH;
+        ures_close(&stackBundle);
+        return;
+    }
+    // Get all table items with fallback.
+    ResourceDataValue value;
+    getAllContainerItemsWithFallback(rb, value, arraySink, tableSink, errorCode);
+    ures_close(&stackBundle);
+}
+
+}  // namespace
+
+U_CAPI void U_EXPORT2
+ures_getAllArrayItemsWithFallback(const UResourceBundle *bundle, const char *path,
+                                  ResourceArraySink &sink, UErrorCode &errorCode) {
+    getAllContainerItemsWithFallback(bundle, path, &sink, NULL, errorCode);
+}
+
+U_CAPI void U_EXPORT2
+ures_getAllTableItemsWithFallback(const UResourceBundle *bundle, const char *path,
+                                  ResourceTableSink &sink, UErrorCode &errorCode) {
+    getAllContainerItemsWithFallback(bundle, path, NULL, &sink, errorCode);
+}
 
 U_CAPI UResourceBundle* U_EXPORT2 ures_getByKey(const UResourceBundle *resB, const char* inKey, UResourceBundle *fillIn, UErrorCode *status) {
     Resource res = RES_BOGUS;
 
 U_CAPI UResourceBundle* U_EXPORT2 ures_getByKey(const UResourceBundle *resB, const char* inKey, UResourceBundle *fillIn, UErrorCode *status) {
     Resource res = RES_BOGUS;
@@ -1985,7 +2190,6 @@ ures_getLocaleByType(const UResourceBundle* resourceBundle,
         case ULOC_VALID_LOCALE:
             return resourceBundle->fTopLevelData->fName;
         case ULOC_REQUESTED_LOCALE:
         case ULOC_VALID_LOCALE:
             return resourceBundle->fTopLevelData->fName;
         case ULOC_REQUESTED_LOCALE:
-            return NULL;
         default:
             *status = U_ILLEGAL_ARGUMENT_ERROR;
             return NULL;
         default:
             *status = U_ILLEGAL_ARGUMENT_ERROR;
             return NULL;
@@ -2011,172 +2215,93 @@ U_CFUNC const char* ures_getPath(const UResourceBundle* resB) {
 }
 #endif
 
 }
 #endif
 
-/* OLD API implementation */
+static UResourceBundle*
+ures_openWithType(UResourceBundle *r, const char* path, const char* localeID,
+                  UResOpenType openType, UErrorCode* status) {
+    if(U_FAILURE(*status)) {
+        return NULL;
+    }
 
 
-/**
- *  API: This function is used to open a resource bundle 
- *  proper fallback chaining is executed while initialization. 
- *  The result is stored in cache for later fallback search.
- */
-U_CAPI void  U_EXPORT2
-ures_openFillIn(UResourceBundle *r, const char* path,
-                    const char* localeID, UErrorCode* status) {
-    if(r == NULL) {
-        *status = U_ILLEGAL_ARGUMENT_ERROR;
-    } else {
-        UResourceDataEntry *firstData;
-        UBool isStackObject = ures_isStackObject(r);
+    UResourceDataEntry *entry;
+    if(openType != URES_OPEN_DIRECT) {
+        /* first "canonicalize" the locale ID */
         char canonLocaleID[ULOC_FULLNAME_CAPACITY];
         char canonLocaleID[ULOC_FULLNAME_CAPACITY];
-
-        uloc_getBaseName(localeID, canonLocaleID, sizeof(canonLocaleID), status);
+        uloc_getBaseName(localeID, canonLocaleID, UPRV_LENGTHOF(canonLocaleID), status);
         if(U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) {
             *status = U_ILLEGAL_ARGUMENT_ERROR;
         if(U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) {
             *status = U_ILLEGAL_ARGUMENT_ERROR;
-            return;
-        }
-
-        ures_closeBundle(r, FALSE);
-        uprv_memset(r, 0, sizeof(UResourceBundle));
-        ures_setIsStackObject(r, isStackObject);
-        r->fHasFallback = TRUE;
-        r->fIsTopLevel = TRUE;
-        r->fIndex = -1;
-        r->fData = entryOpen(path, canonLocaleID, status);
-        if(U_FAILURE(*status)) {
-            return;
-        }
-        /* this is a quick fix to get regular data in bundle - until construction is cleaned up */
-        firstData = r->fData;
-        while(firstData->fBogus != U_ZERO_ERROR && firstData->fParent != NULL) {
-            firstData = firstData->fParent;
+            return NULL;
         }
         }
-        uprv_memcpy(&r->fResData, &firstData->fData, sizeof(ResourceData));
-        r->fHasFallback=(UBool)!r->fResData.noFallback;
-        r->fRes = r->fResData.rootRes;
-        r->fSize = res_countArrayItems(&(r->fResData), r->fRes);
-        r->fTopLevelData = r->fData;
+        entry = entryOpen(path, canonLocaleID, openType, status);
+    } else {
+        entry = entryOpenDirect(path, localeID, status);
     }
     }
-}
-
-U_CAPI UResourceBundle*  U_EXPORT2
-ures_open(const char* path,
-                    const char* localeID,
-                    UErrorCode* status)
-{
-    char canonLocaleID[ULOC_FULLNAME_CAPACITY];
-    UResourceDataEntry *hasData = NULL;
-    UResourceBundle *r;
-
-    if(status == NULL || U_FAILURE(*status)) {
+    if(U_FAILURE(*status)) {
         return NULL;
     }
         return NULL;
     }
-
-    /* first "canonicalize" the locale ID */
-    uloc_getBaseName(localeID, canonLocaleID, sizeof(canonLocaleID), status);
-    if(U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) {
-        *status = U_ILLEGAL_ARGUMENT_ERROR;
+    if(entry == NULL) {
+        *status = U_MISSING_RESOURCE_ERROR;
         return NULL;
     }
 
         return NULL;
     }
 
-    r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
+    UBool isStackObject;
     if(r == NULL) {
     if(r == NULL) {
-        *status = U_MEMORY_ALLOCATION_ERROR;
-        return NULL;
-    }
-
-    uprv_memset(r, 0, sizeof(UResourceBundle));
-    r->fHasFallback = TRUE;
-    r->fIsTopLevel = TRUE;
-    ures_setIsStackObject(r, FALSE);
-    r->fIndex = -1;
-    r->fData = entryOpen(path, canonLocaleID, status);
-    if(U_FAILURE(*status)) {
-        uprv_free(r);
-        return NULL;
-    }
-    r->fTopLevelData = r->fData;
-
-    hasData = r->fData;
-    while(hasData->fBogus != U_ZERO_ERROR) {
-        hasData = hasData->fParent;
-        if(hasData == NULL) {
-          /* This can happen only if fallback chain gets broken by an act of God */
-          /* TODO: this unlikely to happen, consider removing it */
-            entryClose(r->fData);
-            uprv_free(r);
-            *status = U_MISSING_RESOURCE_ERROR;
+        r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
+        if(r == NULL) {
+            entryClose(entry);
+            *status = U_MEMORY_ALLOCATION_ERROR;
             return NULL;
         }
             return NULL;
         }
+        isStackObject = FALSE;
+    } else {  // fill-in
+        isStackObject = ures_isStackObject(r);
+        ures_closeBundle(r, FALSE);
     }
     }
+    uprv_memset(r, 0, sizeof(UResourceBundle));
+    ures_setIsStackObject(r, isStackObject);
 
 
-    uprv_memcpy(&r->fResData, &hasData->fData, sizeof(ResourceData));
-    r->fHasFallback=(UBool)!r->fResData.noFallback;
+    r->fTopLevelData = r->fData = entry;
+    uprv_memcpy(&r->fResData, &entry->fData, sizeof(ResourceData));
+    r->fHasFallback = openType != URES_OPEN_DIRECT && !r->fResData.noFallback;
+    r->fIsTopLevel = TRUE;
     r->fRes = r->fResData.rootRes;
     r->fSize = res_countArrayItems(&(r->fResData), r->fRes);
     r->fRes = r->fResData.rootRes;
     r->fSize = res_countArrayItems(&(r->fResData), r->fRes);
-    /*
-    if(r->fData->fPath != NULL) {
-      ures_setResPath(r, r->fData->fPath);
-      ures_appendResPath(r, RES_PATH_PACKAGE_S);
-      ures_appendResPath(r, r->fData->fName);
-    } else {
-      ures_setResPath(r, r->fData->fName);
-    }
-    */
-
+    r->fIndex = -1;
 
     return r;
 }
 
 
     return r;
 }
 
+U_CAPI UResourceBundle* U_EXPORT2
+ures_open(const char* path, const char* localeID, UErrorCode* status) {
+    return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
+}
+
+U_CAPI UResourceBundle* U_EXPORT2
+ures_openNoDefault(const char* path, const char* localeID, UErrorCode* status) {
+    return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_ROOT, status);
+}
+
 /**
  *  Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed 
  *  or sought. However, alias substitution will happen!
  */
 U_CAPI UResourceBundle*  U_EXPORT2
 ures_openDirect(const char* path, const char* localeID, UErrorCode* status) {
 /**
  *  Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed 
  *  or sought. However, alias substitution will happen!
  */
 U_CAPI UResourceBundle*  U_EXPORT2
 ures_openDirect(const char* path, const char* localeID, UErrorCode* status) {
-    UResourceBundle *r;
-    UErrorCode subStatus = U_ZERO_ERROR;
-
-    if(status == NULL || U_FAILURE(*status)) {
-        return NULL;
-    }
-
-    r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
-    if(r == NULL) {
-        *status = U_MEMORY_ALLOCATION_ERROR;
-        return NULL;
-    }
+    return ures_openWithType(NULL, path, localeID, URES_OPEN_DIRECT, status);
+}
 
 
-    r->fHasFallback = FALSE;
-    r->fIsTopLevel = TRUE;
-    ures_setIsStackObject(r, FALSE);
-    r->fIndex = -1;
-    r->fData = entryOpen(path, localeID, &subStatus);
-    if(U_FAILURE(subStatus)) {
-        *status = subStatus;
-        uprv_free(r);
-        return NULL;
-    }
-    if(subStatus != U_ZERO_ERROR /*r->fData->fBogus != U_ZERO_ERROR*/) {
-      /* we didn't find one we were looking for - so openDirect */
-      /* should fail */
-        entryClose(r->fData);
-        uprv_free(r);
-        *status = U_MISSING_RESOURCE_ERROR;
-        return NULL;
+/**
+ *  API: This function is used to open a resource bundle 
+ *  proper fallback chaining is executed while initialization. 
+ *  The result is stored in cache for later fallback search.
+ */
+U_CAPI void U_EXPORT2
+ures_openFillIn(UResourceBundle *r, const char* path,
+                const char* localeID, UErrorCode* status) {
+    if(U_SUCCESS(*status) && r == NULL) {
+        *status = U_ILLEGAL_ARGUMENT_ERROR;
+        return;
     }
     }
-
-    r->fKey = NULL;
-    r->fVersion = NULL;
-    uprv_memcpy(&r->fResData, &r->fData->fData, sizeof(ResourceData));
-    /* r->fHasFallback remains FALSE here in ures_openDirect() */
-    r->fRes = r->fResData.rootRes;
-    /*r->fParent = RES_BOGUS;*/
-    r->fSize = res_countArrayItems(&(r->fResData), r->fRes);
-    r->fResPath = NULL;
-    r->fResPathLen = 0;
-    /*r->fParentRes = NULL;*/
-    r->fTopLevelData = r->fData;
-
-    return r;
+    ures_openWithType(r, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
 }
 
 /**
 }
 
 /**
@@ -2765,20 +2890,23 @@ ures_getKeywordValues(const char *path, const char *keyword, UErrorCode *status)
             const char *k;
             int32_t i;
             k = ures_getKey(subPtr);
             const char *k;
             int32_t i;
             k = ures_getKey(subPtr);
-            
+
 #if defined(URES_TREE_DEBUG)
             /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
 #endif
 #if defined(URES_TREE_DEBUG)
             /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
 #endif
-            for(i=0;k&&i<valuesCount;i++) {
+            if(k == NULL || *k == 0 ||
+                    uprv_strcmp(k, DEFAULT_TAG) == 0 || uprv_strncmp(k, "private-", 8) == 0) {
+                // empty or "default" or unlisted type
+                continue;
+            }
+            for(i=0; i<valuesCount; i++) {
                 if(!uprv_strcmp(valuesList[i],k)) {
                     k = NULL; /* found duplicate */
                 if(!uprv_strcmp(valuesList[i],k)) {
                     k = NULL; /* found duplicate */
+                    break;
                 }
             }
                 }
             }
-            if(k && *k) {
+            if(k != NULL) {
                 int32_t kLen = (int32_t)uprv_strlen(k);
                 int32_t kLen = (int32_t)uprv_strlen(k);
-                if(!uprv_strcmp(k,DEFAULT_TAG)) {
-                    continue; /* don't need 'default'. */
-                }
                 if((valuesCount >= (VALUES_LIST_SIZE-1)) ||       /* no more space in list .. */
                     ((valuesIndex+kLen+1+1) >= VALUES_BUF_SIZE)) { /* no more space in buffer (string + 2 nulls) */
                     *status = U_ILLEGAL_ARGUMENT_ERROR; /* out of space.. */
                 if((valuesCount >= (VALUES_LIST_SIZE-1)) ||       /* no more space in list .. */
                     ((valuesIndex+kLen+1+1) >= VALUES_BUF_SIZE)) { /* no more space in buffer (string + 2 nulls) */
                     *status = U_ILLEGAL_ARGUMENT_ERROR; /* out of space.. */