/* Static cache for already opened resource bundles - mostly for keeping fallback info TODO: This cache should probably be removed when the deprecated code is completely removed.
*/ static UHashtable *cache = nullptr; static icu::UInitOnce gCacheInitOnce {};
/** * Internal function, gets parts of locale name according * to the position of '_' character
*/ static UBool chopLocale(char *name) { char *i = uprv_strrchr(name, '_');
// This file contains the tables for doing locale fallback, which are generated // by the CLDR-to-ICU process directly from the CLDR data. This file should only // ever be included from here. #define INCLUDED_FROM_URESBUND_CPP #include"localefallback_data.h"
// the default script will be "Latn" if we don't find the locale ID in the tables
CharString result("Latn", err);
// if we were passed both language and region, make them into a locale ID and look that up in the default // script table if (!region.isEmpty()) {
CharString localeID;
localeID.append(language, err).append("_", err).append(region, err); if (U_FAILURE(err)) { return result;
}
defaultScript = performFallbackLookup(localeID.data(), dsLocaleIDChars, scriptCodeChars, defaultScriptTable, UPRV_LENGTHOF(defaultScriptTable));
}
// if we didn't find anything, look up just the language in the default script table if (defaultScript == nullptr) {
defaultScript = performFallbackLookup(language.data(), dsLocaleIDChars, scriptCodeChars, defaultScriptTable, UPRV_LENGTHOF(defaultScriptTable));
}
// if either lookup above succeeded, copy the result from "defaultScript" into "result"; otherwise, return "Latn" if (defaultScript != nullptr) {
result.clear();
result.append(defaultScript, err);
} return result;
}
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
}; typedefenum UResOpenType UResOpenType;
/** * Internal function, determines the search path for resource bundle files. * Currently, this function is used only by findFirstExisting() to help search for resource bundle files when a bundle for the specified * locale doesn't exist. The code that supports inheritance of resources between existing resource bundle files continues to * use chopLocale() below. * @param name In-out parameter: On input, the locale ID to get a parent locale ID for (this is a locale's base name, without keywords); on output, the * requested parent locale ID. * @param origName The original locale ID the caller of findFirstExisting() requested. This is the same as `name` on the first call to this function, * but as findFirstExisting() ascends the resource bundle's parent tree, this parameter will continue to be the original locale ID requested.
*/ staticbool getParentLocaleID(char *name, constchar *origName, UResOpenType openType) { // early out if the locale ID has a variant code or ends with _
size_t nameLen = uprv_strlen(name); if (!nameLen || name[nameLen - 1] == '_' || hasVariant(name)) { return chopLocale(name);
}
if (U_FAILURE(err)) { // hopefully this never happens... return chopLocale(name);
}
// if the open type is URES_OPEN_LOCALE_DEFAULT_ROOT, first look the locale ID up in the parent locale table; // if that table specifies a parent for it, return that (we don't do this for the other open types-- if we're not // falling back through the system default locale, we also want to do straight truncation fallback instead // of looking things up in the parent locale table-- see https://www.unicode.org/reports/tr35/tr35.html#Parent_Locales: // "Collation data, however, is an exception...") if (openType == URES_OPEN_LOCALE_DEFAULT_ROOT) { constchar* parentID = performFallbackLookup(name, parentLocaleChars, parentLocaleChars, parentLocaleTable, UPRV_LENGTHOF(parentLocaleTable)); if (parentID != nullptr) {
uprv_strcpy(name, parentID); returntrue;
}
}
CharString workingLocale;
// if it's not in the parent locale table, figure out the fallback script algorithmically // (see CLDR-15265 for an explanation of the algorithm) if (!script.isEmpty() && !region.isEmpty()) { // if "name" has both script and region, is the script the default script? // - if so, remove it and keep the region // - if not, remove the region and keep the script if (getDefaultScript(language, region) == script) {
workingLocale.append(language, err).append("_", err).append(region, err);
} else {
workingLocale.append(language, err).append("_", err).append(script, err);
}
} elseif (!region.isEmpty()) { // if "name" has region but not script, did the original locale ID specify a script? // - if yes, replace the region with the script from the original locale ID // - if no, replace the region with the default script for that language and region
UErrorCode err = U_ZERO_ERROR;
CharString origNameLanguage;
CharString origNameScript;
ulocimp_getSubtags(origName, &origNameLanguage, &origNameScript, nullptr, nullptr, nullptr, err); if (!origNameScript.isEmpty()) {
workingLocale.append(language, err).append("_", err).append(origNameScript, err);
} else {
workingLocale.append(language, err).append("_", err).append(getDefaultScript(language, region), err);
}
} elseif (!script.isEmpty()) { // if "name" has script but not region (and our open type if URES_OPEN_LOCALE_DEFAULT_ROOT), is the script // the default script for the language? // - if so, remove it from the locale ID // - if not, return false to continue up the chain // (we don't do this for other open types for the same reason we don't look things up in the parent // locale table for other open types-- see the reference to UTS #35 above) if (openType != URES_OPEN_LOCALE_DEFAULT_ROOT || getDefaultScript(language, CharString()) == script) {
workingLocale.append(language, err);
} else { returnfalse;
}
} else { // if "name" just contains a language code, return false so the calling code falls back to "root" returnfalse;
} if (U_SUCCESS(err) && !workingLocale.isEmpty()) {
uprv_strcpy(name, workingLocale.data()); returntrue;
} else { returnfalse;
}
}
/** * Called to check whether a name without '_' needs to be checked for a parent. * Some code had assumed that locale IDs with '_' could not have a non-root parent. * We may want a better way of doing this.
*/ static UBool mayHaveParent(char *name) { return (name[0] != 0 && uprv_strstr("nb nn",name) != nullptr);
}
/** * Internal function. Tries to find a resource in given Resource * Bundle, as well as in its parents
*/ static UResourceDataEntry *getFallbackData( const UResourceBundle *resBundle, constchar **resTag, Resource *res, UErrorCode *status) {
UResourceDataEntry *dataEntry = resBundle->fData;
int32_t indexR = -1;
int32_t i = 0;
*res = RES_BOGUS; if(dataEntry == nullptr) {
*status = U_MISSING_RESOURCE_ERROR; return nullptr;
} if(dataEntry->fBogus == U_ZERO_ERROR) { /* if this resource is real, */
*res = res_getTableItemByKey(&(dataEntry->fData), dataEntry->fData.rootRes, &indexR, resTag); /* try to get data from there */
i++;
} if(resBundle->fHasFallback) { // Otherwise, we'll look in parents. while(*res == RES_BOGUS && dataEntry->fParent != nullptr) {
dataEntry = dataEntry->fParent; if(dataEntry->fBogus == U_ZERO_ERROR) {
i++;
*res = res_getTableItemByKey(&(dataEntry->fData), dataEntry->fData.rootRes, &indexR, resTag);
}
}
}
if(*res == RES_BOGUS) { // If the resource is not found, we need to give an error.
*status = U_MISSING_RESOURCE_ERROR; return nullptr;
} // If the resource is found in parents, we need to adjust the error. if(i>1) { if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) {
*status = U_USING_DEFAULT_WARNING;
} else {
*status = U_USING_FALLBACK_WARNING;
}
} return dataEntry;
}
/* Works just like ucnv_flushCache() */ static int32_t ures_flushCache()
{
UResourceDataEntry *resB;
int32_t pos;
int32_t rbDeletedNum = 0; const UHashElement *e;
UBool deletedMore;
/*if shared data hasn't even been lazy evaluated yet * return 0
*/
Mutex lock(&resbMutex); if (cache == nullptr) { return 0;
}
do {
deletedMore = false; /*creates an enumeration to iterate through every element in the table */
pos = UHASH_FIRST; while ((e = uhash_nextElement(cache, &pos)) != nullptr)
{
resB = static_cast<UResourceDataEntry*>(e->value.pointer); /* Deletes only if reference counter == 0 * Don't worry about the children of this node. * Those will eventually get deleted too, if not already. * Don't worry about the parents of this node. * Those will eventually get deleted too, if not already.
*/ /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */ /* some resource bundles are still open somewhere. */
if (resB->fCountExisting == 0) {
rbDeletedNum++;
deletedMore = true;
uhash_removeElement(cache, e);
free_entry(resB);
}
} /* * Do it again to catch bundles (aliases, pool bundle) whose fCountExisting * got decremented by free_entry().
*/
} while(deletedMore);
/** * INTERNAL: Inits and opens an entry from a data DLL. * CAUTION: resbMutex must be locked when calling this function.
*/ static UResourceDataEntry *init_entry(constchar *localeID, constchar *path, UErrorCode *status) {
UResourceDataEntry *r = nullptr;
UResourceDataEntry find; /*int32_t hashValue;*/ constchar *name; char aliasName[100] = { 0 };
int32_t aliasLen = 0; /*UBool isAlias = false;*/ /*UHashTok hashkey; */
if(U_FAILURE(*status)) { return nullptr;
}
/* here we try to deduce the right locale name */ if(localeID == nullptr) { /* if localeID is nullptr, we're trying to open default locale */
name = uloc_getDefault();
} elseif(*localeID == 0) { /* if localeID is "" then we try to open root locale */
name = kRootLocaleName;
} else { /* otherwise, we'll open what we're given */
name = localeID;
}
/* calculate the hash value of the entry */ /*hashkey.pointer = (void *)&find;*/ /*hashValue = hashEntry(hashkey);*/
/* check to see if we already have this entry */
r = static_cast<UResourceDataEntry*>(uhash_get(cache, &find)); if(r == nullptr) { /* if the entry is not yet in the hash table, we'll try to construct a new one */
r = static_cast<UResourceDataEntry*>(uprv_malloc(sizeof(UResourceDataEntry))); if(r == nullptr) {
*status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
}
/* this is the actual loading */
res_load(&(r->fData), r->fPath, r->fName, status);
if (U_FAILURE(*status)) { /* if we failed to load due to an out-of-memory error, exit early. */ if (*status == U_MEMORY_ALLOCATION_ERROR) {
uprv_free(r); return nullptr;
} /* we have no such entry in dll, so it will always use fallback */
*status = U_USING_FALLBACK_WARNING;
r->fBogus = U_USING_FALLBACK_WARNING;
} else { /* if we have a regular entry */
Resource aliasres; if (r->fData.usesPoolBundle) {
r->fPool = getPoolEntry(r->fPath, status); if (U_SUCCESS(*status)) { 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 = reinterpret_cast<constchar*>(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;
}
} if (U_SUCCESS(*status)) { /* handle the alias by trying to get out the %%Alias tag.*/ /* We'll try to get alias string from the bundle */
aliasres = res_getResource(&(r->fData), "%%ALIAS"); if (aliasres != RES_BOGUS) { // No tracing: called during initial data loading const char16_t *alias = res_getStringNoTrace(&(r->fData), aliasres, &aliasLen); if(alias != nullptr && aliasLen > 0) { /* if there is actual alias - unload and load new data */
u_UCharsToChars(alias, aliasName, aliasLen+1);
r->fAlias = init_entry(aliasName, path, status);
}
}
}
}
{
UResourceDataEntry *oldR = nullptr; if ((oldR = static_cast<UResourceDataEntry*>(uhash_get(cache, r))) == nullptr) { /* if the data is not cached */ /* just insert it in the cache */
UErrorCode cacheStatus = U_ZERO_ERROR;
uhash_put(cache, (void *)r, r, &cacheStatus); if (U_FAILURE(cacheStatus)) {
*status = cacheStatus;
free_entry(r);
r = nullptr;
}
} else { /* somebody have already inserted it while we were working, discard newly opened data */ /* Also, we could get here IF we opened an alias */
free_entry(r);
r = oldR;
}
}
} if(r != nullptr) { /* return the real bundle */ while(r->fAlias != nullptr) {
r = r->fAlias;
}
r->fCountExisting++; /* we increase its reference count */ /* if the resource has a warning */ /* we don't want to overwrite a status with no error */ if(r->fBogus != U_ZERO_ERROR && U_SUCCESS(*status)) {
*status = r->fBogus; /* set the returning status */
}
} return r;
}
/* INTERNAL: */ /* CAUTION: resbMutex must be locked when calling this function! */ static UResourceDataEntry *
findFirstExisting(constchar* path, char* name, constchar* defaultLocale, UResOpenType openType,
UBool *isRoot, UBool *foundParent, UBool *isDefault, UErrorCode* status) {
UResourceDataEntry *r = nullptr;
UBool hasRealData = false;
*foundParent = true; /* we're starting with a fresh name */ char origName[ULOC_FULLNAME_CAPACITY];
uprv_strcpy(origName, name); while(*foundParent && !hasRealData) {
r = init_entry(name, path, status); /* Null pointer test */ if (U_FAILURE(*status)) { return nullptr;
}
*isDefault = static_cast<UBool>(uprv_strncmp(name, defaultLocale, uprv_strlen(name)) == 0);
hasRealData = static_cast<UBool>(r->fBogus == U_ZERO_ERROR); if(!hasRealData) { /* this entry is not real. We will discard it. */ /* However, the parent line for this entry is */ /* not to be used - as there might be parent */ /* lines in cache from previous openings that */ /* are not updated yet. */
r->fCountExisting--; /*entryCloseInt(r);*/
r = nullptr;
*status = U_USING_FALLBACK_WARNING;
} else {
uprv_strcpy(name, r->fName); /* this is needed for supporting aliases */
}
/*Fallback data stuff*/ if (!hasRealData) {
*foundParent = getParentLocaleID(name, origName, openType);
} else { // we've already found a real resource file; what we return to the caller is the parent // locale ID for inheritance, which should come from chopLocale(), not getParentLocaleID()
*foundParent = chopLocale(name);
} if (*foundParent && *name == '\0') {
uprv_strcpy(name, "und");
}
} return r;
}
// Note: We need to query the default locale *before* locking resbMutex. constchar *defaultLocale = uloc_getDefault();
Mutex lock(&resbMutex); // Lock resbMutex until the end of this function.
/* We're going to skip all the locales that do not have any data */
r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus);
// If we failed due to out-of-memory, report the failure and exit early. if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
*status = intStatus; goto finish;
}
if(r != nullptr) { /* if there is one real locale, we can look for parents. */
t1 = r;
hasRealData = true; if ( usingUSRData ) { /* This code inserts user override data into the inheritance chain */
UErrorCode usrStatus = U_ZERO_ERROR;
UResourceDataEntry *u1 = init_entry(t1->fName, usrDataPath, &usrStatus); // If we failed due to out-of-memory, report the failure and exit early. if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
*status = intStatus; goto finish;
} if ( u1 != nullptr ) { if(u1->fBogus == U_ZERO_ERROR) {
u1->fParent = t1;
r = u1;
} else { /* the USR override data wasn't found, set it to be deleted */
u1->fCountExisting = 0;
}
}
} if ((hasChopped || mayHaveParent(name)) && !isRoot) { if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) { goto finish;
}
}
}
/* 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==nullptr && openType == URES_OPEN_LOCALE_DEFAULT_ROOT && !isDefault && !isRoot) { /* insert default locale */
uprv_strcpy(name, defaultLocale);
r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus); // If we failed due to out-of-memory, report the failure and exit early. if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
*status = intStatus; goto finish;
}
intStatus = U_USING_DEFAULT_WARNING; if(r != nullptr) { /* the default locale exists */
t1 = r;
hasRealData = true;
isDefault = true; // TODO: Why not if (usingUSRData) { ... } like in the non-default-locale code path? if ((hasChopped || mayHaveParent(name)) && !isRoot) { if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) { goto finish;
}
}
}
}
/* we could still have r == nullptr at this point - maybe even default locale is not */ /* present */ if(r == nullptr) {
uprv_strcpy(name, kRootLocaleName);
r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus); // If we failed due to out-of-memory, report the failure and exit early. if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
*status = intStatus; goto finish;
} if(r != nullptr) {
t1 = r;
intStatus = U_USING_DEFAULT_WARNING;
hasRealData = true;
} else { /* we don't even have the root locale */
*status = U_MISSING_RESOURCE_ERROR; goto finish;
}
} elseif(!isRoot && uprv_strcmp(t1->fName, kRootLocaleName) != 0 &&
t1->fParent == nullptr && !r->fData.noFallback) { if (!insertRootBundle(t1, status)) { goto finish;
} if(!hasRealData) {
r->fBogus = U_USING_DEFAULT_WARNING;
}
}
/** * 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(constchar* path, constchar* localeID, UErrorCode* status) {
initCache(status); if(U_FAILURE(*status)) { return nullptr;
}
// Note: We need to query the default locale *before* locking resbMutex. // If the localeID is nullptr, then we want to use the default locale. if (localeID == nullptr) {
localeID = uloc_getDefault();
} elseif (*localeID == 0) { // If the localeID is "", then we want to use the root locale.
localeID = kRootLocaleName;
}
Mutex 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 = nullptr;
}
} else {
r = nullptr;
}
// 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 != nullptr && uprv_strcmp(localeID, kRootLocaleName) != 0 && // not root
r->fParent == nullptr && !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, nullptr, status)) { if(uprv_strcmp(t1->fName, kRootLocaleName) != 0 && t1->fParent == nullptr) {
insertRootBundle(t1, status);
}
} if(U_FAILURE(*status)) {
r = nullptr;
}
}
// TODO: Try to refactor further, so that we output a dataEntry + Resource + (optionally) resPath, // rather than a UResourceBundle. // May need to entryIncrease() the resulting dataEntry.
UResourceBundle *getAliasTargetAsResourceBundle( const ResourceData &resData, Resource r, constchar *key, int32_t idx,
UResourceDataEntry *validLocaleDataEntry, constchar *containerResPath,
int32_t recursionDepth,
UResourceBundle *resB, UErrorCode *status) { // TODO: When an error occurs: Should we return nullptr vs. resB? if (U_FAILURE(*status)) { return resB; }
U_ASSERT(RES_GET_TYPE(r) == URES_ALIAS);
int32_t len = 0; const char16_t *alias = res_getAlias(&resData, r, &len); if(len <= 0) { // bad alias
*status = U_ILLEGAL_ARGUMENT_ERROR; return resB;
}
// Copy the UTF-16 alias string into an invariant-character string. // // We do this so that res_findResource() can modify the path, // which allows us to remove redundant _res_findResource() variants // in uresdata.c. // res_findResource() now NUL-terminates each segment so that table keys // can always be compared with strcmp() instead of strncmp(). // Saves code there and simplifies testing and code coverage. // // markus 2003oct17
CharString chAlias;
chAlias.appendInvariantChars(alias, len, *status); if (U_FAILURE(*status)) { return nullptr;
}
// We have an alias, now let's cut it up. constchar *path = nullptr, *locale = nullptr, *keyPath = nullptr; if(chAlias[0] == RES_PATH_SEPARATOR) { // There is a path included. char *chAliasData = chAlias.data(); char *sep = chAliasData + 1;
path = sep;
sep = uprv_strchr(sep, RES_PATH_SEPARATOR); if(sep != nullptr) {
*sep++ = 0;
} if(uprv_strcmp(path, "LOCALE") == 0) { // This is an XPath alias, starting with "/LOCALE/". // It contains the path to a resource which should be looked up // starting in the valid locale. // TODO: Can/should we forbid a /LOCALE alias without key path? // It seems weird to alias to the same path, just starting from the valid locale. // That will often yield an infinite loop.
keyPath = sep; // Read from the valid locale which we already have.
path = locale = nullptr;
} else { if(uprv_strcmp(path, "ICUDATA") == 0) { /* want ICU data */
path = nullptr;
} if (sep == nullptr) { // TODO: This ends up using the root bundle. Can/should we forbid this?
locale = "";
} else {
locale = sep;
sep = uprv_strchr(sep, RES_PATH_SEPARATOR); if(sep != nullptr) {
*sep++ = 0;
}
keyPath = sep;
}
}
} else { // No path, start with a locale. char *sep = chAlias.data();
locale = sep;
sep = uprv_strchr(sep, RES_PATH_SEPARATOR); if(sep != nullptr) {
*sep++ = 0;
}
keyPath = sep;
path = validLocaleDataEntry->fPath;
}
// Got almost everything, let's try to open. // First, open the bundle with real data.
LocalUResourceBundlePointer mainRes;
UResourceDataEntry *dataEntry; if (locale == nullptr) { // alias = /LOCALE/keyPath // Read from the valid locale which we already have.
dataEntry = validLocaleDataEntry;
} else {
UErrorCode intStatus = U_ZERO_ERROR; // TODO: Shouldn't we use ures_open() for locale data bundles (!noFallback)?
mainRes.adoptInstead(ures_openDirect(path, locale, &intStatus)); if(U_FAILURE(intStatus)) { // We failed to open the resource bundle we're aliasing to.
*status = intStatus; return resB;
}
dataEntry = mainRes->fData;
}
constchar* temp = nullptr; if(keyPath == nullptr) { // No key path. This means that we are going to to use the corresponding resource from // another bundle. // TODO: Why the special code path? // Why not put together a key path from containerResPath + key or idx, // as a comment below suggests, and go into the regular code branch? // First, we are going to get a corresponding container // resource to the one we are searching.
r = dataEntry->fData.rootRes; if(containerResPath) {
chAlias.clear().append(containerResPath, *status); if (U_FAILURE(*status)) { return nullptr;
} char *aKey = chAlias.data(); // TODO: should res_findResource() return a new dataEntry, too?
r = res_findResource(&dataEntry->fData, r, &aKey, &temp);
} if(key) { // We need to make keyPath from the containerResPath and // current key, if there is a key associated.
chAlias.clear().append(key, *status); if (U_FAILURE(*status)) { return nullptr;
} char *aKey = chAlias.data();
r = res_findResource(&dataEntry->fData, r, &aKey, &temp);
} elseif(idx != -1) { // If there is no key, but there is an index, try to get by the index. // Here we have either a table or an array, so get the element.
int32_t type = RES_GET_TYPE(r); if(URES_IS_TABLE(type)) { constchar *aKey;
r = res_getTableItemByIndex(&dataEntry->fData, r, idx, &aKey);
} else { /* array */
r = res_getArrayItem(&dataEntry->fData, r, idx);
}
} if(r != RES_BOGUS) {
resB = init_resb_result(
dataEntry, r, temp, -1, validLocaleDataEntry, nullptr, recursionDepth+1,
resB, status);
} else {
*status = U_MISSING_RESOURCE_ERROR;
}
} else { // This one is a bit trickier. // We start finding keys, but after we resolve one alias, the path might continue. // Consider: // aliastest:alias { "testtypes/anotheralias/Sequence" } // anotheralias:alias { "/ICUDATA/sh/CollationElements" } // aliastest resource should finally have the sequence, not collation elements.
CharString pathBuf(keyPath, *status); if (U_FAILURE(*status)) { return nullptr;
} char *myPath = pathBuf.data();
containerResPath = nullptr; // Now we have fallback following here. for(;;) {
r = dataEntry->fData.rootRes; // TODO: Move containerResPath = nullptr to here, // consistent with restarting from the rootRes of another bundle?!
// This loop handles 'found' resources over several levels. while(*myPath && U_SUCCESS(*status)) {
r = res_findResource(&(dataEntry->fData), r, &myPath, &temp); if(r == RES_BOGUS) { // No resource found, we don't really want to look anymore on this level. break;
} // Found a resource, but it might be an indirection.
resB = init_resb_result(
dataEntry, r, temp, -1,
validLocaleDataEntry, containerResPath, recursionDepth+1,
resB, status); if (U_FAILURE(*status)) { break;
} if (temp == nullptr || uprv_strcmp(keyPath, temp) != 0) { // The call to init_resb_result() above will set resB->fKeyPath to be // the same as resB->fKey, // throwing away any additional path elements if we had them -- // if the key path wasn't just a single resource ID, clear out // the bundle's key path and re-set it to be equal to keyPath.
ures_freeResPath(resB);
ures_appendResPath(resB, keyPath, static_cast<int32_t>(uprv_strlen(keyPath)), status); if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
} if (U_FAILURE(*status)) { break;
}
}
r = resB->fRes; /* switch to a new resource, possibly a new tree */
dataEntry = resB->fData;
containerResPath = resB->fResPath;
} if (U_FAILURE(*status) || r != RES_BOGUS) { break;
} // Fall back to the parent bundle, if there is one.
dataEntry = dataEntry->fParent; if (dataEntry == nullptr) {
*status = U_MISSING_RESOURCE_ERROR; break;
} // Copy the same keyPath again.
myPath = pathBuf.data();
uprv_strcpy(myPath, keyPath);
}
} if(mainRes.getAlias() == resB) {
mainRes.orphan();
}
ResourceTracer(resB).maybeTrace("getalias"); return resB;
}
// Recursive function, should be called only by itself, by its simpler wrapper, // or by getAliasTargetAsResourceBundle().
UResourceBundle *init_resb_result(
UResourceDataEntry *dataEntry, Resource r, constchar *key, int32_t idx,
UResourceDataEntry *validLocaleDataEntry, constchar *containerResPath,
int32_t recursionDepth,
UResourceBundle *resB, UErrorCode *status) { // TODO: When an error occurs: Should we return nullptr vs. resB? if(status == nullptr || U_FAILURE(*status)) { return resB;
} if (validLocaleDataEntry == nullptr) {
*status = U_ILLEGAL_ARGUMENT_ERROR; return nullptr;
} if(RES_GET_TYPE(r) == URES_ALIAS) { // This is an alias, need to exchange with real data. if(recursionDepth >= URES_MAX_ALIAS_LEVEL) {
*status = U_TOO_MANY_ALIASES_ERROR; return resB;
} return getAliasTargetAsResourceBundle(
dataEntry->fData, r, key, idx,
validLocaleDataEntry, containerResPath, recursionDepth, resB, status);
} if(resB == nullptr) {
resB = static_cast<UResourceBundle*>(uprv_malloc(sizeof(UResourceBundle))); if (resB == nullptr) {
*status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
}
ures_setIsStackObject(resB, false);
resB->fResPath = nullptr;
resB->fResPathLen = 0;
} else { if(resB->fData != nullptr) {
entryClose(resB->fData);
} if(resB->fVersion != nullptr) {
uprv_free(resB->fVersion);
} /* weiv: if stack object was passed in, it doesn't really need to be reinited, since the purpose of initing is to remove stack junk. However, at this point we would not do anything to an allocated object, so stack object should be treated the same
*/ /* if(ures_isStackObject(resB) != false) { ures_initStackObject(resB); }
*/ if(containerResPath != resB->fResPath) {
ures_freeResPath(resB);
}
}
resB->fData = dataEntry;
entryIncrease(resB->fData);
resB->fHasFallback = false;
resB->fIsTopLevel = false;
resB->fIndex = -1;
resB->fKey = key;
resB->fValidLocaleDataEntry = validLocaleDataEntry; if(containerResPath != resB->fResPath) {
ures_appendResPath(
resB, containerResPath, static_cast<int32_t>(uprv_strlen(containerResPath)), status);
} if(key != nullptr) {
ures_appendResPath(resB, key, static_cast<int32_t>(uprv_strlen(key)), status); if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
}
} elseif(idx >= 0) { char buf[256];
int32_t len = T_CString_integerToString(buf, idx, 10);
ures_appendResPath(resB, buf, len, status); if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
}
} /* Make sure that Purify doesn't complain about uninitialized memory copies. */
{
int32_t usedLen = ((resB->fResBuf == resB->fResPath) ? resB->fResPathLen : 0);
uprv_memset(resB->fResBuf + usedLen, 0, sizeof(resB->fResBuf) - usedLen);
}
if (U_FAILURE(*status)) { return nullptr;
} if (pLength != nullptr) {
capacity = *pLength;
} else {
capacity = 0;
} if (capacity < 0 || (capacity > 0 && dest == nullptr)) {
*status = U_ILLEGAL_ARGUMENT_ERROR; return nullptr;
}
if (length16 == 0) { /* empty string, return as read-only pointer */ if (pLength != nullptr) {
*pLength = 0;
} if (forceCopy) {
u_terminateChars(dest, capacity, 0, status); return dest;
} else { return"";
}
} else { /* We need to transform the string to the destination buffer. */ if (capacity < length16) { /* No chance for the string to fit. Pure preflighting. */ return u_strToUTF8(nullptr, 0, pLength, s16, length16, status);
} if (!forceCopy && (length16 <= 0x2aaaaaaa)) { /* * We know the string will fit into dest because each char16_t turns * into at most three UTF-8 bytes. Fill the latter part of dest * so that callers do not expect to use dest as a string pointer, * hopefully leading to more robust code for when resource bundles * may store UTF-8 natively. * (In which case dest would not be used at all.) * * We do not do this if forceCopy=true because then the caller * expects the string to start exactly at dest. * * The test above for <= 0x2aaaaaaa prevents overflows. * The +1 is for the NUL terminator.
*/
int32_t maxLength = 3 * length16 + 1; if (capacity > maxLength) {
dest += capacity - maxLength;
capacity = maxLength;
}
} return u_strToUTF8(dest, capacity, pLength, s16, length16, status);
}
}
U_CAPI constchar * U_EXPORT2 ures_getKey(const UResourceBundle *resB) { // // TODO: Trace ures_getKey? I guess not usually. // // We usually get the key string to decide whether we want the value, or to // make a key-value pair. Tracing the value should suffice. // // However, I believe we have some data (e.g., in res_index) where the key // strings are the data. Tracing the enclosing table should suffice. // if(resB == nullptr) { return nullptr;
} return(resB->fKey);
}
/* here we do looping and circular alias checking */ /* this loop is here because aliasing is resolved on this level, not on res level */ /* so, when we encounter an alias, it is not an aggregate resource, so we return */ do {
res = res_findResource(&resB->getResData(), resB->fRes, &path, &key); if(res != RES_BOGUS) {
result = init_resb_result(resB->fData, res, key, -1, resB, fillIn, status);
resB = result;
} else {
*status = U_MISSING_RESOURCE_ERROR; break;
}
} while(*path); /* there is more stuff in the path */
/* Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
*/ static Resource getTableItemByKeyPath(const ResourceData *pResData, Resource table, constchar *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 = static_cast<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 != nullptr) {
*nextPathPart = 0; /* Terminating null for this part of path. */
nextPathPart++;
} else {
nextPathPart = uprv_strchr(pathPart, 0);
}
int32_t t; constchar *pathP = pathPart;
resource = res_getTableItemByKey(pResData, resource, &t, &pathP);
type = static_cast<UResType>(RES_GET_TYPE(resource));
pathPart = nextPathPart;
} if (*pathPart) { return RES_BOGUS;
} return resource;
}
staticvoid createPath(constchar* origResPath,
int32_t origResPathLen, constchar* resPath,
int32_t resPathLen, constchar* inKey,
CharString& path,
UErrorCode* status) { // This is a utility function used by ures_getByKeyWithFallback() below. This function builds a path from // resPath and inKey, returning the result in `path`. Originally, this function just cleared `path` and // appended resPath and inKey to it, but that caused problems for horizontal inheritance. // // In normal cases, resPath is the same as origResPath, but if ures_getByKeyWithFallback() has followed an // alias, resPath may be different from origResPath. Not only may the existing path elements be different, // but resPath may also have MORE path elements than origResPath did. If it does, those additional path // elements SUPERSEDE the corresponding elements of inKey. So this code counts the number of elements in // resPath and origResPath and, for each path element in resPath that doesn't have a counterpart in origResPath, // deletes a path element from the beginning of inKey. The remainder of inKey is then appended to // resPath to form the result. (We're not using uprv_strchr() here because resPath and origResPath may // not be zero-terminated.)
path.clear(); constchar* key = inKey; if (resPathLen > 0) {
path.append(resPath, resPathLen, *status); if (U_SUCCESS(*status)) { constchar* resPathLimit = resPath + resPathLen; constchar* origResPathLimit = origResPath + origResPathLen; constchar* resPathPtr = resPath; constchar* origResPathPtr = origResPath;
// Remove from the beginning of resPath the number of segments that are contained in origResPath. // If origResPath has MORE segments than resPath, this will leave resPath as the empty string. while (origResPathPtr < origResPathLimit && resPathPtr < resPathLimit) { while (origResPathPtr < origResPathLimit && *origResPathPtr != RES_PATH_SEPARATOR) {
++origResPathPtr;
} if (origResPathPtr < origResPathLimit && *origResPathPtr == RES_PATH_SEPARATOR) {
++origResPathPtr;
} while (resPathPtr < resPathLimit && *resPathPtr != RES_PATH_SEPARATOR) {
++resPathPtr;
} if (resPathPtr < resPathLimit && *resPathPtr == RES_PATH_SEPARATOR) {
++resPathPtr;
}
}
// New remove from the beginning of `key` the number of segments remaining in resPath. // If resPath has more segments than `key` does, `key` will end up empty. while (resPathPtr < resPathLimit && *key != '\0') { while (resPathPtr < resPathLimit && *resPathPtr != RES_PATH_SEPARATOR) {
++resPathPtr;
} if (resPathPtr < resPathLimit && *resPathPtr == RES_PATH_SEPARATOR) {
++resPathPtr;
} while (*key != '\0' && *key != RES_PATH_SEPARATOR) {
++key;
} if (*key == RES_PATH_SEPARATOR) {
++key;
}
}
} // Finally, append what's left of `key` to `path`. What you end up with here is `resPath`, plus // any pieces of `key` that aren't superseded by `resPath`. // Or, to put it another way, calculate <#-segments-in-key> - (<#-segments-in-resPath> - <#-segments-in-origResPath>), // and append that many segments from the end of `key` to `resPath` to produce the result.
path.append(key, *status);
} else {
path.append(inKey, *status);
}
}
int32_t type = RES_GET_TYPE(resB->fRes); if(URES_IS_TABLE(type)) { constchar* origResPath = resB->fResPath;
int32_t origResPathLen = resB->fResPathLen;
res = getTableItemByKeyPath(&resB->getResData(), resB->fRes, inKey); constchar* key = inKey; bool didRootOnce = false; if(res == RES_BOGUS) {
UResourceDataEntry *dataEntry = resB->fData;
CharString path; char *myPath = nullptr; constchar* resPath = resB->fResPath;
int32_t len = resB->fResPathLen; while(res == RES_BOGUS && (dataEntry->fParent != nullptr || !didRootOnce)) { /* Otherwise, we'll look in parents */ if (dataEntry->fParent != nullptr) {
dataEntry = dataEntry->fParent;
} else { // We can't just stop when we get to a bundle whose fParent is nullptr. That'll work most of the time, // but if the bundle that the caller passed to us was "root" (which happens in getAllItemsWithFallback(), // this function will drop right out without doing anything if "root" doesn't contain the exact key path // specified. In that case, we need one extra time through this loop to make sure we follow any // applicable aliases at the root level.
didRootOnce = true;
}
rootRes = dataEntry->fData.rootRes;
if(dataEntry->fBogus == U_ZERO_ERROR) {
createPath(origResPath, origResPathLen, resPath, len, inKey, path, status); if (U_FAILURE(*status)) {
ures_close(helper); return fillIn;
}
myPath = path.data();
key = inKey; do {
res = res_findResource(&(dataEntry->fData), rootRes, &myPath, &key); if (RES_GET_TYPE(res) == URES_ALIAS && *myPath) { /* We hit an alias, but we didn't finish following the path. */
helper = init_resb_result(dataEntry, res, nullptr, -1, resB, helper, status); /*helper = init_resb_result(dataEntry, res, inKey, -1, resB, helper, status);*/ if(helper) {
dataEntry = helper->fData;
rootRes = helper->fRes;
resPath = helper->fResPath;
len = helper->fResPathLen;
} else { break;
}
} elseif (res == RES_BOGUS) { break;
}
} while(*myPath); /* Continue until the whole path is consumed */
}
} /*dataEntry = getFallbackData(resB, &key, &res, status);*/ if(res != RES_BOGUS) { /* check if resB->fResPath gives the right name here */ if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) {
*status = U_USING_DEFAULT_WARNING;
} else {
*status = U_USING_FALLBACK_WARNING;
}
void getAllItemsWithFallback( const UResourceBundle *bundle, ResourceDataValue &value,
ResourceSink &sink, UErrorCode &errorCode) { if (U_FAILURE(errorCode)) { return; } // We recursively enumerate child-first, // only storing parent items in the absence of child items. // The sink needs to 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 the sink sees the no-fallback/no-inheritance marker, // then it would remove the parent's item. // We would deserialize parent values even though they are overridden in a child bundle.
value.setData(bundle->getResData());
value.setValidLocaleDataEntry(bundle->fValidLocaleDataEntry);
UResourceDataEntry *parentEntry = bundle->fData->fParent;
UBool hasParent = parentEntry != nullptr && U_SUCCESS(parentEntry->fBogus);
value.setResource(bundle->fRes, ResourceTracer(bundle));
sink.put(bundle->fKey, value, !hasParent, errorCode); if (hasParent) { // 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.
StackUResourceBundle parentBundle;
UResourceBundle &parentRef = parentBundle.ref();
parentRef.fData = parentEntry;
parentRef.fValidLocaleDataEntry = bundle->fValidLocaleDataEntry;
parentRef.fHasFallback = !parentRef.getResData().noFallback;
parentRef.fIsTopLevel = true;
parentRef.fRes = parentRef.getResData().rootRes;
parentRef.fSize = res_countArrayItems(&parentRef.getResData(), parentRef.fRes);
parentRef.fIndex = -1;
entryIncrease(parentEntry);
// Look up the container item in the parent bundle.
StackUResourceBundle containerBundle; const UResourceBundle *rb;
UErrorCode pathErrorCode = U_ZERO_ERROR; // Ignore if parents up to root do not have this path. if (bundle->fResPath == nullptr || *bundle->fResPath == 0) {
rb = parentBundle.getAlias();
} else {
rb = ures_getByKeyWithFallback(parentBundle.getAlias(), bundle->fResPath,
containerBundle.getAlias(), &pathErrorCode);
} if (U_SUCCESS(pathErrorCode)) {
getAllItemsWithFallback(rb, value, sink, errorCode);
}
}
}
if (aliasedValue.getType() != URES_TABLE) {
dest.put(key, aliasedValue, isRoot, errorCode);
} else { // if the resource we're aliasing over to is a table, the sink might iterate over its contents. // If it does, it'll get only the things defined in the actual alias target, not the things // the target inherits from its parent resources. So we walk the parent chain for the *alias target*, // calling dest.put() for each of the parent tables we could be inheriting from. This means // that dest.put() has to iterate over the children of multiple tables to get all of the inherited // resource values, but it already has to do that to handle normal vertical inheritance.
UResType aliasedValueType = URES_TABLE;
CharString tablePath;
tablePath.append(aliasRB->fResPath, errorCode); constchar* parentKey = key; // dest.put() changes the key
dest.put(parentKey, aliasedValue, isRoot, errorCode);
UResourceDataEntry* entry = aliasRB->fData;
Resource res = aliasRB->fRes; while (aliasedValueType == URES_TABLE && entry->fParent != nullptr) {
CharString localPath;
localPath.copyFrom(tablePath, errorCode); char* localPathAsCharPtr = localPath.data(); constchar* childKey;
entry = entry->fParent;
res = entry->fData.rootRes;
Resource newRes = res_findResource(&entry->fData, res, &localPathAsCharPtr, &childKey); if (newRes != RES_BOGUS) {
aliasedValue.setData(entry->fData); // TODO: do I also need to call aliasedValue.setValueLocaleDataEntry() ?
aliasedValue.setResource(newRes, ResourceTracer(aliasRB)); // probably wrong to use aliasRB here
aliasedValueType = aliasedValue.getType(); if (aliasedValueType == URES_ALIAS) { // in a few rare cases, when we get to the root resource bundle, the resource in question // won't be an actual table, but will instead be an alias to a table. That is, we have // two aliases in the inheritance path. (For some locales, such as Zulu, we see this with // children of the "fields" resource: "day-narrow" aliases to "day-short", which aliases // to "day".) When this happens, we need to make sure we follow all the aliases.
ResourceDataValue& rdv2 = static_cast<ResourceDataValue&>(aliasedValue);
aliasRB = getAliasTargetAsResourceBundle(rdv2.getData(), rdv2.getResource(), nullptr, -1,
rdv2.getValidLocaleDataEntry(), nullptr, 0,
stackTempBundle.getAlias(), &errorCode);
tablePath.clear();
tablePath.append(aliasRB->fResPath, errorCode);
entry = aliasRB->fData;
res = aliasRB->fRes;
aliasedValue.setData(entry->fData); // TODO: do I also need to call aliasedValue.setValueLocaleDataEntry() ?
aliasedValue.setResource(res, ResourceTracer(aliasRB)); // probably wrong to use aliasRB here
aliasedValueType = aliasedValue.getType();
} if (aliasedValueType == URES_TABLE) {
dest.put(parentKey, aliasedValue, isRoot, errorCode);
} else { // once we've followed the alias, the resource we're looking at really should // be a table
errorCode = U_INTERNAL_PROGRAM_ERROR; return;
}
}
}
}
}
} else {
dest.put(key, value, isRoot, errorCode);
} if (U_FAILURE(errorCode)) { return; }
}
}
};
// Virtual destructors must be defined out of line.
GetAllChildrenSink::~GetAllChildrenSink() {}
// Requires a ResourceDataValue fill-in, so that we need not cast from a ResourceValue. // Unfortunately, the caller must know which subclass to make and pass in. // Alternatively, we could make it as polymorphic as in Java by // returning a ResourceValue pointer (possibly wrapped into a LocalPointer) // that the caller then owns. // // Also requires a UResourceBundle fill-in, so that the value's ResourceTracer // can point to a non-local bundle. // Without tracing, the child bundle could be a function-local object.
U_CAPI void U_EXPORT2
ures_getValueWithFallback(const UResourceBundle *bundle, constchar *path,
UResourceBundle *tempFillIn,
ResourceDataValue &value, UErrorCode &errorCode) { if (U_FAILURE(errorCode)) { return; } if (path == nullptr) {
errorCode = U_ILLEGAL_ARGUMENT_ERROR; return;
} const UResourceBundle *rb; if (*path == 0) { // empty path
rb = bundle;
} else {
rb = ures_getByKeyWithFallback(bundle, path, tempFillIn, &errorCode); if (U_FAILURE(errorCode)) { return;
}
}
value.setData(rb->getResData());
value.setValidLocaleDataEntry(rb->fValidLocaleDataEntry);
value.setResource(rb->fRes, ResourceTracer(rb));
}
int32_t type = RES_GET_TYPE(resB->fRes); if(URES_IS_TABLE(type)) {
int32_t t;
res = res_getTableItemByKey(&resB->getResData(), resB->fRes, &t, &key); if(res == RES_BOGUS) {
key = inKey; if(resB->fHasFallback) {
dataEntry = getFallbackData(resB, &key, &res, status); if(U_SUCCESS(*status)) { /* check if resB->fResPath gives the right name here */ return init_resb_result(dataEntry, res, key, -1, resB, fillIn, status);
} else {
*status = U_MISSING_RESOURCE_ERROR;
}
} else {
*status = U_MISSING_RESOURCE_ERROR;
}
} else { return init_resb_result(resB->fData, res, key, -1, resB, fillIn, status);
}
} #if 0 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */ /* not currently */ elseif(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == true) { /* here should go a first attempt to locate the key using index table */
dataEntry = getFallbackData(resB, &key, &res, status); if(U_SUCCESS(*status)) { return init_resb_result(dataEntry, res, key, resB, fillIn, status);
} else {
*status = U_MISSING_RESOURCE_ERROR;
}
} #endif else {
*status = U_RESOURCE_TYPE_MISMATCH;
} return fillIn;
}
/** * INTERNAL: Get the name of the first real locale (not placeholder) * that has resource bundle data.
*/
U_CAPI constchar* U_EXPORT2
ures_getLocaleInternal(const UResourceBundle* resourceBundle, UErrorCode* status)
{ if (status==nullptr || U_FAILURE(*status)) { return nullptr;
} if (!resourceBundle) {
*status = U_ILLEGAL_ARGUMENT_ERROR; return nullptr;
} else { return resourceBundle->fData->fName;
}
}
/** * 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(constchar* path, constchar* localeID, UErrorCode* status) { return ures_openWithType(nullptr, path, localeID, URES_OPEN_DIRECT, status);
}
/** * Internal 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. * * Same as ures_open(), but uses the fill-in parameter and does not allocate a new bundle.
*/
U_CAPI void U_EXPORT2
ures_openFillIn(UResourceBundle *r, constchar* path, constchar* localeID, UErrorCode* status) { if(U_SUCCESS(*status) && r == nullptr) {
*status = U_ILLEGAL_ARGUMENT_ERROR; return;
}
ures_openWithType(r, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
}
/** * Same as ures_openDirect(), but uses the fill-in parameter and does not allocate a new bundle.
*/
U_CAPI void U_EXPORT2
ures_openDirectFillIn(UResourceBundle *r, constchar* path, constchar* localeID, UErrorCode* status) { if(U_SUCCESS(*status) && r == nullptr) {
*status = U_ILLEGAL_ARGUMENT_ERROR; return;
}
ures_openWithType(r, path, localeID, URES_OPEN_DIRECT, status);
}
/** * API: Counts members. For arrays and tables, returns number of resources. * For strings, returns 1.
*/
U_CAPI int32_t U_EXPORT2
ures_countArrayItems(const UResourceBundle* resourceBundle, constchar* resourceKey,
UErrorCode* status)
{
UResourceBundle resData;
ures_initStackObject(&resData); if (status==nullptr || U_FAILURE(*status)) { return 0;
} if(resourceBundle == nullptr) {
*status = U_ILLEGAL_ARGUMENT_ERROR; return 0;
}
ures_getByKey(resourceBundle, resourceKey, &resData, status);
/** * Internal function. * Return the version number associated with this ResourceBundle as a string. * * @param resourceBundle The resource bundle for which the version is checked. * @return A version number string as specified in the resource bundle or its parent. * The caller does not own this string. * @see ures_getVersion * @internal
*/
U_CAPI constchar* U_EXPORT2
ures_getVersionNumberInternal(const UResourceBundle *resourceBundle)
{ if (!resourceBundle) return nullptr;
if(resourceBundle->fVersion == nullptr) {
/* If the version ID has not been built yet, then do so. Retrieve */ /* the minor version from the file. */
UErrorCode status = U_ZERO_ERROR;
int32_t minor_len = 0;
int32_t len;
/* Determine the length of of the final version string. This is */ /* the length of the major part + the length of the separator */ /* (==1) + the length of the minor part (+ 1 for the zero byte at */ /* the end). */
len = (minor_len > 0) ? minor_len : 1;
/* Allocate the string, and build it up. */ /* + 1 for zero byte */
staticvoid getParentForFunctionalEquivalent(constchar* localeID,
UResourceBundle* res,
UResourceBundle* bund1,
CharString& parent) { // Get parent. // First check for a parent from %%Parent resource (Note that in resource trees // such as collation, data may have different parents than in parentLocales).
UErrorCode subStatus = U_ZERO_ERROR;
parent.clear(); if (res != nullptr) {
ures_getByKey(res, "%%Parent", bund1, &subStatus); if (U_SUCCESS(subStatus)) {
int32_t length16; const char16_t* s16 = ures_getString(bund1, &length16, &subStatus);
parent.appendInvariantChars(s16, length16, subStatus);
}
}
// If none there, use normal truncation parent if (U_FAILURE(subStatus) || parent.isEmpty()) {
subStatus = U_ZERO_ERROR;
parent = ulocimp_getParent(localeID, subStatus);
}
}
do {
subStatus = U_ZERO_ERROR;
res = ures_open(path, parent.data(), &subStatus); if(((subStatus == U_USING_FALLBACK_WARNING) ||
(subStatus == U_USING_DEFAULT_WARNING)) && isAvailable)
{
*isAvailable = false;
}
isAvailable = nullptr; /* only want to set this the first time around */
/* Now, see if we can find the kwVal collator.. start the search over.. */
parent.copyFrom(base, subStatus);
found.copyFrom(base, subStatus);
do {
res = ures_open(path, parent.data(), &subStatus); if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
*isAvailable = false;
}
isAvailable = nullptr; /* only want to set this the first time around */
subStatus = U_ZERO_ERROR;
UBool haveFound = false; // At least for collations which may be aliased, we need to use the VALID locale // as the parent instead of just truncating, as long as the VALID locale is not // root and has a different language than the parent. Use of the VALID locale // here is similar to the procedure used at the end of the previous do-while loop // for all resource types. if (res != nullptr && uprv_strcmp(resName, "collations") == 0) { constchar *validLoc = ures_getLocaleByType(res, ULOC_VALID_LOCALE, &subStatus); if (U_SUCCESS(subStatus) && validLoc != nullptr && validLoc[0] != 0 && uprv_strcmp(validLoc, "root") != 0) {
CharString validLang = ulocimp_getLanguage(validLoc, subStatus);
CharString parentLang = ulocimp_getLanguage(parent.data(), subStatus); if (U_SUCCESS(subStatus) && validLang != parentLang) { // validLoc is not root and has a different language than parent, use it instead
found.clear().append(validLoc, subStatus);
haveFound = true;
}
}
subStatus = U_ZERO_ERROR;
} if (!haveFound) {
found.copyFrom(parent, subStatus);
}
do { /* search for 'default' named item */
res = ures_open(path, parent.data(), &subStatus); if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
*isAvailable = false;
}
isAvailable = nullptr; /* only want to set this the first time around */
if(U_SUCCESS(*status)) { if(full.isEmpty()) { #ifdefined(URES_TREE_DEBUG)
fprintf(stderr, "Still could not load keyword %s=%s\n", keyword, kwVal.data()); #endif
*status = U_MISSING_RESOURCE_ERROR;
} elseif(omitDefault) { #ifdefined(URES_TREE_DEBUG)
fprintf(stderr,"Trim? full=%s, defLoc=%s, found=%s\n", full.data(), defLoc.data(), found.data()); #endif if(defLoc.length() <= full.length()) { /* found the keyword in a *child* of where the default tag was present. */ if(kwVal == defVal) { /* if the requested kw is default, */ /* and the default is in or in an ancestor of the current locale */ #ifdefined(URES_TREE_DEBUG)
fprintf(stderr, "Removing unneeded var %s=%s\n", keyword, kwVal.data()); #endif
kwVal.clear();
}
}
}
found.copyFrom(full, subStatus); if(!kwVal.isEmpty()) {
found
.append("@", subStatus)
.append(keyword, subStatus)
.append("=", subStatus)
.append(kwVal, subStatus);
} elseif(!omitDefault) {
found
.append("@", subStatus)
.append(keyword, subStatus)
.append("=", subStatus)
.append(defVal, subStatus);
}
} /* we found the default locale - no need to repeat it.*/
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.