X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/4388f060552cc537e71e957d32f35e9d75a61233..ef6cf650f4a75c3f97de06b51fa104f2069b9ea2:/icuSources/common/uresbund.cpp diff --git a/icuSources/common/uresbund.cpp b/icuSources/common/uresbund.cpp index 107426de..62460f8e 100644 --- a/icuSources/common/uresbund.cpp +++ b/icuSources/common/uresbund.cpp @@ -1,10 +1,10 @@ /* ****************************************************************************** -* Copyright (C) 1997-2012, International Business Machines Corporation and * -* others. All Rights Reserved. * +* Copyright (C) 1997-2016, International Business Machines Corporation and +* others. All Rights Reserved. ****************************************************************************** * -* File URESBUND.C +* File uresbund.cpp * * Modification History: * @@ -21,6 +21,7 @@ #include "unicode/ustring.h" #include "unicode/ucnv.h" +#include "charstr.h" #include "uresimp.h" #include "ustr_imp.h" #include "cwchar.h" @@ -33,7 +34,9 @@ #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 @@ -41,8 +44,9 @@ TODO: This cache should probably be removed when the deprecated code is completely removed. */ static UHashtable *cache = NULL; +static icu::UInitOnce gCacheInitOnce; -static UMTX resbMutex = NULL; +static UMutex resbMutex = U_MUTEX_INITIALIZER; /* INTERNAL: hashes an entry */ static int32_t U_CALLCONV hashEntry(const UHashTok parm) { @@ -50,7 +54,7 @@ static int32_t U_CALLCONV hashEntry(const UHashTok parm) { 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 */ @@ -183,7 +187,7 @@ static int32_t ures_flushCache() 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; @@ -218,7 +222,7 @@ static int32_t ures_flushCache() 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; @@ -255,37 +259,22 @@ static UBool U_CALLCONV ures_cleanup(void) { if (cache != NULL) { ures_flushCache(); - if (cache != NULL && uhash_count(cache) == 0) { - uhash_close(cache); - cache = NULL; - } - } - if (cache == NULL && resbMutex != NULL) { - umtx_destroy(&resbMutex); + uhash_close(cache); + cache = NULL; } - return (cache == NULL); + gCacheInitOnce.reset(); + return TRUE; } /** 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) { - 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 */ @@ -388,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)); + r->fData.poolBundleStrings = r->fPool->fData.p16BitUnits; } else { r->fBogus = *status = U_INVALID_FORMAT_ERROR; } @@ -457,7 +447,9 @@ getPoolEntry(const char *path, UErrorCode *status) { /* 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(); @@ -513,15 +505,106 @@ U_CFUNC void ures_initStackObject(UResourceBundle* resB) { 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 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 *t2 = NULL; - UResourceDataEntry *u1 = NULL; - UResourceDataEntry *u2 = NULL; UBool isDefault = FALSE; UBool isRoot = FALSE; UBool hasRealData = FALSE; @@ -561,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 */ - 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; @@ -572,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; } - - 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 */ - 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); @@ -622,31 +674,11 @@ static UResourceDataEntry *entryOpen(const char* path, const char* localeID, UEr 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; } - - if ( res_getResource(&t1->fData,"%%ParentIsRoot") == RES_BOGUS) { - t1->fParent = t2; - t1 = t2; - } - hasChopped = chopLocale(name); } } } @@ -664,46 +696,89 @@ static UResourceDataEntry *entryOpen(const char* path, const char* localeID, UEr *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; } - 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; - hasRealData = (UBool)((t1->fBogus == U_ZERO_ERROR) || hasRealData); } } /* 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; } } +/** + * 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. @@ -1410,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; - default: /*fall through*/ + U_FALLTHROUGH; + default: return NULL; } } @@ -1673,16 +1749,54 @@ ures_getStringByKeyWithFallback(const UResourceBundle *resB, 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); - 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; - *len = 0; + length = 0; *status = U_MISSING_RESOURCE_ERROR; } + if (len != NULL) { + *len = length; + } return retVal; } +/* + Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort". +*/ +static Resource getTableItemByKeyPath(const ResourceData *pResData, Resource table, const char *key) { + Resource resource = table; /* The current resource */ + icu::CharString path; + UErrorCode errorCode = U_ZERO_ERROR; + path.append(key, errorCode); + if (U_FAILURE(errorCode)) { return RES_BOGUS; } + char *pathPart = path.data(); /* Path from current resource to desired resource */ + UResType type = (UResType)RES_GET_TYPE(resource); /* the current resource type */ + while (*pathPart && resource != RES_BOGUS && URES_IS_CONTAINER(type)) { + char *nextPathPart = uprv_strchr(pathPart, RES_PATH_SEPARATOR); + if (nextPathPart != NULL) { + *nextPathPart = 0; /* Terminating null for this part of path. */ + nextPathPart++; + } else { + nextPathPart = uprv_strchr(pathPart, 0); + } + int32_t t; + const char *pathP = pathPart; + resource = res_getTableItemByKey(pResData, resource, &t, &pathP); + type = (UResType)RES_GET_TYPE(resource); + pathPart = nextPathPart; + } + if (*pathPart) { + return RES_BOGUS; + } + return resource; +} + U_CAPI UResourceBundle* U_EXPORT2 ures_getByKeyWithFallback(const UResourceBundle *resB, const char* inKey, @@ -1690,7 +1804,6 @@ ures_getByKeyWithFallback(const UResourceBundle *resB, UErrorCode *status) { Resource res = RES_BOGUS, rootRes = RES_BOGUS; /*UResourceDataEntry *realData = NULL;*/ - const char *key = inKey; UResourceBundle *helper = NULL; if (status==NULL || U_FAILURE(*status)) { @@ -1703,23 +1816,29 @@ ures_getByKeyWithFallback(const UResourceBundle *resB, int32_t type = RES_GET_TYPE(resB->fRes); if(URES_IS_TABLE(type)) { - int32_t t; - res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key); + res = getTableItemByKeyPath(&(resB->fResData), resB->fRes, inKey); + 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 */ dataEntry = dataEntry->fParent; rootRes = dataEntry->fData.rootRes; if(dataEntry->fBogus == U_ZERO_ERROR) { - uprv_strncpy(path, resPath, len); - uprv_strcpy(path+len, inKey); - myPath = path; + path.clear(); + if (len > 0) { + path.append(resPath, len, *status); + } + path.append(inKey, *status); + if (U_FAILURE(*status)) { + ures_close(helper); + return fillIn; + } + myPath = path.data(); key = inKey; do { res = res_findResource(&(dataEntry->fData), rootRes, &myPath, &key); @@ -1764,6 +1883,121 @@ ures_getByKeyWithFallback(const UResourceBundle *resB, 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; @@ -1956,7 +2190,6 @@ ures_getLocaleByType(const UResourceBundle* resourceBundle, case ULOC_VALID_LOCALE: return resourceBundle->fTopLevelData->fName; case ULOC_REQUESTED_LOCALE: - return NULL; default: *status = U_ILLEGAL_ARGUMENT_ERROR; return NULL; @@ -1982,172 +2215,93 @@ U_CFUNC const char* ures_getPath(const UResourceBundle* resB) { } #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]; - - 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; - 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; } - - /* 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; } - r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle)); + UBool isStackObject; 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; } + 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); - /* - 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; } +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) { - 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); } /** @@ -2324,7 +2478,7 @@ ures_openAvailableLocales(const char *path, UErrorCode *status) if(U_FAILURE(*status)) { return NULL; } - myContext = reinterpret_cast(uprv_malloc(sizeof(ULocalesContext))); + myContext = static_cast(uprv_malloc(sizeof(ULocalesContext))); en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration)); if(!en || !myContext) { *status = U_MEMORY_ALLOCATION_ERROR; @@ -2736,20 +2890,23 @@ ures_getKeywordValues(const char *path, const char *keyword, UErrorCode *status) const char *k; int32_t i; k = ures_getKey(subPtr); - + #if defined(URES_TREE_DEBUG) /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"", keyword, locale, k); */ #endif - for(i=0;k&&i= (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.. */