/* 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);
}
if(resB->fIndex == resB->fSize-1) {
*status = U_INDEX_OUTOFBOUNDS_ERROR; /*return nullptr;*/
} else {
resB->fIndex++; switch(RES_GET_TYPE(resB->fRes)) { case URES_INT: case URES_BINARY: case URES_STRING: case URES_STRING_V2: case URES_INT_VECTOR: return ures_copyResb(fillIn, resB, status); case URES_TABLE: case URES_TABLE16: case URES_TABLE32:
r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, resB->fIndex, &key); if(r == RES_BOGUS && resB->fHasFallback) { /* TODO: do the fallback */
} return init_resb_result(resB->fData, r, key, resB->fIndex, resB, fillIn, status); case URES_ARRAY: case URES_ARRAY16:
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ 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.0.29Bemerkung:
Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können
¤
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.