U_CDECL_END U_NAMESPACE_BEGIN /* Reduce excessive reallocation, and make it easier to detect initialization problems. Usually you don't see smaller sets than this for Unicode 5.0. */ #define DEFAULT_INCLUSION_CAPACITY 3072 const UnicodeSet* UnicodeSet::getInclusions(int32_t src, UErrorCode &status) { UBool needInit; UMTX_CHECK(NULL, (INCLUSIONS[src] == NULL), needInit); if (needInit) { UnicodeSet* incl = new UnicodeSet(); USetAdder sa = { (USet *)incl, _set_add, _set_addRange, _set_addString, NULL, // don't need remove() NULL // don't need removeRange() }; incl->ensureCapacity(DEFAULT_INCLUSION_CAPACITY, status); if (incl != NULL) { switch(src) { case UPROPS_SRC_CHAR: uchar_addPropertyStarts(&sa, &status); break; case UPROPS_SRC_PROPSVEC: upropsvec_addPropertyStarts(&sa, &status); break; case UPROPS_SRC_CHAR_AND_PROPSVEC: uchar_addPropertyStarts(&sa, &status); upropsvec_addPropertyStarts(&sa, &status); break; case UPROPS_SRC_HST: uhst_addPropertyStarts(&sa, &status); break; #if !UCONFIG_NO_NORMALIZATION case UPROPS_SRC_NORM: unorm_addPropertyStarts(&sa, &status); break; #endif case UPROPS_SRC_CASE: ucase_addPropertyStarts(ucase_getSingleton(&status), &sa, &status); break; case UPROPS_SRC_BIDI: ubidi_addPropertyStarts(ubidi_getSingleton(&status), &sa, &status); break; default: status = U_INTERNAL_PROGRAM_ERROR; break; } if (U_SUCCESS(status)) { // Compact for caching incl->compact(); umtx_lock(NULL); if (INCLUSIONS[src] == NULL) { INCLUSIONS[src] = incl; incl = NULL; ucln_common_registerCleanup(UCLN_COMMON_USET, uset_cleanup); } umtx_unlock(NULL); } delete incl; } else { status = U_MEMORY_ALLOCATION_ERROR; } } return INCLUSIONS[src]; }
NumberFormat* NumberFormat::makeInstance(const Locale& desiredLocale, EStyles style, UErrorCode& status) { if (U_FAILURE(status)) return NULL; if (style < 0 || style >= kStyleCount) { status = U_ILLEGAL_ARGUMENT_ERROR; return NULL; } #ifdef U_WINDOWS char buffer[8]; int32_t count = desiredLocale.getKeywordValue("compat", buffer, sizeof(buffer), status); // if the locale has "@compat=host", create a host-specific NumberFormat if (count > 0 && uprv_strcmp(buffer, "host") == 0) { Win32NumberFormat *f = NULL; UBool curr = TRUE; switch (style) { case kNumberStyle: curr = FALSE; // fall-through case kCurrencyStyle: case kIsoCurrencyStyle: // do not support plural formatting here case kPluralCurrencyStyle: f = new Win32NumberFormat(desiredLocale, curr, status); if (U_SUCCESS(status)) { return f; } delete f; break; default: break; } } #endif NumberFormat* f = NULL; DecimalFormatSymbols* symbolsToAdopt = NULL; UnicodeString pattern; UResourceBundle *resource = ures_open((char *)0, desiredLocale.getName(), &status); UResourceBundle *numberPatterns = ures_getByKey(resource, DecimalFormat::fgNumberPatterns, NULL, &status); NumberingSystem *ns = NULL; UBool deleteSymbols = TRUE; UHashtable * cache = NULL; int32_t hashKey; UBool getCache = FALSE; UBool deleteNS = FALSE; if (U_FAILURE(status)) { // We don't appear to have resource data available -- use the last-resort data status = U_USING_FALLBACK_WARNING; // When the data is unavailable, and locale isn't passed in, last resort data is used. symbolsToAdopt = new DecimalFormatSymbols(status); // Creates a DecimalFormat instance with the last resort number patterns. pattern.setTo(TRUE, gLastResortNumberPatterns[style], -1); } else { // If not all the styled patterns exists for the NumberFormat in this locale, // sets the status code to failure and returns nil. if (ures_getSize(numberPatterns) < (int32_t)(sizeof(gLastResortNumberPatterns)/sizeof(gLastResortNumberPatterns[0])) -2 ) { //minus 2: ISO and plural status = U_INVALID_FORMAT_ERROR; goto cleanup; } // Loads the decimal symbols of the desired locale. symbolsToAdopt = new DecimalFormatSymbols(desiredLocale, status); int32_t patLen = 0; /* for ISOCURRENCYSTYLE and PLURALCURRENCYSTYLE, * the pattern is the same as the pattern of CURRENCYSTYLE * but by replacing the single currency sign with * double currency sign or triple currency sign. */ int styleInNumberPattern = ((style == kIsoCurrencyStyle || style == kPluralCurrencyStyle) ? kCurrencyStyle : style); const UChar *patResStr = ures_getStringByIndex(numberPatterns, (int32_t)styleInNumberPattern, &patLen, &status); // Creates the specified decimal format style of the desired locale. pattern.setTo(TRUE, patResStr, patLen); } if (U_FAILURE(status) || symbolsToAdopt == NULL) { goto cleanup; } if(style==kCurrencyStyle || style == kIsoCurrencyStyle){ const UChar* currPattern = symbolsToAdopt->getCurrencyPattern(); if(currPattern!=NULL){ pattern.setTo(currPattern, u_strlen(currPattern)); } } // Use numbering system cache hashtable UMTX_CHECK(&nscacheMutex, (UBool)(cache != NumberingSystem_cache), getCache); if (getCache) { umtx_lock(&nscacheMutex); cache = NumberingSystem_cache; umtx_unlock(&nscacheMutex); } // Check cache we got, create if non-existant status = U_ZERO_ERROR; if (cache == NULL) { cache = uhash_open(uhash_hashLong, uhash_compareLong, NULL, &status); if (cache == NULL || U_FAILURE(status)) { // cache not created - out of memory cache = NULL; } else { // cache created uhash_setValueDeleter(cache, deleteNumberingSystem); // set final NumberingSystem_cache value UHashtable* h = NULL; UMTX_CHECK(&nscacheMutex, (UBool)(h != NumberingSystem_cache), getCache); if (getCache) { umtx_lock(&nscacheMutex); h = NumberingSystem_cache; umtx_unlock(&nscacheMutex); } if (h == NULL) { umtx_lock(&nscacheMutex); NumberingSystem_cache = h = cache; cache = NULL; ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); umtx_unlock(&nscacheMutex); } if(cache != NULL) { uhash_close(cache); } cache = h; } } // Get cached numbering system if (cache != NULL) { hashKey = desiredLocale.hashCode(); umtx_lock(&nscacheMutex); ns = (NumberingSystem *)uhash_iget(cache, hashKey); if (ns == NULL) { ns = NumberingSystem::createInstance(desiredLocale,status); uhash_iput(cache, hashKey, (void*)ns, &status); } umtx_unlock(&nscacheMutex); } else { ns = NumberingSystem::createInstance(desiredLocale,status); deleteNS = TRUE; } // check results of getting a numbering system if ((ns == NULL) || (U_FAILURE(status))) { goto cleanup; } if (ns->isAlgorithmic()) { UnicodeString nsDesc; UnicodeString nsRuleSetGroup; UnicodeString nsRuleSetName; Locale nsLoc; URBNFRuleSetTag desiredRulesType = URBNF_NUMBERING_SYSTEM; nsDesc.setTo(ns->getDescription()); int32_t firstSlash = nsDesc.indexOf(gSlash); int32_t lastSlash = nsDesc.lastIndexOf(gSlash); if ( lastSlash > firstSlash ) { char nsLocID[ULOC_FULLNAME_CAPACITY]; nsDesc.extract(0,firstSlash,nsLocID,ULOC_FULLNAME_CAPACITY,US_INV); nsRuleSetGroup.setTo(nsDesc,firstSlash+1,lastSlash-firstSlash-1); nsRuleSetName.setTo(nsDesc,lastSlash+1); nsLoc = Locale::createFromName(nsLocID); UnicodeString SpelloutRules = UNICODE_STRING_SIMPLE("SpelloutRules"); if ( nsRuleSetGroup.compare(SpelloutRules) == 0 ) { desiredRulesType = URBNF_SPELLOUT; } } else { nsLoc = desiredLocale; nsRuleSetName.setTo(nsDesc); } RuleBasedNumberFormat *r = new RuleBasedNumberFormat(desiredRulesType,nsLoc,status); if (U_FAILURE(status) || r == NULL) { goto cleanup; } r->setDefaultRuleSet(nsRuleSetName,status); f = (NumberFormat *) r; } else { // replace single currency sign in the pattern with double currency sign // if the style is kIsoCurrencyStyle if (style == kIsoCurrencyStyle) { pattern.findAndReplace(gSingleCurrencySign, gDoubleCurrencySign); } f = new DecimalFormat(pattern, symbolsToAdopt, style, status); if (U_FAILURE(status) || f == NULL) { goto cleanup; } deleteSymbols = FALSE; } f->setLocaleIDs(ures_getLocaleByType(numberPatterns, ULOC_VALID_LOCALE, &status), ures_getLocaleByType(numberPatterns, ULOC_ACTUAL_LOCALE, &status)); cleanup: ures_close(numberPatterns); ures_close(resource); if (deleteNS && ns) { delete ns; } if (U_FAILURE(status)) { /* If f exists, then it will delete the symbols */ if (f==NULL) { delete symbolsToAdopt; } else { delete f; } return NULL; } if (f == NULL || symbolsToAdopt == NULL) { status = U_MEMORY_ALLOCATION_ERROR; f = NULL; } if (deleteSymbols && symbolsToAdopt != NULL) { delete symbolsToAdopt; } return f; }
/** * Initialize DEFAULT_ZONE from the system default time zone. The * caller should confirm that DEFAULT_ZONE is NULL before calling. * Upon return, DEFAULT_ZONE will not be NULL, unless operator new() * returns NULL. * * Must be called OUTSIDE mutex. */ void TimeZone::initDefault() { // We access system timezone data through TPlatformUtilities, // including tzset(), timezone, and tzname[]. int32_t rawOffset = 0; const char *hostID; // First, try to create a system timezone, based // on the string ID in tzname[0]. { // NOTE: Local mutex here. TimeZone mutex below // mutexed to avoid threading issues in the platform functions. // Some of the locale/timezone OS functions may not be thread safe, // so the intent is that any setting from anywhere within ICU // happens while the ICU mutex is held. // The operating system might actually use ICU to implement timezones. // So we may have ICU calling ICU here, like on AIX. // In order to prevent a double lock of a non-reentrant mutex in a // different part of ICU, we use TZSET_LOCK to allow only one instance // of ICU to query these thread unsafe OS functions at any given time. Mutex lock(&TZSET_LOCK); ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); uprv_tzset(); // Initialize tz... system data // Get the timezone ID from the host. This function should do // any required host-specific remapping; e.g., on Windows this // function maps the Date and Time control panel setting to an // ICU timezone ID. hostID = uprv_tzname(0); // Invert sign because UNIX semantics are backwards rawOffset = uprv_timezone() * -U_MILLIS_PER_SECOND; } UBool initialized; UMTX_CHECK(&LOCK, (DEFAULT_ZONE != NULL), initialized); if (initialized) { /* Hrmph? Either a race condition happened, or tzset initialized ICU. */ return; } TimeZone* default_zone = NULL; /* Make sure that the string is NULL terminated to prevent BoundsChecker/Purify warnings. */ UnicodeString hostStrID(hostID, -1, US_INV); hostStrID.append((UChar)0); hostStrID.truncate(hostStrID.length()-1); default_zone = createSystemTimeZone(hostStrID); #ifdef U_WINDOWS // hostID points to a heap-allocated location on Windows. uprv_free(const_cast<char *>(hostID)); #endif int32_t hostIDLen = hostStrID.length(); if (default_zone != NULL && rawOffset != default_zone->getRawOffset() && (3 <= hostIDLen && hostIDLen <= 4)) { // Uh oh. This probably wasn't a good id. // It was probably an ambiguous abbreviation delete default_zone; default_zone = NULL; } // Construct a fixed standard zone with the host's ID // and raw offset. if (default_zone == NULL) { default_zone = new SimpleTimeZone(rawOffset, hostStrID); } // If we _still_ don't have a time zone, use GMT. if (default_zone == NULL) { const TimeZone* temptz = getGMT(); // If we can't use GMT, get out. if (temptz == NULL) { return; } default_zone = temptz->clone(); } // If DEFAULT_ZONE is still NULL, set it up. umtx_lock(&LOCK); if (DEFAULT_ZONE == NULL) { DEFAULT_ZONE = default_zone; default_zone = NULL; ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); } umtx_unlock(&LOCK); delete default_zone; }
TimeZoneGenericNames* TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) { if (U_FAILURE(status)) { return NULL; } TimeZoneGenericNames* instance = new TimeZoneGenericNames(); if (instance == NULL) { status = U_MEMORY_ALLOCATION_ERROR; return NULL; } UBool initialized; UMTX_CHECK(&gTZGNLock, gTZGNCoreCacheInitialized, initialized); if (!initialized) { // Create empty hashtable umtx_lock(&gTZGNLock); { if (!gTZGNCoreCacheInitialized) { gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); if (U_SUCCESS(status)) { uhash_setKeyDeleter(gTZGNCoreCache, uprv_free); uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef); gTZGNCoreCacheInitialized = TRUE; ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup); } } } umtx_unlock(&gTZGNLock); if (U_FAILURE(status)) { return NULL; } } // Check the cache, if not available, create new one and cache TZGNCoreRef *cacheEntry = NULL; umtx_lock(&gTZGNLock); { const char *key = locale.getName(); cacheEntry = (TZGNCoreRef *)uhash_get(gTZGNCoreCache, key); if (cacheEntry == NULL) { TZGNCore *tzgnCore = NULL; char *newKey = NULL; tzgnCore = new TZGNCore(locale, status); if (tzgnCore == NULL) { status = U_MEMORY_ALLOCATION_ERROR; } if (U_SUCCESS(status)) { newKey = (char *)uprv_malloc(uprv_strlen(key) + 1); if (newKey == NULL) { status = U_MEMORY_ALLOCATION_ERROR; } else { uprv_strcpy(newKey, key); } } if (U_SUCCESS(status)) { cacheEntry = (TZGNCoreRef *)uprv_malloc(sizeof(TZGNCoreRef)); if (cacheEntry == NULL) { status = U_MEMORY_ALLOCATION_ERROR; } else { cacheEntry->obj = tzgnCore; cacheEntry->refCount = 1; cacheEntry->lastAccess = (double)uprv_getUTCtime(); uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status); } } if (U_FAILURE(status)) { if (tzgnCore != NULL) { delete tzgnCore; } if (newKey != NULL) { uprv_free(newKey); } if (cacheEntry != NULL) { uprv_free(cacheEntry); } cacheEntry = NULL; } } else { // Update the reference count cacheEntry->refCount++; cacheEntry->lastAccess = (double)uprv_getUTCtime(); } gAccessCount++; if (gAccessCount >= SWEEP_INTERVAL) { // sweep sweepCache(); gAccessCount = 0; } } umtx_unlock(&gTZGNLock); if (cacheEntry == NULL) { delete instance; return NULL; } instance->fRef = cacheEntry; return instance; }
U_CDECL_END U_NAMESPACE_BEGIN #define DEFAULT_INCLUSION_CAPACITY 3072 const UnicodeSet* UnicodeSet::getInclusions(int32_t src, UErrorCode &status) { UBool needInit; UMTX_CHECK(NULL, (INCLUSIONS[src] == NULL), needInit); if (needInit) { UnicodeSet* incl = new UnicodeSet(); USetAdder sa = { (USet *)incl, _set_add, _set_addRange, _set_addString, NULL, // don't need remove() NULL // don't need removeRange() }; incl->ensureCapacity(DEFAULT_INCLUSION_CAPACITY, status); if (incl != NULL) { switch(src) { case UPROPS_SRC_CHAR: uchar_addPropertyStarts(&sa, &status); break; case UPROPS_SRC_PROPSVEC: upropsvec_addPropertyStarts(&sa, &status); break; case UPROPS_SRC_CHAR_AND_PROPSVEC: uchar_addPropertyStarts(&sa, &status); upropsvec_addPropertyStarts(&sa, &status); break; #if !UCONFIG_NO_NORMALIZATION case UPROPS_SRC_NORM: unorm_addPropertyStarts(&sa, &status); break; case UPROPS_SRC_CASE_AND_NORM: ucase_addPropertyStarts(ucase_getSingleton(&status), &sa, &status); unorm_addPropertyStarts(&sa, &status); break; case UPROPS_SRC_NFC: { const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(status); if(U_SUCCESS(status)) { impl->addPropertyStarts(&sa, status); } break; } case UPROPS_SRC_NFKC: { const Normalizer2Impl *impl=Normalizer2Factory::getNFKCImpl(status); if(U_SUCCESS(status)) { impl->addPropertyStarts(&sa, status); } break; } case UPROPS_SRC_NFKC_CF: { const Normalizer2Impl *impl=Normalizer2Factory::getNFKC_CFImpl(status); if(U_SUCCESS(status)) { impl->addPropertyStarts(&sa, status); } break; } #endif case UPROPS_SRC_CASE: ucase_addPropertyStarts(ucase_getSingleton(&status), &sa, &status); break; case UPROPS_SRC_BIDI: ubidi_addPropertyStarts(ubidi_getSingleton(&status), &sa, &status); break; default: status = U_INTERNAL_PROGRAM_ERROR; break; } if (U_SUCCESS(status)) { // Compact for caching incl->compact(); umtx_lock(NULL); if (INCLUSIONS[src] == NULL) { INCLUSIONS[src] = incl; incl = NULL; ucln_common_registerCleanup(UCLN_COMMON_USET, uset_cleanup); } umtx_unlock(NULL); } delete incl; } else { status = U_MEMORY_ALLOCATION_ERROR; } } return INCLUSIONS[src]; }
static UBool haveAliasData(UErrorCode* pErrorCode) { int needInit; if (pErrorCode == NULL || U_FAILURE(*pErrorCode)) { return FALSE; } UMTX_CHECK(NULL, (gAliasData == NULL), needInit); /* load converter alias data from file if necessary */ if (needInit) { UDataMemory* data = NULL; const uint16_t* table = NULL; uint32_t tableStart; uint32_t currOffset; data = udata_openChoice(NULL, DATA_TYPE, DATA_NAME, isAcceptable, NULL, pErrorCode); if (U_FAILURE(*pErrorCode)) { return FALSE; } table = (const uint16_t*) udata_getMemory(data); tableStart = ((const uint32_t*) (table))[0]; if (tableStart < minTocLength) { *pErrorCode = U_INVALID_FORMAT_ERROR; udata_close(data); return FALSE; } umtx_lock(NULL); if (gAliasData == NULL) { gAliasData = data; data = NULL; gMainTable.converterListSize = ((const uint32_t*) (table))[1]; gMainTable.tagListSize = ((const uint32_t*) (table))[2]; gMainTable.aliasListSize = ((const uint32_t*) (table))[3]; gMainTable.untaggedConvArraySize = ((const uint32_t*) (table))[4]; gMainTable.taggedAliasArraySize = ((const uint32_t*) (table))[5]; gMainTable.taggedAliasListsSize = ((const uint32_t*) (table))[6]; gMainTable.optionTableSize = ((const uint32_t*) (table))[7]; gMainTable.stringTableSize = ((const uint32_t*) (table))[8]; if (((const uint32_t*) (table))[0] > 8) { gMainTable.normalizedStringTableSize = ((const uint32_t*) (table))[9]; } currOffset = tableStart * (sizeof(uint32_t) / sizeof(uint16_t)) + (sizeof(uint32_t) / sizeof(uint16_t)); gMainTable.converterList = table + currOffset; currOffset += gMainTable.converterListSize; gMainTable.tagList = table + currOffset; currOffset += gMainTable.tagListSize; gMainTable.aliasList = table + currOffset; currOffset += gMainTable.aliasListSize; gMainTable.untaggedConvArray = table + currOffset; currOffset += gMainTable.untaggedConvArraySize; gMainTable.taggedAliasArray = table + currOffset; /* aliasLists is a 1's based array, but it has a padding character */ currOffset += gMainTable.taggedAliasArraySize; gMainTable.taggedAliasLists = table + currOffset; currOffset += gMainTable.taggedAliasListsSize; if (gMainTable.optionTableSize > 0 && ((const UConverterAliasOptions*) (table + currOffset))->stringNormalizationType < UCNV_IO_NORM_TYPE_COUNT) { /* Faster table */ gMainTable.optionTable = (const UConverterAliasOptions*) (table + currOffset); } else { /* Smaller table, or I can't handle this normalization mode! Use the original slower table lookup. */ gMainTable.optionTable = &defaultTableOptions; } currOffset += gMainTable.optionTableSize; gMainTable.stringTable = table + currOffset; currOffset += gMainTable.stringTableSize; gMainTable.normalizedStringTable = ((gMainTable.optionTable->stringNormalizationType == UCNV_IO_UNNORMALIZED) ? gMainTable.stringTable : (table + currOffset)); ucln_common_registerCleanup(UCLN_COMMON_UCNV_IO, ucnv_io_cleanup); } umtx_unlock(NULL); /* if a different thread set it first, then close the extra data */ if (data != NULL) { udata_close(data); /* NULL if it was set correctly */ } } return TRUE; }
/** * Inline function that expands to code that does a lazy load of the * property names data. If the data is already loaded, avoids an * unnecessary function call. If the data is not loaded, call _load() * to load it, and return TRUE if the load succeeds. */ static inline UBool load() { UBool f; UMTX_CHECK(NULL, (PNAME!=NULL), f); return f || _load(); }
void ZoneMeta::initAvailableMetaZoneIDs () { UBool initialized; UMTX_CHECK(&gZoneMetaLock, gMetaZoneIDsInitialized, initialized); if (!initialized) { umtx_lock(&gZoneMetaLock); { if (!gMetaZoneIDsInitialized) { UErrorCode status = U_ZERO_ERROR; UHashtable *metaZoneIDTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status); uhash_setKeyDeleter(metaZoneIDTable, uprv_deleteUObject); // No valueDeleter, because the vector maintain the value objects UVector *metaZoneIDs = NULL; if (U_SUCCESS(status)) { metaZoneIDs = new UVector(NULL, uhash_compareUChars, status); if (metaZoneIDs == NULL) { status = U_MEMORY_ALLOCATION_ERROR; } } else { uhash_close(metaZoneIDTable); } if (U_SUCCESS(status)) { U_ASSERT(metaZoneIDs != NULL); metaZoneIDs->setDeleter(uprv_free); UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status); UResourceBundle *bundle = ures_getByKey(rb, gMapTimezonesTag, NULL, &status); UResourceBundle res; ures_initStackObject(&res); while (U_SUCCESS(status) && ures_hasNext(bundle)) { ures_getNextResource(bundle, &res, &status); if (U_FAILURE(status)) { break; } const char *mzID = ures_getKey(&res); int32_t len = uprv_strlen(mzID); UChar *uMzID = (UChar*)uprv_malloc(sizeof(UChar) * (len + 1)); if (uMzID == NULL) { status = U_MEMORY_ALLOCATION_ERROR; break; } u_charsToUChars(mzID, uMzID, len); uMzID[len] = 0; UnicodeString *usMzID = new UnicodeString(uMzID); if (uhash_get(metaZoneIDTable, usMzID) == NULL) { metaZoneIDs->addElement((void *)uMzID, status); uhash_put(metaZoneIDTable, (void *)usMzID, (void *)uMzID, &status); } else { uprv_free(uMzID); delete usMzID; } } if (U_SUCCESS(status)) { gMetaZoneIDs = metaZoneIDs; gMetaZoneIDTable = metaZoneIDTable; gMetaZoneIDsInitialized = TRUE; } else { uhash_close(metaZoneIDTable); delete metaZoneIDs; } ures_close(&res); ures_close(bundle); ures_close(rb); } } } umtx_unlock(&gZoneMetaLock); } }
const UVector* U_EXPORT2 ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) { UErrorCode status = U_ZERO_ERROR; UChar tzidUChars[ZID_KEY_MAX + 1]; tzid.extract(tzidUChars, ZID_KEY_MAX + 1, status); if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { return NULL; } UBool initialized; UMTX_CHECK(&gZoneMetaLock, gOlsonToMetaInitialized, initialized); if (!initialized) { UHashtable *tmpOlsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); if (U_FAILURE(status)) { return NULL; } uhash_setKeyDeleter(tmpOlsonToMeta, deleteUCharString); uhash_setValueDeleter(tmpOlsonToMeta, deleteUVector); umtx_lock(&gZoneMetaLock); { if (!gOlsonToMetaInitialized) { gOlsonToMeta = tmpOlsonToMeta; tmpOlsonToMeta = NULL; gOlsonToMetaInitialized = TRUE; } } umtx_unlock(&gZoneMetaLock); // OK to call the following multiple times with the same function ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup); if (tmpOlsonToMeta != NULL) { uhash_close(tmpOlsonToMeta); } } // get the mapping from cache const UVector *result = NULL; umtx_lock(&gZoneMetaLock); { result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars); } umtx_unlock(&gZoneMetaLock); if (result != NULL) { return result; } // miss the cache - create new one UVector *tmpResult = createMetazoneMappings(tzid); if (tmpResult == NULL) { // not available return NULL; } // put the new one into the cache umtx_lock(&gZoneMetaLock); { // make sure it's already created result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars); if (result == NULL) { // add the one just created int32_t tzidLen = tzid.length() + 1; UChar *key = (UChar*)uprv_malloc(tzidLen * sizeof(UChar)); if (key == NULL) { // memory allocation error.. just return NULL result = NULL; delete tmpResult; } else { tzid.extract(key, tzidLen, status); uhash_put(gOlsonToMeta, key, tmpResult, &status); if (U_FAILURE(status)) { // delete the mapping result = NULL; delete tmpResult; } else { result = tmpResult; } } } else { // another thread already put the one delete tmpResult; } } umtx_unlock(&gZoneMetaLock); return result; }
UnicodeString& U_EXPORT2 ZoneMeta::getSingleCountry(const UnicodeString &tzid, UnicodeString &country) { // Get canonical country for the zone const UChar *region = TimeZone::getRegion(tzid); if (region == NULL || u_strcmp(gWorld, region) == 0) { // special case - unknown or "001" country.setToBogus(); return country; } // Checking the cached results UErrorCode status = U_ZERO_ERROR; UBool initialized; UMTX_CHECK(&gZoneMetaLock, gCountryInfoVectorsInitialized, initialized); if (!initialized) { // Create empty vectors umtx_lock(&gZoneMetaLock); { if (!gCountryInfoVectorsInitialized) { // No deleters for these UVectors, it's a reference to a resource bundle string. gSingleZoneCountries = new UVector(NULL, uhash_compareUChars, status); if (gSingleZoneCountries == NULL) { status = U_MEMORY_ALLOCATION_ERROR; } gMultiZonesCountries = new UVector(NULL, uhash_compareUChars, status); if (gMultiZonesCountries == NULL) { status = U_MEMORY_ALLOCATION_ERROR; } if (U_SUCCESS(status)) { gCountryInfoVectorsInitialized = TRUE; } else { delete gSingleZoneCountries; delete gMultiZonesCountries; } } } umtx_unlock(&gZoneMetaLock); if (U_FAILURE(status)) { country.setToBogus(); return country; } U_ASSERT(gSingleZoneCountries != NULL); U_ASSERT(gMultiZonesCountries != NULL); } // Check if it was already cached UBool cached = FALSE; UBool multiZones = FALSE; umtx_lock(&gZoneMetaLock); { multiZones = cached = gMultiZonesCountries->contains((void*)region); if (!multiZones) { cached = gSingleZoneCountries->contains((void*)region); } } umtx_unlock(&gZoneMetaLock); if (!cached) { // We need to go through all zones associated with the region. // This is relatively heavy operation. U_ASSERT(u_strlen(region) == 2); char buf[] = {0, 0, 0}; u_UCharsToChars(region, buf, 2); StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, buf, NULL, status); int32_t idsLen = ids->count(status); if (U_SUCCESS(status) && idsLen > 1) { // multiple canonical zones are available for the region multiZones = TRUE; } if (U_FAILURE(status)) { // no single country by default for any error cases multiZones = TRUE; } delete ids; // Cache the result umtx_lock(&gZoneMetaLock); { UErrorCode ec = U_ZERO_ERROR; if (multiZones) { if (!gMultiZonesCountries->contains((void*)region)) { gMultiZonesCountries->addElement((void*)region, ec); } } else { if (!gSingleZoneCountries->contains((void*)region)) { gSingleZoneCountries->addElement((void*)region, ec); } } } umtx_unlock(&gZoneMetaLock); } if (multiZones) { country.setToBogus(); } else { country.setTo(region, -1); } return country; }
const UChar* U_EXPORT2 ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) { if (U_FAILURE(status)) { return NULL; } int32_t len = tzid.length(); if (len > ZID_KEY_MAX) { status = U_ILLEGAL_ARGUMENT_ERROR; return NULL; } // Checking the cached results UBool initialized; UMTX_CHECK(&gZoneMetaLock, gCanonicalIDCacheInitialized, initialized); if (!initialized) { // Create empty hashtable umtx_lock(&gZoneMetaLock); { if (!gCanonicalIDCacheInitialized) { gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); if (gCanonicalIDCache == NULL) { status = U_MEMORY_ALLOCATION_ERROR; } if (U_FAILURE(status)) { gCanonicalIDCache = NULL; return NULL; } // No key/value deleters - keys/values are from a resource bundle gCanonicalIDCacheInitialized = TRUE; ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup); } } umtx_unlock(&gZoneMetaLock); } const UChar *canonicalID = NULL; UErrorCode tmpStatus = U_ZERO_ERROR; UChar utzid[ZID_KEY_MAX + 1]; tzid.extract(utzid, ZID_KEY_MAX + 1, tmpStatus); U_ASSERT(tmpStatus == U_ZERO_ERROR); // we checked the length of tzid already // Check if it was already cached umtx_lock(&gZoneMetaLock); { canonicalID = (const UChar *)uhash_get(gCanonicalIDCache, utzid); } umtx_unlock(&gZoneMetaLock); if (canonicalID != NULL) { return canonicalID; } // If not, resolve CLDR canonical ID with resource data UBool isInputCanonical = FALSE; char id[ZID_KEY_MAX + 1]; const UChar* idChars = tzid.getBuffer(); u_UCharsToChars(idChars,id,len); id[len] = (char) 0; // Make sure it is null terminated. // replace '/' with ':' char *p = id; while (*p++) { if (*p == '/') { *p = ':'; } } UResourceBundle *top = ures_openDirect(NULL, gTimeZoneTypes, &tmpStatus); UResourceBundle *rb = ures_getByKey(top, gTypeMapTag, NULL, &tmpStatus); ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus); ures_getByKey(rb, id, rb, &tmpStatus); if (U_SUCCESS(tmpStatus)) { // type entry (canonical) found // the input is the canonical ID. resolve to const UChar* canonicalID = TimeZone::findID(tzid); isInputCanonical = TRUE; } if (canonicalID == NULL) { // If a map element not found, then look for an alias tmpStatus = U_ZERO_ERROR; ures_getByKey(top, gTypeAliasTag, rb, &tmpStatus); ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus); const UChar *canonical = ures_getStringByKey(rb,id,NULL,&tmpStatus); if (U_SUCCESS(tmpStatus)) { // canonical map found canonicalID = canonical; } if (canonicalID == NULL) { // Dereference the input ID using the tz data const UChar *derefer = TimeZone::dereferOlsonLink(tzid); if (derefer == NULL) { status = U_ILLEGAL_ARGUMENT_ERROR; } else { len = u_strlen(derefer); u_UCharsToChars(derefer,id,len); id[len] = (char) 0; // Make sure it is null terminated. // replace '/' with ':' char *p = id; while (*p++) { if (*p == '/') { *p = ':'; } } // If a dereference turned something up then look for an alias. // rb still points to the alias table, so we don't have to go looking // for it. tmpStatus = U_ZERO_ERROR; canonical = ures_getStringByKey(rb,id,NULL,&tmpStatus); if (U_SUCCESS(tmpStatus)) { // canonical map for the dereferenced ID found canonicalID = canonical; } else { canonicalID = derefer; isInputCanonical = TRUE; } } } } ures_close(rb); ures_close(top); if (U_SUCCESS(status)) { U_ASSERT(canonicalID != NULL); // canocanilD must be non-NULL here // Put the resolved canonical ID to the cache umtx_lock(&gZoneMetaLock); { const UChar* idInCache = (const UChar *)uhash_get(gCanonicalIDCache, utzid); if (idInCache == NULL) { const UChar* key = ZoneMeta::findTimeZoneID(tzid); U_ASSERT(key != NULL); if (key != NULL) { idInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)key, (void *)canonicalID, &status); U_ASSERT(idInCache == NULL); } } if (U_SUCCESS(status) && isInputCanonical) { // Also put canonical ID itself into the cache if not exist const UChar *canonicalInCache = (const UChar*)uhash_get(gCanonicalIDCache, canonicalID); if (canonicalInCache == NULL) { canonicalInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)canonicalID, (void *)canonicalID, &status); U_ASSERT(canonicalInCache == NULL); } } } umtx_unlock(&gZoneMetaLock); } return canonicalID; }