diff options
Diffstat (limited to 'deps/node/deps/icu-small/source/i18n/tzgnames.cpp')
-rw-r--r-- | deps/node/deps/icu-small/source/i18n/tzgnames.cpp | 1324 |
1 files changed, 0 insertions, 1324 deletions
diff --git a/deps/node/deps/icu-small/source/i18n/tzgnames.cpp b/deps/node/deps/icu-small/source/i18n/tzgnames.cpp deleted file mode 100644 index 5f5b7db3..00000000 --- a/deps/node/deps/icu-small/source/i18n/tzgnames.cpp +++ /dev/null @@ -1,1324 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2011-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "tzgnames.h" - -#include "unicode/basictz.h" -#include "unicode/locdspnm.h" -#include "unicode/rbtz.h" -#include "unicode/simpleformatter.h" -#include "unicode/simpletz.h" -#include "unicode/strenum.h" -#include "unicode/vtzone.h" - -#include "cmemory.h" -#include "cstring.h" -#include "mutex.h" -#include "uhash.h" -#include "uassert.h" -#include "umutex.h" -#include "uresimp.h" -#include "ureslocs.h" -#include "zonemeta.h" -#include "tznames_impl.h" -#include "olsontz.h" -#include "ucln_in.h" - -U_NAMESPACE_BEGIN - -#define ZID_KEY_MAX 128 - -static const char gZoneStrings[] = "zoneStrings"; - -static const char gRegionFormatTag[] = "regionFormat"; -static const char gFallbackFormatTag[] = "fallbackFormat"; - -static const UChar gEmpty[] = {0x00}; - -static const UChar gDefRegionPattern[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}" -static const UChar gDefFallbackPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})" - -static const double kDstCheckRange = (double)184*U_MILLIS_PER_DAY; - - - -U_CDECL_BEGIN - -typedef struct PartialLocationKey { - const UChar* tzID; - const UChar* mzID; - UBool isLong; -} PartialLocationKey; - -/** - * Hash function for partial location name hash key - */ -static int32_t U_CALLCONV -hashPartialLocationKey(const UHashTok key) { - // <tzID>&<mzID>#[L|S] - PartialLocationKey *p = (PartialLocationKey *)key.pointer; - UnicodeString str(p->tzID); - str.append((UChar)0x26) - .append(p->mzID, -1) - .append((UChar)0x23) - .append((UChar)(p->isLong ? 0x4C : 0x53)); - return str.hashCode(); -} - -/** - * Comparer for partial location name hash key - */ -static UBool U_CALLCONV -comparePartialLocationKey(const UHashTok key1, const UHashTok key2) { - PartialLocationKey *p1 = (PartialLocationKey *)key1.pointer; - PartialLocationKey *p2 = (PartialLocationKey *)key2.pointer; - - if (p1 == p2) { - return TRUE; - } - if (p1 == NULL || p2 == NULL) { - return FALSE; - } - // We just check identity of tzID/mzID - return (p1->tzID == p2->tzID && p1->mzID == p2->mzID && p1->isLong == p2->isLong); -} - -/** - * Deleter for GNameInfo - */ -static void U_CALLCONV -deleteGNameInfo(void *obj) { - uprv_free(obj); -} - -/** - * GNameInfo stores zone name information in the local trie - */ -typedef struct GNameInfo { - UTimeZoneGenericNameType type; - const UChar* tzID; -} ZNameInfo; - -/** - * GMatchInfo stores zone name match information used by find method - */ -typedef struct GMatchInfo { - const GNameInfo* gnameInfo; - int32_t matchLength; - UTimeZoneFormatTimeType timeType; -} ZMatchInfo; - -U_CDECL_END - -// --------------------------------------------------- -// The class stores time zone generic name match information -// --------------------------------------------------- -class TimeZoneGenericNameMatchInfo : public UMemory { -public: - TimeZoneGenericNameMatchInfo(UVector* matches); - ~TimeZoneGenericNameMatchInfo(); - - int32_t size() const; - UTimeZoneGenericNameType getGenericNameType(int32_t index) const; - int32_t getMatchLength(int32_t index) const; - UnicodeString& getTimeZoneID(int32_t index, UnicodeString& tzID) const; - -private: - UVector* fMatches; // vector of MatchEntry -}; - -TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches) -: fMatches(matches) { -} - -TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() { - if (fMatches != NULL) { - delete fMatches; - } -} - -int32_t -TimeZoneGenericNameMatchInfo::size() const { - if (fMatches == NULL) { - return 0; - } - return fMatches->size(); -} - -UTimeZoneGenericNameType -TimeZoneGenericNameMatchInfo::getGenericNameType(int32_t index) const { - GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index); - if (minfo != NULL) { - return static_cast<UTimeZoneGenericNameType>(minfo->gnameInfo->type); - } - return UTZGNM_UNKNOWN; -} - -int32_t -TimeZoneGenericNameMatchInfo::getMatchLength(int32_t index) const { - ZMatchInfo *minfo = (ZMatchInfo *)fMatches->elementAt(index); - if (minfo != NULL) { - return minfo->matchLength; - } - return -1; -} - -UnicodeString& -TimeZoneGenericNameMatchInfo::getTimeZoneID(int32_t index, UnicodeString& tzID) const { - GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index); - if (minfo != NULL && minfo->gnameInfo->tzID != NULL) { - tzID.setTo(TRUE, minfo->gnameInfo->tzID, -1); - } else { - tzID.setToBogus(); - } - return tzID; -} - -// --------------------------------------------------- -// GNameSearchHandler -// --------------------------------------------------- -class GNameSearchHandler : public TextTrieMapSearchResultHandler { -public: - GNameSearchHandler(uint32_t types); - virtual ~GNameSearchHandler(); - - UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status); - UVector* getMatches(int32_t& maxMatchLen); - -private: - uint32_t fTypes; - UVector* fResults; - int32_t fMaxMatchLen; -}; - -GNameSearchHandler::GNameSearchHandler(uint32_t types) -: fTypes(types), fResults(NULL), fMaxMatchLen(0) { -} - -GNameSearchHandler::~GNameSearchHandler() { - if (fResults != NULL) { - delete fResults; - } -} - -UBool -GNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) { - if (U_FAILURE(status)) { - return FALSE; - } - if (node->hasValues()) { - int32_t valuesCount = node->countValues(); - for (int32_t i = 0; i < valuesCount; i++) { - GNameInfo *nameinfo = (ZNameInfo *)node->getValue(i); - if (nameinfo == NULL) { - break; - } - if ((nameinfo->type & fTypes) != 0) { - // matches a requested type - if (fResults == NULL) { - fResults = new UVector(uprv_free, NULL, status); - if (fResults == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } - } - if (U_SUCCESS(status)) { - U_ASSERT(fResults != NULL); - GMatchInfo *gmatch = (GMatchInfo *)uprv_malloc(sizeof(GMatchInfo)); - if (gmatch == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } else { - // add the match to the vector - gmatch->gnameInfo = nameinfo; - gmatch->matchLength = matchLength; - gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN; - fResults->addElement(gmatch, status); - if (U_FAILURE(status)) { - uprv_free(gmatch); - } else { - if (matchLength > fMaxMatchLen) { - fMaxMatchLen = matchLength; - } - } - } - } - } - } - } - return TRUE; -} - -UVector* -GNameSearchHandler::getMatches(int32_t& maxMatchLen) { - // give the ownership to the caller - UVector *results = fResults; - maxMatchLen = fMaxMatchLen; - - // reset - fResults = NULL; - fMaxMatchLen = 0; - return results; -} - -static UMutex gLock = U_MUTEX_INITIALIZER; - -class TZGNCore : public UMemory { -public: - TZGNCore(const Locale& locale, UErrorCode& status); - virtual ~TZGNCore(); - - UnicodeString& getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, - UDate date, UnicodeString& name) const; - - UnicodeString& getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const; - - int32_t findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, - UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const; - -private: - Locale fLocale; - const TimeZoneNames* fTimeZoneNames; - UHashtable* fLocationNamesMap; - UHashtable* fPartialLocationNamesMap; - - SimpleFormatter fRegionFormat; - SimpleFormatter fFallbackFormat; - - LocaleDisplayNames* fLocaleDisplayNames; - ZNStringPool fStringPool; - - TextTrieMap fGNamesTrie; - UBool fGNamesTrieFullyLoaded; - - char fTargetRegion[ULOC_COUNTRY_CAPACITY]; - - void initialize(const Locale& locale, UErrorCode& status); - void cleanup(); - - void loadStrings(const UnicodeString& tzCanonicalID); - - const UChar* getGenericLocationName(const UnicodeString& tzCanonicalID); - - UnicodeString& formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, - UDate date, UnicodeString& name) const; - - UnicodeString& getPartialLocationName(const UnicodeString& tzCanonicalID, - const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName, - UnicodeString& name) const; - - const UChar* getPartialLocationName(const UnicodeString& tzCanonicalID, - const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName); - - TimeZoneGenericNameMatchInfo* findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const; - - TimeZoneNames::MatchInfoCollection* findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const; -}; - - -// --------------------------------------------------- -// TZGNCore - core implmentation of TimeZoneGenericNames -// -// TimeZoneGenericNames is parallel to TimeZoneNames, -// but handles run-time generated time zone names. -// This is the main part of this module. -// --------------------------------------------------- -TZGNCore::TZGNCore(const Locale& locale, UErrorCode& status) -: fLocale(locale), - fTimeZoneNames(NULL), - fLocationNamesMap(NULL), - fPartialLocationNamesMap(NULL), - fLocaleDisplayNames(NULL), - fStringPool(status), - fGNamesTrie(TRUE, deleteGNameInfo), - fGNamesTrieFullyLoaded(FALSE) { - initialize(locale, status); -} - -TZGNCore::~TZGNCore() { - cleanup(); -} - -void -TZGNCore::initialize(const Locale& locale, UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - - // TimeZoneNames - fTimeZoneNames = TimeZoneNames::createInstance(locale, status); - if (U_FAILURE(status)) { - return; - } - - // Initialize format patterns - UnicodeString rpat(TRUE, gDefRegionPattern, -1); - UnicodeString fpat(TRUE, gDefFallbackPattern, -1); - - UErrorCode tmpsts = U_ZERO_ERROR; // OK with fallback warning.. - UResourceBundle *zoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts); - zoneStrings = ures_getByKeyWithFallback(zoneStrings, gZoneStrings, zoneStrings, &tmpsts); - - if (U_SUCCESS(tmpsts)) { - const UChar *regionPattern = ures_getStringByKeyWithFallback(zoneStrings, gRegionFormatTag, NULL, &tmpsts); - if (U_SUCCESS(tmpsts) && u_strlen(regionPattern) > 0) { - rpat.setTo(regionPattern, -1); - } - tmpsts = U_ZERO_ERROR; - const UChar *fallbackPattern = ures_getStringByKeyWithFallback(zoneStrings, gFallbackFormatTag, NULL, &tmpsts); - if (U_SUCCESS(tmpsts) && u_strlen(fallbackPattern) > 0) { - fpat.setTo(fallbackPattern, -1); - } - } - ures_close(zoneStrings); - - fRegionFormat.applyPatternMinMaxArguments(rpat, 1, 1, status); - fFallbackFormat.applyPatternMinMaxArguments(fpat, 2, 2, status); - if (U_FAILURE(status)) { - cleanup(); - return; - } - - // locale display names - fLocaleDisplayNames = LocaleDisplayNames::createInstance(locale); - - // hash table for names - no key/value deleters - fLocationNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); - if (U_FAILURE(status)) { - cleanup(); - return; - } - - fPartialLocationNamesMap = uhash_open(hashPartialLocationKey, comparePartialLocationKey, NULL, &status); - if (U_FAILURE(status)) { - cleanup(); - return; - } - uhash_setKeyDeleter(fPartialLocationNamesMap, uprv_free); - // no value deleter - - // target region - const char* region = fLocale.getCountry(); - int32_t regionLen = static_cast<int32_t>(uprv_strlen(region)); - if (regionLen == 0) { - char loc[ULOC_FULLNAME_CAPACITY]; - uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status); - - regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status); - if (U_SUCCESS(status)) { - fTargetRegion[regionLen] = 0; - } else { - cleanup(); - return; - } - } else if (regionLen < (int32_t)sizeof(fTargetRegion)) { - uprv_strcpy(fTargetRegion, region); - } else { - fTargetRegion[0] = 0; - } - - // preload generic names for the default zone - TimeZone *tz = TimeZone::createDefault(); - const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz); - if (tzID != NULL) { - loadStrings(UnicodeString(TRUE, tzID, -1)); - } - delete tz; -} - -void -TZGNCore::cleanup() { - if (fLocaleDisplayNames != NULL) { - delete fLocaleDisplayNames; - } - if (fTimeZoneNames != NULL) { - delete fTimeZoneNames; - } - - uhash_close(fLocationNamesMap); - uhash_close(fPartialLocationNamesMap); -} - - -UnicodeString& -TZGNCore::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const { - name.setToBogus(); - switch (type) { - case UTZGNM_LOCATION: - { - const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz); - if (tzCanonicalID != NULL) { - getGenericLocationName(UnicodeString(TRUE, tzCanonicalID, -1), name); - } - } - break; - case UTZGNM_LONG: - case UTZGNM_SHORT: - formatGenericNonLocationName(tz, type, date, name); - if (name.isEmpty()) { - const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz); - if (tzCanonicalID != NULL) { - getGenericLocationName(UnicodeString(TRUE, tzCanonicalID, -1), name); - } - } - break; - default: - break; - } - return name; -} - -UnicodeString& -TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const { - if (tzCanonicalID.isEmpty()) { - name.setToBogus(); - return name; - } - - const UChar *locname = NULL; - TZGNCore *nonConstThis = const_cast<TZGNCore *>(this); - umtx_lock(&gLock); - { - locname = nonConstThis->getGenericLocationName(tzCanonicalID); - } - umtx_unlock(&gLock); - - if (locname == NULL) { - name.setToBogus(); - } else { - name.setTo(locname, u_strlen(locname)); - } - - return name; -} - -/* - * This method updates the cache and must be called with a lock - */ -const UChar* -TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID) { - U_ASSERT(!tzCanonicalID.isEmpty()); - if (tzCanonicalID.length() > ZID_KEY_MAX) { - return NULL; - } - - UErrorCode status = U_ZERO_ERROR; - UChar tzIDKey[ZID_KEY_MAX + 1]; - int32_t tzIDKeyLen = tzCanonicalID.extract(tzIDKey, ZID_KEY_MAX + 1, status); - U_ASSERT(status == U_ZERO_ERROR); // already checked length above - tzIDKey[tzIDKeyLen] = 0; - - const UChar *locname = (const UChar *)uhash_get(fLocationNamesMap, tzIDKey); - - if (locname != NULL) { - // gEmpty indicate the name is not available - if (locname == gEmpty) { - return NULL; - } - return locname; - } - - // Construct location name - UnicodeString name; - UnicodeString usCountryCode; - UBool isPrimary = FALSE; - - ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode, &isPrimary); - - if (!usCountryCode.isEmpty()) { - if (isPrimary) { - // If this is the primary zone in the country, use the country name. - char countryCode[ULOC_COUNTRY_CAPACITY]; - U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY); - int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV); - countryCode[ccLen] = 0; - - UnicodeString country; - fLocaleDisplayNames->regionDisplayName(countryCode, country); - fRegionFormat.format(country, name, status); - } else { - // If this is not the primary zone in the country, - // use the exemplar city name. - - // getExemplarLocationName should retur non-empty string - // if the time zone is associated with a region - - UnicodeString city; - fTimeZoneNames->getExemplarLocationName(tzCanonicalID, city); - fRegionFormat.format(city, name, status); - } - if (U_FAILURE(status)) { - return NULL; - } - } - - locname = name.isEmpty() ? NULL : fStringPool.get(name, status); - if (U_SUCCESS(status)) { - // Cache the result - const UChar* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID); - U_ASSERT(cacheID != NULL); - if (locname == NULL) { - // gEmpty to indicate - no location name available - uhash_put(fLocationNamesMap, (void *)cacheID, (void *)gEmpty, &status); - } else { - uhash_put(fLocationNamesMap, (void *)cacheID, (void *)locname, &status); - if (U_FAILURE(status)) { - locname = NULL; - } else { - // put the name info into the trie - GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo)); - if (nameinfo != NULL) { - nameinfo->type = UTZGNM_LOCATION; - nameinfo->tzID = cacheID; - fGNamesTrie.put(locname, nameinfo, status); - } - } - } - } - - return locname; -} - -UnicodeString& -TZGNCore::formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const { - U_ASSERT(type == UTZGNM_LONG || type == UTZGNM_SHORT); - name.setToBogus(); - - const UChar* uID = ZoneMeta::getCanonicalCLDRID(tz); - if (uID == NULL) { - return name; - } - - UnicodeString tzID(TRUE, uID, -1); - - // Try to get a name from time zone first - UTimeZoneNameType nameType = (type == UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC; - fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name); - - if (!name.isEmpty()) { - return name; - } - - // Try meta zone - UChar mzIDBuf[32]; - UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf)); - fTimeZoneNames->getMetaZoneID(tzID, date, mzID); - if (!mzID.isEmpty()) { - UErrorCode status = U_ZERO_ERROR; - UBool useStandard = FALSE; - int32_t raw, sav; - UChar tmpNameBuf[ZONE_NAME_U16_MAX]; - - tz.getOffset(date, FALSE, raw, sav, status); - if (U_FAILURE(status)) { - return name; - } - - if (sav == 0) { - useStandard = TRUE; - - TimeZone *tmptz = tz.clone(); - // Check if the zone actually uses daylight saving time around the time - BasicTimeZone *btz = NULL; - if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL - || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL - || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL - || dynamic_cast<VTimeZone *>(tmptz) != NULL) { - btz = (BasicTimeZone*)tmptz; - } - - if (btz != NULL) { - TimeZoneTransition before; - UBool beforTrs = btz->getPreviousTransition(date, TRUE, before); - if (beforTrs - && (date - before.getTime() < kDstCheckRange) - && before.getFrom()->getDSTSavings() != 0) { - useStandard = FALSE; - } else { - TimeZoneTransition after; - UBool afterTrs = btz->getNextTransition(date, FALSE, after); - if (afterTrs - && (after.getTime() - date < kDstCheckRange) - && after.getTo()->getDSTSavings() != 0) { - useStandard = FALSE; - } - } - } else { - // If not BasicTimeZone... only if the instance is not an ICU's implementation. - // We may get a wrong answer in edge case, but it should practically work OK. - tmptz->getOffset(date - kDstCheckRange, FALSE, raw, sav, status); - if (sav != 0) { - useStandard = FALSE; - } else { - tmptz->getOffset(date + kDstCheckRange, FALSE, raw, sav, status); - if (sav != 0){ - useStandard = FALSE; - } - } - if (U_FAILURE(status)) { - delete tmptz; - return name; - } - } - delete tmptz; - } - if (useStandard) { - UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC) - ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD; - UnicodeString stdName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf)); - fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName); - if (!stdName.isEmpty()) { - name.setTo(stdName); - - // TODO: revisit this issue later - // In CLDR, a same display name is used for both generic and standard - // for some meta zones in some locales. This looks like a data bugs. - // For now, we check if the standard name is different from its generic - // name below. - UChar genNameBuf[ZONE_NAME_U16_MAX]; - UnicodeString mzGenericName(genNameBuf, 0, UPRV_LENGTHOF(genNameBuf)); - fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName); - if (stdName.caseCompare(mzGenericName, 0) == 0) { - name.setToBogus(); - } - } - } - if (name.isEmpty()) { - // Get a name from meta zone - UnicodeString mzName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf)); - fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName); - if (!mzName.isEmpty()) { - // Check if we need to use a partial location format. - // This check is done by comparing offset with the meta zone's - // golden zone at the given date. - UChar idBuf[32]; - UnicodeString goldenID(idBuf, 0, UPRV_LENGTHOF(idBuf)); - fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, goldenID); - if (!goldenID.isEmpty() && goldenID != tzID) { - TimeZone *goldenZone = TimeZone::createTimeZone(goldenID); - int32_t raw1, sav1; - - // Check offset in the golden zone with wall time. - // With getOffset(date, false, offsets1), - // you may get incorrect results because of time overlap at DST->STD - // transition. - goldenZone->getOffset(date + raw + sav, TRUE, raw1, sav1, status); - delete goldenZone; - if (U_SUCCESS(status)) { - if (raw != raw1 || sav != sav1) { - // Now we need to use a partial location format - getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name); - } else { - name.setTo(mzName); - } - } - } else { - name.setTo(mzName); - } - } - } - } - return name; -} - -UnicodeString& -TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID, - const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName, - UnicodeString& name) const { - name.setToBogus(); - if (tzCanonicalID.isEmpty() || mzID.isEmpty() || mzDisplayName.isEmpty()) { - return name; - } - - const UChar *uplname = NULL; - TZGNCore *nonConstThis = const_cast<TZGNCore *>(this); - umtx_lock(&gLock); - { - uplname = nonConstThis->getPartialLocationName(tzCanonicalID, mzID, isLong, mzDisplayName); - } - umtx_unlock(&gLock); - - if (uplname == NULL) { - name.setToBogus(); - } else { - name.setTo(TRUE, uplname, -1); - } - return name; -} - -/* - * This method updates the cache and must be called with a lock - */ -const UChar* -TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID, - const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) { - U_ASSERT(!tzCanonicalID.isEmpty()); - U_ASSERT(!mzID.isEmpty()); - U_ASSERT(!mzDisplayName.isEmpty()); - - PartialLocationKey key; - key.tzID = ZoneMeta::findTimeZoneID(tzCanonicalID); - key.mzID = ZoneMeta::findMetaZoneID(mzID); - key.isLong = isLong; - U_ASSERT(key.tzID != NULL && key.mzID != NULL); - - const UChar* uplname = (const UChar*)uhash_get(fPartialLocationNamesMap, (void *)&key); - if (uplname != NULL) { - return uplname; - } - - UnicodeString location; - UnicodeString usCountryCode; - ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode); - if (!usCountryCode.isEmpty()) { - char countryCode[ULOC_COUNTRY_CAPACITY]; - U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY); - int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV); - countryCode[ccLen] = 0; - - UnicodeString regionalGolden; - fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden); - if (tzCanonicalID == regionalGolden) { - // Use country name - fLocaleDisplayNames->regionDisplayName(countryCode, location); - } else { - // Otherwise, use exemplar city name - fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location); - } - } else { - fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location); - if (location.isEmpty()) { - // This could happen when the time zone is not associated with a country, - // and its ID is not hierarchical, for example, CST6CDT. - // We use the canonical ID itself as the location for this case. - location.setTo(tzCanonicalID); - } - } - - UErrorCode status = U_ZERO_ERROR; - UnicodeString name; - fFallbackFormat.format(location, mzDisplayName, name, status); - if (U_FAILURE(status)) { - return NULL; - } - - uplname = fStringPool.get(name, status); - if (U_SUCCESS(status)) { - // Add the name to cache - PartialLocationKey* cacheKey = (PartialLocationKey *)uprv_malloc(sizeof(PartialLocationKey)); - if (cacheKey != NULL) { - cacheKey->tzID = key.tzID; - cacheKey->mzID = key.mzID; - cacheKey->isLong = key.isLong; - uhash_put(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status); - if (U_FAILURE(status)) { - uprv_free(cacheKey); - } else { - // put the name to the local trie as well - GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo)); - if (nameinfo != NULL) { - nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT; - nameinfo->tzID = key.tzID; - fGNamesTrie.put(uplname, nameinfo, status); - } - } - } - } - return uplname; -} - -/* - * This method updates the cache and must be called with a lock, - * except initializer. - */ -void -TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) { - // load the generic location name - getGenericLocationName(tzCanonicalID); - - // partial location names - UErrorCode status = U_ZERO_ERROR; - - const UnicodeString *mzID; - UnicodeString goldenID; - UnicodeString mzGenName; - UTimeZoneNameType genNonLocTypes[] = { - UTZNM_LONG_GENERIC, UTZNM_SHORT_GENERIC, - UTZNM_UNKNOWN /*terminator*/ - }; - - StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status); - while ((mzID = mzIDs->snext(status)) != NULL) { - if (U_FAILURE(status)) { - break; - } - // if this time zone is not the golden zone of the meta zone, - // partial location name (such as "PT (Los Angeles)") might be - // available. - fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion, goldenID); - if (tzCanonicalID != goldenID) { - for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) { - fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName); - if (!mzGenName.isEmpty()) { - // getPartialLocationName formats a name and put it into the trie - getPartialLocationName(tzCanonicalID, *mzID, - (genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName); - } - } - } - } - if (mzIDs != NULL) { - delete mzIDs; - } -} - -int32_t -TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, - UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const { - timeType = UTZFMT_TIME_TYPE_UNKNOWN; - tzID.setToBogus(); - - if (U_FAILURE(status)) { - return 0; - } - - // Find matches in the TimeZoneNames first - TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status); - if (U_FAILURE(status)) { - return 0; - } - - int32_t bestMatchLen = 0; - UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; - UnicodeString bestMatchTzID; - // UBool isLongStandard = FALSE; // workaround - see the comments below - UBool isStandard = FALSE; // TODO: Temporary hack (on hack) for short standard name/location name conflict (found in zh_Hant), should be removed after CLDR 21m1 integration - - if (tznamesMatches != NULL) { - UnicodeString mzID; - for (int32_t i = 0; i < tznamesMatches->size(); i++) { - int32_t len = tznamesMatches->getMatchLengthAt(i); - if (len > bestMatchLen) { - bestMatchLen = len; - if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) { - // name for a meta zone - if (tznamesMatches->getMetaZoneIDAt(i, mzID)) { - fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, bestMatchTzID); - } - } - UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i); - if (U_FAILURE(status)) { - break; - } - switch (nameType) { - case UTZNM_LONG_STANDARD: - // isLongStandard = TRUE; - case UTZNM_SHORT_STANDARD: // this one is never used for generic, but just in case - isStandard = TRUE; // TODO: Remove this later, see the comments above. - bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD; - break; - case UTZNM_LONG_DAYLIGHT: - case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case - bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT; - break; - default: - bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; - } - } - } - delete tznamesMatches; - if (U_FAILURE(status)) { - return 0; - } - - if (bestMatchLen == (text.length() - start)) { - // Full match - - //tzID.setTo(bestMatchTzID); - //timeType = bestMatchTimeType; - //return bestMatchLen; - - // TODO Some time zone uses a same name for the long standard name - // and the location name. When the match is a long standard name, - // then we need to check if the name is same with the location name. - // This is probably a data error or a design bug. -/* - if (!isLongStandard) { - tzID.setTo(bestMatchTzID); - timeType = bestMatchTimeType; - return bestMatchLen; - } -*/ - // TODO The deprecation of commonlyUsed flag introduced the name - // conflict not only for long standard names, but short standard names too. - // These short names (found in zh_Hant) should be gone once we clean - // up CLDR time zone display name data. Once the short name conflict - // problem (with location name) is resolved, we should change the condition - // below back to the original one above. -Yoshito (2011-09-14) - if (!isStandard) { - tzID.setTo(bestMatchTzID); - timeType = bestMatchTimeType; - return bestMatchLen; - } - } - } - - // Find matches in the local trie - TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status); - if (U_FAILURE(status)) { - return 0; - } - if (localMatches != NULL) { - for (int32_t i = 0; i < localMatches->size(); i++) { - int32_t len = localMatches->getMatchLength(i); - - // TODO See the above TODO. We use len >= bestMatchLen - // because of the long standard/location name collision - // problem. If it is also a location name, carrying - // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a - // problem in SimpleDateFormat - if (len >= bestMatchLen) { - bestMatchLen = localMatches->getMatchLength(i); - bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; // because generic - localMatches->getTimeZoneID(i, bestMatchTzID); - } - } - delete localMatches; - } - - if (bestMatchLen > 0) { - timeType = bestMatchTimeType; - tzID.setTo(bestMatchTzID); - } - return bestMatchLen; -} - -TimeZoneGenericNameMatchInfo* -TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { - GNameSearchHandler handler(types); - - TZGNCore *nonConstThis = const_cast<TZGNCore *>(this); - - umtx_lock(&gLock); - { - fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); - } - umtx_unlock(&gLock); - - if (U_FAILURE(status)) { - return NULL; - } - - TimeZoneGenericNameMatchInfo *gmatchInfo = NULL; - - int32_t maxLen = 0; - UVector *results = handler.getMatches(maxLen); - if (results != NULL && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) { - // perfect match - gmatchInfo = new TimeZoneGenericNameMatchInfo(results); - if (gmatchInfo == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - delete results; - return NULL; - } - return gmatchInfo; - } - - if (results != NULL) { - delete results; - } - - // All names are not yet loaded into the local trie. - // Load all available names into the trie. This could be very heavy. - umtx_lock(&gLock); - { - if (!fGNamesTrieFullyLoaded) { - StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status); - if (U_SUCCESS(status)) { - const UnicodeString *tzID; - while ((tzID = tzIDs->snext(status)) != NULL) { - if (U_FAILURE(status)) { - break; - } - nonConstThis->loadStrings(*tzID); - } - } - if (tzIDs != NULL) { - delete tzIDs; - } - - if (U_SUCCESS(status)) { - nonConstThis->fGNamesTrieFullyLoaded = TRUE; - } - } - } - umtx_unlock(&gLock); - - if (U_FAILURE(status)) { - return NULL; - } - - umtx_lock(&gLock); - { - // now try it again - fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); - } - umtx_unlock(&gLock); - - results = handler.getMatches(maxLen); - if (results != NULL && maxLen > 0) { - gmatchInfo = new TimeZoneGenericNameMatchInfo(results); - if (gmatchInfo == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - delete results; - return NULL; - } - } - - return gmatchInfo; -} - -TimeZoneNames::MatchInfoCollection* -TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { - // Check if the target name typs is really in the TimeZoneNames - uint32_t nameTypes = 0; - if (types & UTZGNM_LONG) { - nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD); - } - if (types & UTZGNM_SHORT) { - nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD); - } - - if (types) { - // Find matches in the TimeZoneNames - return fTimeZoneNames->find(text, start, nameTypes, status); - } - - return NULL; -} - -typedef struct TZGNCoreRef { - TZGNCore* obj; - int32_t refCount; - double lastAccess; -} TZGNCoreRef; - -// TZGNCore object cache handling -static UMutex gTZGNLock = U_MUTEX_INITIALIZER; -static UHashtable *gTZGNCoreCache = NULL; -static UBool gTZGNCoreCacheInitialized = FALSE; - -// Access count - incremented every time up to SWEEP_INTERVAL, -// then reset to 0 -static int32_t gAccessCount = 0; - -// Interval for calling the cache sweep function - every 100 times -#define SWEEP_INTERVAL 100 - -// Cache expiration in millisecond. When a cached entry is no -// longer referenced and exceeding this threshold since last -// access time, then the cache entry will be deleted by the sweep -// function. For now, 3 minutes. -#define CACHE_EXPIRATION 180000.0 - -U_CDECL_BEGIN -/** - * Cleanup callback func - */ -static UBool U_CALLCONV tzgnCore_cleanup(void) -{ - if (gTZGNCoreCache != NULL) { - uhash_close(gTZGNCoreCache); - gTZGNCoreCache = NULL; - } - gTZGNCoreCacheInitialized = FALSE; - return TRUE; -} - -/** - * Deleter for TZGNCoreRef - */ -static void U_CALLCONV -deleteTZGNCoreRef(void *obj) { - icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj; - delete (icu::TZGNCore*) entry->obj; - uprv_free(entry); -} -U_CDECL_END - -/** - * Function used for removing unreferrenced cache entries exceeding - * the expiration time. This function must be called with in the mutex - * block. - */ -static void sweepCache() { - int32_t pos = UHASH_FIRST; - const UHashElement* elem; - double now = (double)uprv_getUTCtime(); - - while ((elem = uhash_nextElement(gTZGNCoreCache, &pos)) != NULL) { - TZGNCoreRef *entry = (TZGNCoreRef *)elem->value.pointer; - if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) { - // delete this entry - uhash_removeElement(gTZGNCoreCache, elem); - } - } -} - -TimeZoneGenericNames::TimeZoneGenericNames() -: fRef(0) { -} - -TimeZoneGenericNames::~TimeZoneGenericNames() { - umtx_lock(&gTZGNLock); - { - U_ASSERT(fRef->refCount > 0); - // Just decrement the reference count - fRef->refCount--; - } - umtx_unlock(&gTZGNLock); -} - -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; - } - - TZGNCoreRef *cacheEntry = NULL; - { - Mutex lock(&gTZGNLock); - - if (!gTZGNCoreCacheInitialized) { - // Create empty hashtable - 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); - } - } - if (U_FAILURE(status)) { - return NULL; - } - - // Check the cache, if not available, create new one and cache - 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; - } - } // End of mutex locked block - - if (cacheEntry == NULL) { - delete instance; - return NULL; - } - - instance->fRef = cacheEntry; - return instance; -} - -UBool -TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const { - // Just compare if the other object also use the same - // ref entry - return fRef == other.fRef; -} - -TimeZoneGenericNames* -TimeZoneGenericNames::clone() const { - TimeZoneGenericNames* other = new TimeZoneGenericNames(); - if (other) { - umtx_lock(&gTZGNLock); - { - // Just increments the reference count - fRef->refCount++; - other->fRef = fRef; - } - umtx_unlock(&gTZGNLock); - } - return other; -} - -UnicodeString& -TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, - UDate date, UnicodeString& name) const { - return fRef->obj->getDisplayName(tz, type, date, name); -} - -UnicodeString& -TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const { - return fRef->obj->getGenericLocationName(tzCanonicalID, name); -} - -int32_t -TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, - UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const { - return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status); -} - -U_NAMESPACE_END -#endif |