1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/tzgnames.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1349 @@ 1.4 +/* 1.5 +******************************************************************************* 1.6 +* Copyright (C) 2011-2013, International Business Machines Corporation and 1.7 +* others. All Rights Reserved. 1.8 +******************************************************************************* 1.9 +*/ 1.10 + 1.11 +#include "unicode/utypes.h" 1.12 + 1.13 +#if !UCONFIG_NO_FORMATTING 1.14 + 1.15 +#include "tzgnames.h" 1.16 + 1.17 +#include "unicode/basictz.h" 1.18 +#include "unicode/locdspnm.h" 1.19 +#include "unicode/msgfmt.h" 1.20 +#include "unicode/rbtz.h" 1.21 +#include "unicode/simpletz.h" 1.22 +#include "unicode/vtzone.h" 1.23 + 1.24 +#include "cmemory.h" 1.25 +#include "cstring.h" 1.26 +#include "mutex.h" 1.27 +#include "uhash.h" 1.28 +#include "uassert.h" 1.29 +#include "umutex.h" 1.30 +#include "uresimp.h" 1.31 +#include "ureslocs.h" 1.32 +#include "zonemeta.h" 1.33 +#include "tznames_impl.h" 1.34 +#include "olsontz.h" 1.35 +#include "ucln_in.h" 1.36 + 1.37 +U_NAMESPACE_BEGIN 1.38 + 1.39 +#define ZID_KEY_MAX 128 1.40 + 1.41 +static const char gZoneStrings[] = "zoneStrings"; 1.42 + 1.43 +static const char gRegionFormatTag[] = "regionFormat"; 1.44 +static const char gFallbackFormatTag[] = "fallbackFormat"; 1.45 + 1.46 +static const UChar gEmpty[] = {0x00}; 1.47 + 1.48 +static const UChar gDefRegionPattern[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}" 1.49 +static const UChar gDefFallbackPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})" 1.50 + 1.51 +static const double kDstCheckRange = (double)184*U_MILLIS_PER_DAY; 1.52 + 1.53 + 1.54 + 1.55 +U_CDECL_BEGIN 1.56 + 1.57 +typedef struct PartialLocationKey { 1.58 + const UChar* tzID; 1.59 + const UChar* mzID; 1.60 + UBool isLong; 1.61 +} PartialLocationKey; 1.62 + 1.63 +/** 1.64 + * Hash function for partial location name hash key 1.65 + */ 1.66 +static int32_t U_CALLCONV 1.67 +hashPartialLocationKey(const UHashTok key) { 1.68 + // <tzID>&<mzID>#[L|S] 1.69 + PartialLocationKey *p = (PartialLocationKey *)key.pointer; 1.70 + UnicodeString str(p->tzID); 1.71 + str.append((UChar)0x26) 1.72 + .append(p->mzID, -1) 1.73 + .append((UChar)0x23) 1.74 + .append((UChar)(p->isLong ? 0x4C : 0x53)); 1.75 + return str.hashCode(); 1.76 +} 1.77 + 1.78 +/** 1.79 + * Comparer for partial location name hash key 1.80 + */ 1.81 +static UBool U_CALLCONV 1.82 +comparePartialLocationKey(const UHashTok key1, const UHashTok key2) { 1.83 + PartialLocationKey *p1 = (PartialLocationKey *)key1.pointer; 1.84 + PartialLocationKey *p2 = (PartialLocationKey *)key2.pointer; 1.85 + 1.86 + if (p1 == p2) { 1.87 + return TRUE; 1.88 + } 1.89 + if (p1 == NULL || p2 == NULL) { 1.90 + return FALSE; 1.91 + } 1.92 + // We just check identity of tzID/mzID 1.93 + return (p1->tzID == p2->tzID && p1->mzID == p2->mzID && p1->isLong == p2->isLong); 1.94 +} 1.95 + 1.96 +/** 1.97 + * Deleter for GNameInfo 1.98 + */ 1.99 +static void U_CALLCONV 1.100 +deleteGNameInfo(void *obj) { 1.101 + uprv_free(obj); 1.102 +} 1.103 + 1.104 +/** 1.105 + * GNameInfo stores zone name information in the local trie 1.106 + */ 1.107 +typedef struct GNameInfo { 1.108 + UTimeZoneGenericNameType type; 1.109 + const UChar* tzID; 1.110 +} ZNameInfo; 1.111 + 1.112 +/** 1.113 + * GMatchInfo stores zone name match information used by find method 1.114 + */ 1.115 +typedef struct GMatchInfo { 1.116 + const GNameInfo* gnameInfo; 1.117 + int32_t matchLength; 1.118 + UTimeZoneFormatTimeType timeType; 1.119 +} ZMatchInfo; 1.120 + 1.121 +U_CDECL_END 1.122 + 1.123 +// --------------------------------------------------- 1.124 +// The class stores time zone generic name match information 1.125 +// --------------------------------------------------- 1.126 +class TimeZoneGenericNameMatchInfo : public UMemory { 1.127 +public: 1.128 + TimeZoneGenericNameMatchInfo(UVector* matches); 1.129 + ~TimeZoneGenericNameMatchInfo(); 1.130 + 1.131 + int32_t size() const; 1.132 + UTimeZoneGenericNameType getGenericNameType(int32_t index) const; 1.133 + int32_t getMatchLength(int32_t index) const; 1.134 + UnicodeString& getTimeZoneID(int32_t index, UnicodeString& tzID) const; 1.135 + 1.136 +private: 1.137 + UVector* fMatches; // vector of MatchEntry 1.138 +}; 1.139 + 1.140 +TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches) 1.141 +: fMatches(matches) { 1.142 +} 1.143 + 1.144 +TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() { 1.145 + if (fMatches != NULL) { 1.146 + delete fMatches; 1.147 + } 1.148 +} 1.149 + 1.150 +int32_t 1.151 +TimeZoneGenericNameMatchInfo::size() const { 1.152 + if (fMatches == NULL) { 1.153 + return 0; 1.154 + } 1.155 + return fMatches->size(); 1.156 +} 1.157 + 1.158 +UTimeZoneGenericNameType 1.159 +TimeZoneGenericNameMatchInfo::getGenericNameType(int32_t index) const { 1.160 + GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index); 1.161 + if (minfo != NULL) { 1.162 + return static_cast<UTimeZoneGenericNameType>(minfo->gnameInfo->type); 1.163 + } 1.164 + return UTZGNM_UNKNOWN; 1.165 +} 1.166 + 1.167 +int32_t 1.168 +TimeZoneGenericNameMatchInfo::getMatchLength(int32_t index) const { 1.169 + ZMatchInfo *minfo = (ZMatchInfo *)fMatches->elementAt(index); 1.170 + if (minfo != NULL) { 1.171 + return minfo->matchLength; 1.172 + } 1.173 + return -1; 1.174 +} 1.175 + 1.176 +UnicodeString& 1.177 +TimeZoneGenericNameMatchInfo::getTimeZoneID(int32_t index, UnicodeString& tzID) const { 1.178 + GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index); 1.179 + if (minfo != NULL && minfo->gnameInfo->tzID != NULL) { 1.180 + tzID.setTo(TRUE, minfo->gnameInfo->tzID, -1); 1.181 + } else { 1.182 + tzID.setToBogus(); 1.183 + } 1.184 + return tzID; 1.185 +} 1.186 + 1.187 +// --------------------------------------------------- 1.188 +// GNameSearchHandler 1.189 +// --------------------------------------------------- 1.190 +class GNameSearchHandler : public TextTrieMapSearchResultHandler { 1.191 +public: 1.192 + GNameSearchHandler(uint32_t types); 1.193 + virtual ~GNameSearchHandler(); 1.194 + 1.195 + UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status); 1.196 + UVector* getMatches(int32_t& maxMatchLen); 1.197 + 1.198 +private: 1.199 + uint32_t fTypes; 1.200 + UVector* fResults; 1.201 + int32_t fMaxMatchLen; 1.202 +}; 1.203 + 1.204 +GNameSearchHandler::GNameSearchHandler(uint32_t types) 1.205 +: fTypes(types), fResults(NULL), fMaxMatchLen(0) { 1.206 +} 1.207 + 1.208 +GNameSearchHandler::~GNameSearchHandler() { 1.209 + if (fResults != NULL) { 1.210 + delete fResults; 1.211 + } 1.212 +} 1.213 + 1.214 +UBool 1.215 +GNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) { 1.216 + if (U_FAILURE(status)) { 1.217 + return FALSE; 1.218 + } 1.219 + if (node->hasValues()) { 1.220 + int32_t valuesCount = node->countValues(); 1.221 + for (int32_t i = 0; i < valuesCount; i++) { 1.222 + GNameInfo *nameinfo = (ZNameInfo *)node->getValue(i); 1.223 + if (nameinfo == NULL) { 1.224 + break; 1.225 + } 1.226 + if ((nameinfo->type & fTypes) != 0) { 1.227 + // matches a requested type 1.228 + if (fResults == NULL) { 1.229 + fResults = new UVector(uprv_free, NULL, status); 1.230 + if (fResults == NULL) { 1.231 + status = U_MEMORY_ALLOCATION_ERROR; 1.232 + } 1.233 + } 1.234 + if (U_SUCCESS(status)) { 1.235 + U_ASSERT(fResults != NULL); 1.236 + GMatchInfo *gmatch = (GMatchInfo *)uprv_malloc(sizeof(GMatchInfo)); 1.237 + if (gmatch == NULL) { 1.238 + status = U_MEMORY_ALLOCATION_ERROR; 1.239 + } else { 1.240 + // add the match to the vector 1.241 + gmatch->gnameInfo = nameinfo; 1.242 + gmatch->matchLength = matchLength; 1.243 + gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN; 1.244 + fResults->addElement(gmatch, status); 1.245 + if (U_FAILURE(status)) { 1.246 + uprv_free(gmatch); 1.247 + } else { 1.248 + if (matchLength > fMaxMatchLen) { 1.249 + fMaxMatchLen = matchLength; 1.250 + } 1.251 + } 1.252 + } 1.253 + } 1.254 + } 1.255 + } 1.256 + } 1.257 + return TRUE; 1.258 +} 1.259 + 1.260 +UVector* 1.261 +GNameSearchHandler::getMatches(int32_t& maxMatchLen) { 1.262 + // give the ownership to the caller 1.263 + UVector *results = fResults; 1.264 + maxMatchLen = fMaxMatchLen; 1.265 + 1.266 + // reset 1.267 + fResults = NULL; 1.268 + fMaxMatchLen = 0; 1.269 + return results; 1.270 +} 1.271 + 1.272 +static UMutex gLock = U_MUTEX_INITIALIZER; 1.273 + 1.274 +class TZGNCore : public UMemory { 1.275 +public: 1.276 + TZGNCore(const Locale& locale, UErrorCode& status); 1.277 + virtual ~TZGNCore(); 1.278 + 1.279 + UnicodeString& getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, 1.280 + UDate date, UnicodeString& name) const; 1.281 + 1.282 + UnicodeString& getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const; 1.283 + 1.284 + int32_t findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, 1.285 + UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const; 1.286 + 1.287 +private: 1.288 + Locale fLocale; 1.289 + const TimeZoneNames* fTimeZoneNames; 1.290 + UHashtable* fLocationNamesMap; 1.291 + UHashtable* fPartialLocationNamesMap; 1.292 + 1.293 + MessageFormat* fRegionFormat; 1.294 + MessageFormat* fFallbackFormat; 1.295 + 1.296 + LocaleDisplayNames* fLocaleDisplayNames; 1.297 + ZNStringPool fStringPool; 1.298 + 1.299 + TextTrieMap fGNamesTrie; 1.300 + UBool fGNamesTrieFullyLoaded; 1.301 + 1.302 + char fTargetRegion[ULOC_COUNTRY_CAPACITY]; 1.303 + 1.304 + void initialize(const Locale& locale, UErrorCode& status); 1.305 + void cleanup(); 1.306 + 1.307 + void loadStrings(const UnicodeString& tzCanonicalID); 1.308 + 1.309 + const UChar* getGenericLocationName(const UnicodeString& tzCanonicalID); 1.310 + 1.311 + UnicodeString& formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, 1.312 + UDate date, UnicodeString& name) const; 1.313 + 1.314 + UnicodeString& getPartialLocationName(const UnicodeString& tzCanonicalID, 1.315 + const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName, 1.316 + UnicodeString& name) const; 1.317 + 1.318 + const UChar* getPartialLocationName(const UnicodeString& tzCanonicalID, 1.319 + const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName); 1.320 + 1.321 + TimeZoneGenericNameMatchInfo* findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const; 1.322 + 1.323 + TimeZoneNames::MatchInfoCollection* findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const; 1.324 +}; 1.325 + 1.326 + 1.327 +// --------------------------------------------------- 1.328 +// TZGNCore - core implmentation of TimeZoneGenericNames 1.329 +// 1.330 +// TimeZoneGenericNames is parallel to TimeZoneNames, 1.331 +// but handles run-time generated time zone names. 1.332 +// This is the main part of this module. 1.333 +// --------------------------------------------------- 1.334 +TZGNCore::TZGNCore(const Locale& locale, UErrorCode& status) 1.335 +: fLocale(locale), 1.336 + fTimeZoneNames(NULL), 1.337 + fLocationNamesMap(NULL), 1.338 + fPartialLocationNamesMap(NULL), 1.339 + fRegionFormat(NULL), 1.340 + fFallbackFormat(NULL), 1.341 + fLocaleDisplayNames(NULL), 1.342 + fStringPool(status), 1.343 + fGNamesTrie(TRUE, deleteGNameInfo), 1.344 + fGNamesTrieFullyLoaded(FALSE) { 1.345 + initialize(locale, status); 1.346 +} 1.347 + 1.348 +TZGNCore::~TZGNCore() { 1.349 + cleanup(); 1.350 +} 1.351 + 1.352 +void 1.353 +TZGNCore::initialize(const Locale& locale, UErrorCode& status) { 1.354 + if (U_FAILURE(status)) { 1.355 + return; 1.356 + } 1.357 + 1.358 + // TimeZoneNames 1.359 + fTimeZoneNames = TimeZoneNames::createInstance(locale, status); 1.360 + if (U_FAILURE(status)) { 1.361 + return; 1.362 + } 1.363 + 1.364 + // Initialize format patterns 1.365 + UnicodeString rpat(TRUE, gDefRegionPattern, -1); 1.366 + UnicodeString fpat(TRUE, gDefFallbackPattern, -1); 1.367 + 1.368 + UErrorCode tmpsts = U_ZERO_ERROR; // OK with fallback warning.. 1.369 + UResourceBundle *zoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts); 1.370 + zoneStrings = ures_getByKeyWithFallback(zoneStrings, gZoneStrings, zoneStrings, &tmpsts); 1.371 + 1.372 + if (U_SUCCESS(tmpsts)) { 1.373 + const UChar *regionPattern = ures_getStringByKeyWithFallback(zoneStrings, gRegionFormatTag, NULL, &tmpsts); 1.374 + if (U_SUCCESS(tmpsts) && u_strlen(regionPattern) > 0) { 1.375 + rpat.setTo(regionPattern, -1); 1.376 + } 1.377 + tmpsts = U_ZERO_ERROR; 1.378 + const UChar *fallbackPattern = ures_getStringByKeyWithFallback(zoneStrings, gFallbackFormatTag, NULL, &tmpsts); 1.379 + if (U_SUCCESS(tmpsts) && u_strlen(fallbackPattern) > 0) { 1.380 + fpat.setTo(fallbackPattern, -1); 1.381 + } 1.382 + } 1.383 + ures_close(zoneStrings); 1.384 + 1.385 + fRegionFormat = new MessageFormat(rpat, status); 1.386 + if (fRegionFormat == NULL) { 1.387 + status = U_MEMORY_ALLOCATION_ERROR; 1.388 + } 1.389 + fFallbackFormat = new MessageFormat(fpat, status); 1.390 + if (fFallbackFormat == NULL) { 1.391 + status = U_MEMORY_ALLOCATION_ERROR; 1.392 + } 1.393 + if (U_FAILURE(status)) { 1.394 + cleanup(); 1.395 + return; 1.396 + } 1.397 + 1.398 + // locale display names 1.399 + fLocaleDisplayNames = LocaleDisplayNames::createInstance(locale); 1.400 + 1.401 + // hash table for names - no key/value deleters 1.402 + fLocationNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); 1.403 + if (U_FAILURE(status)) { 1.404 + cleanup(); 1.405 + return; 1.406 + } 1.407 + 1.408 + fPartialLocationNamesMap = uhash_open(hashPartialLocationKey, comparePartialLocationKey, NULL, &status); 1.409 + if (U_FAILURE(status)) { 1.410 + cleanup(); 1.411 + return; 1.412 + } 1.413 + uhash_setKeyDeleter(fPartialLocationNamesMap, uprv_free); 1.414 + // no value deleter 1.415 + 1.416 + // target region 1.417 + const char* region = fLocale.getCountry(); 1.418 + int32_t regionLen = uprv_strlen(region); 1.419 + if (regionLen == 0) { 1.420 + char loc[ULOC_FULLNAME_CAPACITY]; 1.421 + uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status); 1.422 + 1.423 + regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status); 1.424 + if (U_SUCCESS(status)) { 1.425 + fTargetRegion[regionLen] = 0; 1.426 + } else { 1.427 + cleanup(); 1.428 + return; 1.429 + } 1.430 + } else if (regionLen < (int32_t)sizeof(fTargetRegion)) { 1.431 + uprv_strcpy(fTargetRegion, region); 1.432 + } else { 1.433 + fTargetRegion[0] = 0; 1.434 + } 1.435 + 1.436 + // preload generic names for the default zone 1.437 + TimeZone *tz = TimeZone::createDefault(); 1.438 + const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz); 1.439 + if (tzID != NULL) { 1.440 + loadStrings(UnicodeString(tzID)); 1.441 + } 1.442 + delete tz; 1.443 +} 1.444 + 1.445 +void 1.446 +TZGNCore::cleanup() { 1.447 + if (fRegionFormat != NULL) { 1.448 + delete fRegionFormat; 1.449 + } 1.450 + if (fFallbackFormat != NULL) { 1.451 + delete fFallbackFormat; 1.452 + } 1.453 + if (fLocaleDisplayNames != NULL) { 1.454 + delete fLocaleDisplayNames; 1.455 + } 1.456 + if (fTimeZoneNames != NULL) { 1.457 + delete fTimeZoneNames; 1.458 + } 1.459 + 1.460 + uhash_close(fLocationNamesMap); 1.461 + uhash_close(fPartialLocationNamesMap); 1.462 +} 1.463 + 1.464 + 1.465 +UnicodeString& 1.466 +TZGNCore::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const { 1.467 + name.setToBogus(); 1.468 + switch (type) { 1.469 + case UTZGNM_LOCATION: 1.470 + { 1.471 + const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz); 1.472 + if (tzCanonicalID != NULL) { 1.473 + getGenericLocationName(UnicodeString(tzCanonicalID), name); 1.474 + } 1.475 + } 1.476 + break; 1.477 + case UTZGNM_LONG: 1.478 + case UTZGNM_SHORT: 1.479 + formatGenericNonLocationName(tz, type, date, name); 1.480 + if (name.isEmpty()) { 1.481 + const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz); 1.482 + if (tzCanonicalID != NULL) { 1.483 + getGenericLocationName(UnicodeString(tzCanonicalID), name); 1.484 + } 1.485 + } 1.486 + break; 1.487 + default: 1.488 + break; 1.489 + } 1.490 + return name; 1.491 +} 1.492 + 1.493 +UnicodeString& 1.494 +TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const { 1.495 + if (tzCanonicalID.isEmpty()) { 1.496 + name.setToBogus(); 1.497 + return name; 1.498 + } 1.499 + 1.500 + const UChar *locname = NULL; 1.501 + TZGNCore *nonConstThis = const_cast<TZGNCore *>(this); 1.502 + umtx_lock(&gLock); 1.503 + { 1.504 + locname = nonConstThis->getGenericLocationName(tzCanonicalID); 1.505 + } 1.506 + umtx_unlock(&gLock); 1.507 + 1.508 + if (locname == NULL) { 1.509 + name.setToBogus(); 1.510 + } else { 1.511 + name.setTo(locname, u_strlen(locname)); 1.512 + } 1.513 + 1.514 + return name; 1.515 +} 1.516 + 1.517 +/* 1.518 + * This method updates the cache and must be called with a lock 1.519 + */ 1.520 +const UChar* 1.521 +TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID) { 1.522 + U_ASSERT(!tzCanonicalID.isEmpty()); 1.523 + if (tzCanonicalID.length() > ZID_KEY_MAX) { 1.524 + return NULL; 1.525 + } 1.526 + 1.527 + UErrorCode status = U_ZERO_ERROR; 1.528 + UChar tzIDKey[ZID_KEY_MAX + 1]; 1.529 + int32_t tzIDKeyLen = tzCanonicalID.extract(tzIDKey, ZID_KEY_MAX + 1, status); 1.530 + U_ASSERT(status == U_ZERO_ERROR); // already checked length above 1.531 + tzIDKey[tzIDKeyLen] = 0; 1.532 + 1.533 + const UChar *locname = (const UChar *)uhash_get(fLocationNamesMap, tzIDKey); 1.534 + 1.535 + if (locname != NULL) { 1.536 + // gEmpty indicate the name is not available 1.537 + if (locname == gEmpty) { 1.538 + return NULL; 1.539 + } 1.540 + return locname; 1.541 + } 1.542 + 1.543 + // Construct location name 1.544 + UnicodeString name; 1.545 + UnicodeString usCountryCode; 1.546 + UBool isPrimary = FALSE; 1.547 + 1.548 + ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode, &isPrimary); 1.549 + 1.550 + if (!usCountryCode.isEmpty()) { 1.551 + FieldPosition fpos; 1.552 + 1.553 + if (isPrimary) { 1.554 + // If this is the primary zone in the country, use the country name. 1.555 + char countryCode[ULOC_COUNTRY_CAPACITY]; 1.556 + U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY); 1.557 + int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV); 1.558 + countryCode[ccLen] = 0; 1.559 + 1.560 + UnicodeString country; 1.561 + fLocaleDisplayNames->regionDisplayName(countryCode, country); 1.562 + 1.563 + Formattable param[] = { 1.564 + Formattable(country) 1.565 + }; 1.566 + 1.567 + fRegionFormat->format(param, 1, name, fpos, status); 1.568 + } else { 1.569 + // If this is not the primary zone in the country, 1.570 + // use the exemplar city name. 1.571 + 1.572 + // getExemplarLocationName should retur non-empty string 1.573 + // if the time zone is associated with a region 1.574 + 1.575 + UnicodeString city; 1.576 + fTimeZoneNames->getExemplarLocationName(tzCanonicalID, city); 1.577 + 1.578 + Formattable param[] = { 1.579 + Formattable(city), 1.580 + }; 1.581 + 1.582 + fRegionFormat->format(param, 1, name, fpos, status); 1.583 + } 1.584 + if (U_FAILURE(status)) { 1.585 + return NULL; 1.586 + } 1.587 + } 1.588 + 1.589 + locname = name.isEmpty() ? NULL : fStringPool.get(name, status); 1.590 + if (U_SUCCESS(status)) { 1.591 + // Cache the result 1.592 + const UChar* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID); 1.593 + U_ASSERT(cacheID != NULL); 1.594 + if (locname == NULL) { 1.595 + // gEmpty to indicate - no location name available 1.596 + uhash_put(fLocationNamesMap, (void *)cacheID, (void *)gEmpty, &status); 1.597 + } else { 1.598 + uhash_put(fLocationNamesMap, (void *)cacheID, (void *)locname, &status); 1.599 + if (U_FAILURE(status)) { 1.600 + locname = NULL; 1.601 + } else { 1.602 + // put the name info into the trie 1.603 + GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo)); 1.604 + if (nameinfo != NULL) { 1.605 + nameinfo->type = UTZGNM_LOCATION; 1.606 + nameinfo->tzID = cacheID; 1.607 + fGNamesTrie.put(locname, nameinfo, status); 1.608 + } 1.609 + } 1.610 + } 1.611 + } 1.612 + 1.613 + return locname; 1.614 +} 1.615 + 1.616 +UnicodeString& 1.617 +TZGNCore::formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const { 1.618 + U_ASSERT(type == UTZGNM_LONG || type == UTZGNM_SHORT); 1.619 + name.setToBogus(); 1.620 + 1.621 + const UChar* uID = ZoneMeta::getCanonicalCLDRID(tz); 1.622 + if (uID == NULL) { 1.623 + return name; 1.624 + } 1.625 + 1.626 + UnicodeString tzID(uID); 1.627 + 1.628 + // Try to get a name from time zone first 1.629 + UTimeZoneNameType nameType = (type == UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC; 1.630 + fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name); 1.631 + 1.632 + if (!name.isEmpty()) { 1.633 + return name; 1.634 + } 1.635 + 1.636 + // Try meta zone 1.637 + UnicodeString mzID; 1.638 + fTimeZoneNames->getMetaZoneID(tzID, date, mzID); 1.639 + if (!mzID.isEmpty()) { 1.640 + UErrorCode status = U_ZERO_ERROR; 1.641 + UBool useStandard = FALSE; 1.642 + int32_t raw, sav; 1.643 + 1.644 + tz.getOffset(date, FALSE, raw, sav, status); 1.645 + if (U_FAILURE(status)) { 1.646 + return name; 1.647 + } 1.648 + 1.649 + if (sav == 0) { 1.650 + useStandard = TRUE; 1.651 + 1.652 + TimeZone *tmptz = tz.clone(); 1.653 + // Check if the zone actually uses daylight saving time around the time 1.654 + BasicTimeZone *btz = NULL; 1.655 + if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL 1.656 + || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL 1.657 + || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL 1.658 + || dynamic_cast<VTimeZone *>(tmptz) != NULL) { 1.659 + btz = (BasicTimeZone*)tmptz; 1.660 + } 1.661 + 1.662 + if (btz != NULL) { 1.663 + TimeZoneTransition before; 1.664 + UBool beforTrs = btz->getPreviousTransition(date, TRUE, before); 1.665 + if (beforTrs 1.666 + && (date - before.getTime() < kDstCheckRange) 1.667 + && before.getFrom()->getDSTSavings() != 0) { 1.668 + useStandard = FALSE; 1.669 + } else { 1.670 + TimeZoneTransition after; 1.671 + UBool afterTrs = btz->getNextTransition(date, FALSE, after); 1.672 + if (afterTrs 1.673 + && (after.getTime() - date < kDstCheckRange) 1.674 + && after.getTo()->getDSTSavings() != 0) { 1.675 + useStandard = FALSE; 1.676 + } 1.677 + } 1.678 + } else { 1.679 + // If not BasicTimeZone... only if the instance is not an ICU's implementation. 1.680 + // We may get a wrong answer in edge case, but it should practically work OK. 1.681 + tmptz->getOffset(date - kDstCheckRange, FALSE, raw, sav, status); 1.682 + if (sav != 0) { 1.683 + useStandard = FALSE; 1.684 + } else { 1.685 + tmptz->getOffset(date + kDstCheckRange, FALSE, raw, sav, status); 1.686 + if (sav != 0){ 1.687 + useStandard = FALSE; 1.688 + } 1.689 + } 1.690 + if (U_FAILURE(status)) { 1.691 + delete tmptz; 1.692 + return name; 1.693 + } 1.694 + } 1.695 + delete tmptz; 1.696 + } 1.697 + if (useStandard) { 1.698 + UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC) 1.699 + ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD; 1.700 + UnicodeString stdName; 1.701 + fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName); 1.702 + if (!stdName.isEmpty()) { 1.703 + name.setTo(stdName); 1.704 + 1.705 + // TODO: revisit this issue later 1.706 + // In CLDR, a same display name is used for both generic and standard 1.707 + // for some meta zones in some locales. This looks like a data bugs. 1.708 + // For now, we check if the standard name is different from its generic 1.709 + // name below. 1.710 + UnicodeString mzGenericName; 1.711 + fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName); 1.712 + if (stdName.caseCompare(mzGenericName, 0) == 0) { 1.713 + name.setToBogus(); 1.714 + } 1.715 + } 1.716 + } 1.717 + if (name.isEmpty()) { 1.718 + // Get a name from meta zone 1.719 + UnicodeString mzName; 1.720 + fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName); 1.721 + if (!mzName.isEmpty()) { 1.722 + // Check if we need to use a partial location format. 1.723 + // This check is done by comparing offset with the meta zone's 1.724 + // golden zone at the given date. 1.725 + UnicodeString goldenID; 1.726 + fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, goldenID); 1.727 + if (!goldenID.isEmpty() && goldenID != tzID) { 1.728 + TimeZone *goldenZone = TimeZone::createTimeZone(goldenID); 1.729 + int32_t raw1, sav1; 1.730 + 1.731 + // Check offset in the golden zone with wall time. 1.732 + // With getOffset(date, false, offsets1), 1.733 + // you may get incorrect results because of time overlap at DST->STD 1.734 + // transition. 1.735 + goldenZone->getOffset(date + raw + sav, TRUE, raw1, sav1, status); 1.736 + delete goldenZone; 1.737 + if (U_SUCCESS(status)) { 1.738 + if (raw != raw1 || sav != sav1) { 1.739 + // Now we need to use a partial location format 1.740 + getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name); 1.741 + } else { 1.742 + name.setTo(mzName); 1.743 + } 1.744 + } 1.745 + } else { 1.746 + name.setTo(mzName); 1.747 + } 1.748 + } 1.749 + } 1.750 + } 1.751 + return name; 1.752 +} 1.753 + 1.754 +UnicodeString& 1.755 +TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID, 1.756 + const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName, 1.757 + UnicodeString& name) const { 1.758 + name.setToBogus(); 1.759 + if (tzCanonicalID.isEmpty() || mzID.isEmpty() || mzDisplayName.isEmpty()) { 1.760 + return name; 1.761 + } 1.762 + 1.763 + const UChar *uplname = NULL; 1.764 + TZGNCore *nonConstThis = const_cast<TZGNCore *>(this); 1.765 + umtx_lock(&gLock); 1.766 + { 1.767 + uplname = nonConstThis->getPartialLocationName(tzCanonicalID, mzID, isLong, mzDisplayName); 1.768 + } 1.769 + umtx_unlock(&gLock); 1.770 + 1.771 + if (uplname == NULL) { 1.772 + name.setToBogus(); 1.773 + } else { 1.774 + name.setTo(TRUE, uplname, -1); 1.775 + } 1.776 + return name; 1.777 +} 1.778 + 1.779 +/* 1.780 + * This method updates the cache and must be called with a lock 1.781 + */ 1.782 +const UChar* 1.783 +TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID, 1.784 + const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) { 1.785 + U_ASSERT(!tzCanonicalID.isEmpty()); 1.786 + U_ASSERT(!mzID.isEmpty()); 1.787 + U_ASSERT(!mzDisplayName.isEmpty()); 1.788 + 1.789 + PartialLocationKey key; 1.790 + key.tzID = ZoneMeta::findTimeZoneID(tzCanonicalID); 1.791 + key.mzID = ZoneMeta::findMetaZoneID(mzID); 1.792 + key.isLong = isLong; 1.793 + U_ASSERT(key.tzID != NULL && key.mzID != NULL); 1.794 + 1.795 + const UChar* uplname = (const UChar*)uhash_get(fPartialLocationNamesMap, (void *)&key); 1.796 + if (uplname != NULL) { 1.797 + return uplname; 1.798 + } 1.799 + 1.800 + UnicodeString location; 1.801 + UnicodeString usCountryCode; 1.802 + ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode); 1.803 + if (!usCountryCode.isEmpty()) { 1.804 + char countryCode[ULOC_COUNTRY_CAPACITY]; 1.805 + U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY); 1.806 + int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV); 1.807 + countryCode[ccLen] = 0; 1.808 + 1.809 + UnicodeString regionalGolden; 1.810 + fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden); 1.811 + if (tzCanonicalID == regionalGolden) { 1.812 + // Use country name 1.813 + fLocaleDisplayNames->regionDisplayName(countryCode, location); 1.814 + } else { 1.815 + // Otherwise, use exemplar city name 1.816 + fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location); 1.817 + } 1.818 + } else { 1.819 + fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location); 1.820 + if (location.isEmpty()) { 1.821 + // This could happen when the time zone is not associated with a country, 1.822 + // and its ID is not hierarchical, for example, CST6CDT. 1.823 + // We use the canonical ID itself as the location for this case. 1.824 + location.setTo(tzCanonicalID); 1.825 + } 1.826 + } 1.827 + 1.828 + UErrorCode status = U_ZERO_ERROR; 1.829 + UnicodeString name; 1.830 + 1.831 + FieldPosition fpos; 1.832 + Formattable param[] = { 1.833 + Formattable(location), 1.834 + Formattable(mzDisplayName) 1.835 + }; 1.836 + fFallbackFormat->format(param, 2, name, fpos, status); 1.837 + if (U_FAILURE(status)) { 1.838 + return NULL; 1.839 + } 1.840 + 1.841 + uplname = fStringPool.get(name, status); 1.842 + if (U_SUCCESS(status)) { 1.843 + // Add the name to cache 1.844 + PartialLocationKey* cacheKey = (PartialLocationKey *)uprv_malloc(sizeof(PartialLocationKey)); 1.845 + if (cacheKey != NULL) { 1.846 + cacheKey->tzID = key.tzID; 1.847 + cacheKey->mzID = key.mzID; 1.848 + cacheKey->isLong = key.isLong; 1.849 + uhash_put(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status); 1.850 + if (U_FAILURE(status)) { 1.851 + uprv_free(cacheKey); 1.852 + } else { 1.853 + // put the name to the local trie as well 1.854 + GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo)); 1.855 + if (nameinfo != NULL) { 1.856 + nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT; 1.857 + nameinfo->tzID = key.tzID; 1.858 + fGNamesTrie.put(uplname, nameinfo, status); 1.859 + } 1.860 + } 1.861 + } 1.862 + } 1.863 + return uplname; 1.864 +} 1.865 + 1.866 +/* 1.867 + * This method updates the cache and must be called with a lock, 1.868 + * except initializer. 1.869 + */ 1.870 +void 1.871 +TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) { 1.872 + // load the generic location name 1.873 + getGenericLocationName(tzCanonicalID); 1.874 + 1.875 + // partial location names 1.876 + UErrorCode status = U_ZERO_ERROR; 1.877 + 1.878 + const UnicodeString *mzID; 1.879 + UnicodeString goldenID; 1.880 + UnicodeString mzGenName; 1.881 + UTimeZoneNameType genNonLocTypes[] = { 1.882 + UTZNM_LONG_GENERIC, UTZNM_SHORT_GENERIC, 1.883 + UTZNM_UNKNOWN /*terminator*/ 1.884 + }; 1.885 + 1.886 + StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status); 1.887 + while ((mzID = mzIDs->snext(status))) { 1.888 + if (U_FAILURE(status)) { 1.889 + break; 1.890 + } 1.891 + // if this time zone is not the golden zone of the meta zone, 1.892 + // partial location name (such as "PT (Los Angeles)") might be 1.893 + // available. 1.894 + fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion, goldenID); 1.895 + if (tzCanonicalID != goldenID) { 1.896 + for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) { 1.897 + fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName); 1.898 + if (!mzGenName.isEmpty()) { 1.899 + // getPartialLocationName formats a name and put it into the trie 1.900 + getPartialLocationName(tzCanonicalID, *mzID, 1.901 + (genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName); 1.902 + } 1.903 + } 1.904 + } 1.905 + } 1.906 + if (mzIDs != NULL) { 1.907 + delete mzIDs; 1.908 + } 1.909 +} 1.910 + 1.911 +int32_t 1.912 +TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, 1.913 + UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const { 1.914 + timeType = UTZFMT_TIME_TYPE_UNKNOWN; 1.915 + tzID.setToBogus(); 1.916 + 1.917 + if (U_FAILURE(status)) { 1.918 + return 0; 1.919 + } 1.920 + 1.921 + // Find matches in the TimeZoneNames first 1.922 + TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status); 1.923 + if (U_FAILURE(status)) { 1.924 + return 0; 1.925 + } 1.926 + 1.927 + int32_t bestMatchLen = 0; 1.928 + UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; 1.929 + UnicodeString bestMatchTzID; 1.930 + // UBool isLongStandard = FALSE; // workaround - see the comments below 1.931 + 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 1.932 + 1.933 + if (tznamesMatches != NULL) { 1.934 + UnicodeString mzID; 1.935 + for (int32_t i = 0; i < tznamesMatches->size(); i++) { 1.936 + int32_t len = tznamesMatches->getMatchLengthAt(i); 1.937 + if (len > bestMatchLen) { 1.938 + bestMatchLen = len; 1.939 + if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) { 1.940 + // name for a meta zone 1.941 + if (tznamesMatches->getMetaZoneIDAt(i, mzID)) { 1.942 + fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, bestMatchTzID); 1.943 + } 1.944 + } 1.945 + UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i); 1.946 + if (U_FAILURE(status)) { 1.947 + break; 1.948 + } 1.949 + switch (nameType) { 1.950 + case UTZNM_LONG_STANDARD: 1.951 + // isLongStandard = TRUE; 1.952 + case UTZNM_SHORT_STANDARD: // this one is never used for generic, but just in case 1.953 + isStandard = TRUE; // TODO: Remove this later, see the comments above. 1.954 + bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD; 1.955 + break; 1.956 + case UTZNM_LONG_DAYLIGHT: 1.957 + case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case 1.958 + bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT; 1.959 + break; 1.960 + default: 1.961 + bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; 1.962 + } 1.963 + } 1.964 + } 1.965 + delete tznamesMatches; 1.966 + if (U_FAILURE(status)) { 1.967 + return 0; 1.968 + } 1.969 + 1.970 + if (bestMatchLen == (text.length() - start)) { 1.971 + // Full match 1.972 + 1.973 + //tzID.setTo(bestMatchTzID); 1.974 + //timeType = bestMatchTimeType; 1.975 + //return bestMatchLen; 1.976 + 1.977 + // TODO Some time zone uses a same name for the long standard name 1.978 + // and the location name. When the match is a long standard name, 1.979 + // then we need to check if the name is same with the location name. 1.980 + // This is probably a data error or a design bug. 1.981 +/* 1.982 + if (!isLongStandard) { 1.983 + tzID.setTo(bestMatchTzID); 1.984 + timeType = bestMatchTimeType; 1.985 + return bestMatchLen; 1.986 + } 1.987 +*/ 1.988 + // TODO The deprecation of commonlyUsed flag introduced the name 1.989 + // conflict not only for long standard names, but short standard names too. 1.990 + // These short names (found in zh_Hant) should be gone once we clean 1.991 + // up CLDR time zone display name data. Once the short name conflict 1.992 + // problem (with location name) is resolved, we should change the condition 1.993 + // below back to the original one above. -Yoshito (2011-09-14) 1.994 + if (!isStandard) { 1.995 + tzID.setTo(bestMatchTzID); 1.996 + timeType = bestMatchTimeType; 1.997 + return bestMatchLen; 1.998 + } 1.999 + } 1.1000 + } 1.1001 + 1.1002 + // Find matches in the local trie 1.1003 + TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status); 1.1004 + if (U_FAILURE(status)) { 1.1005 + return 0; 1.1006 + } 1.1007 + if (localMatches != NULL) { 1.1008 + for (int32_t i = 0; i < localMatches->size(); i++) { 1.1009 + int32_t len = localMatches->getMatchLength(i); 1.1010 + 1.1011 + // TODO See the above TODO. We use len >= bestMatchLen 1.1012 + // because of the long standard/location name collision 1.1013 + // problem. If it is also a location name, carrying 1.1014 + // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a 1.1015 + // problem in SimpleDateFormat 1.1016 + if (len >= bestMatchLen) { 1.1017 + bestMatchLen = localMatches->getMatchLength(i); 1.1018 + bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; // because generic 1.1019 + localMatches->getTimeZoneID(i, bestMatchTzID); 1.1020 + } 1.1021 + } 1.1022 + delete localMatches; 1.1023 + } 1.1024 + 1.1025 + if (bestMatchLen > 0) { 1.1026 + timeType = bestMatchTimeType; 1.1027 + tzID.setTo(bestMatchTzID); 1.1028 + } 1.1029 + return bestMatchLen; 1.1030 +} 1.1031 + 1.1032 +TimeZoneGenericNameMatchInfo* 1.1033 +TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { 1.1034 + GNameSearchHandler handler(types); 1.1035 + 1.1036 + TZGNCore *nonConstThis = const_cast<TZGNCore *>(this); 1.1037 + 1.1038 + umtx_lock(&gLock); 1.1039 + { 1.1040 + fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); 1.1041 + } 1.1042 + umtx_unlock(&gLock); 1.1043 + 1.1044 + if (U_FAILURE(status)) { 1.1045 + return NULL; 1.1046 + } 1.1047 + 1.1048 + TimeZoneGenericNameMatchInfo *gmatchInfo = NULL; 1.1049 + 1.1050 + int32_t maxLen = 0; 1.1051 + UVector *results = handler.getMatches(maxLen); 1.1052 + if (results != NULL && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) { 1.1053 + // perfect match 1.1054 + gmatchInfo = new TimeZoneGenericNameMatchInfo(results); 1.1055 + if (gmatchInfo == NULL) { 1.1056 + status = U_MEMORY_ALLOCATION_ERROR; 1.1057 + delete results; 1.1058 + return NULL; 1.1059 + } 1.1060 + return gmatchInfo; 1.1061 + } 1.1062 + 1.1063 + if (results != NULL) { 1.1064 + delete results; 1.1065 + } 1.1066 + 1.1067 + // All names are not yet loaded into the local trie. 1.1068 + // Load all available names into the trie. This could be very heavy. 1.1069 + umtx_lock(&gLock); 1.1070 + { 1.1071 + if (!fGNamesTrieFullyLoaded) { 1.1072 + StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status); 1.1073 + if (U_SUCCESS(status)) { 1.1074 + const UnicodeString *tzID; 1.1075 + while ((tzID = tzIDs->snext(status))) { 1.1076 + if (U_FAILURE(status)) { 1.1077 + break; 1.1078 + } 1.1079 + nonConstThis->loadStrings(*tzID); 1.1080 + } 1.1081 + } 1.1082 + if (tzIDs != NULL) { 1.1083 + delete tzIDs; 1.1084 + } 1.1085 + 1.1086 + if (U_SUCCESS(status)) { 1.1087 + nonConstThis->fGNamesTrieFullyLoaded = TRUE; 1.1088 + } 1.1089 + } 1.1090 + } 1.1091 + umtx_unlock(&gLock); 1.1092 + 1.1093 + if (U_FAILURE(status)) { 1.1094 + return NULL; 1.1095 + } 1.1096 + 1.1097 + umtx_lock(&gLock); 1.1098 + { 1.1099 + // now try it again 1.1100 + fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); 1.1101 + } 1.1102 + umtx_unlock(&gLock); 1.1103 + 1.1104 + results = handler.getMatches(maxLen); 1.1105 + if (results != NULL && maxLen > 0) { 1.1106 + gmatchInfo = new TimeZoneGenericNameMatchInfo(results); 1.1107 + if (gmatchInfo == NULL) { 1.1108 + status = U_MEMORY_ALLOCATION_ERROR; 1.1109 + delete results; 1.1110 + return NULL; 1.1111 + } 1.1112 + } 1.1113 + 1.1114 + return gmatchInfo; 1.1115 +} 1.1116 + 1.1117 +TimeZoneNames::MatchInfoCollection* 1.1118 +TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { 1.1119 + // Check if the target name typs is really in the TimeZoneNames 1.1120 + uint32_t nameTypes = 0; 1.1121 + if (types & UTZGNM_LONG) { 1.1122 + nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD); 1.1123 + } 1.1124 + if (types & UTZGNM_SHORT) { 1.1125 + nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD); 1.1126 + } 1.1127 + 1.1128 + if (types) { 1.1129 + // Find matches in the TimeZoneNames 1.1130 + return fTimeZoneNames->find(text, start, nameTypes, status); 1.1131 + } 1.1132 + 1.1133 + return NULL; 1.1134 +} 1.1135 + 1.1136 +typedef struct TZGNCoreRef { 1.1137 + TZGNCore* obj; 1.1138 + int32_t refCount; 1.1139 + double lastAccess; 1.1140 +} TZGNCoreRef; 1.1141 + 1.1142 +// TZGNCore object cache handling 1.1143 +static UMutex gTZGNLock = U_MUTEX_INITIALIZER; 1.1144 +static UHashtable *gTZGNCoreCache = NULL; 1.1145 +static UBool gTZGNCoreCacheInitialized = FALSE; 1.1146 + 1.1147 +// Access count - incremented every time up to SWEEP_INTERVAL, 1.1148 +// then reset to 0 1.1149 +static int32_t gAccessCount = 0; 1.1150 + 1.1151 +// Interval for calling the cache sweep function - every 100 times 1.1152 +#define SWEEP_INTERVAL 100 1.1153 + 1.1154 +// Cache expiration in millisecond. When a cached entry is no 1.1155 +// longer referenced and exceeding this threshold since last 1.1156 +// access time, then the cache entry will be deleted by the sweep 1.1157 +// function. For now, 3 minutes. 1.1158 +#define CACHE_EXPIRATION 180000.0 1.1159 + 1.1160 +U_CDECL_BEGIN 1.1161 +/** 1.1162 + * Cleanup callback func 1.1163 + */ 1.1164 +static UBool U_CALLCONV tzgnCore_cleanup(void) 1.1165 +{ 1.1166 + if (gTZGNCoreCache != NULL) { 1.1167 + uhash_close(gTZGNCoreCache); 1.1168 + gTZGNCoreCache = NULL; 1.1169 + } 1.1170 + gTZGNCoreCacheInitialized = FALSE; 1.1171 + return TRUE; 1.1172 +} 1.1173 + 1.1174 +/** 1.1175 + * Deleter for TZGNCoreRef 1.1176 + */ 1.1177 +static void U_CALLCONV 1.1178 +deleteTZGNCoreRef(void *obj) { 1.1179 + icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj; 1.1180 + delete (icu::TZGNCore*) entry->obj; 1.1181 + uprv_free(entry); 1.1182 +} 1.1183 +U_CDECL_END 1.1184 + 1.1185 +/** 1.1186 + * Function used for removing unreferrenced cache entries exceeding 1.1187 + * the expiration time. This function must be called with in the mutex 1.1188 + * block. 1.1189 + */ 1.1190 +static void sweepCache() { 1.1191 + int32_t pos = -1; 1.1192 + const UHashElement* elem; 1.1193 + double now = (double)uprv_getUTCtime(); 1.1194 + 1.1195 + while ((elem = uhash_nextElement(gTZGNCoreCache, &pos))) { 1.1196 + TZGNCoreRef *entry = (TZGNCoreRef *)elem->value.pointer; 1.1197 + if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) { 1.1198 + // delete this entry 1.1199 + uhash_removeElement(gTZGNCoreCache, elem); 1.1200 + } 1.1201 + } 1.1202 +} 1.1203 + 1.1204 +TimeZoneGenericNames::TimeZoneGenericNames() 1.1205 +: fRef(0) { 1.1206 +} 1.1207 + 1.1208 +TimeZoneGenericNames::~TimeZoneGenericNames() { 1.1209 + umtx_lock(&gTZGNLock); 1.1210 + { 1.1211 + U_ASSERT(fRef->refCount > 0); 1.1212 + // Just decrement the reference count 1.1213 + fRef->refCount--; 1.1214 + } 1.1215 + umtx_unlock(&gTZGNLock); 1.1216 +} 1.1217 + 1.1218 +TimeZoneGenericNames* 1.1219 +TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) { 1.1220 + if (U_FAILURE(status)) { 1.1221 + return NULL; 1.1222 + } 1.1223 + TimeZoneGenericNames* instance = new TimeZoneGenericNames(); 1.1224 + if (instance == NULL) { 1.1225 + status = U_MEMORY_ALLOCATION_ERROR; 1.1226 + return NULL; 1.1227 + } 1.1228 + 1.1229 + TZGNCoreRef *cacheEntry = NULL; 1.1230 + { 1.1231 + Mutex lock(&gTZGNLock); 1.1232 + 1.1233 + if (!gTZGNCoreCacheInitialized) { 1.1234 + // Create empty hashtable 1.1235 + gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); 1.1236 + if (U_SUCCESS(status)) { 1.1237 + uhash_setKeyDeleter(gTZGNCoreCache, uprv_free); 1.1238 + uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef); 1.1239 + gTZGNCoreCacheInitialized = TRUE; 1.1240 + ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup); 1.1241 + } 1.1242 + } 1.1243 + if (U_FAILURE(status)) { 1.1244 + return NULL; 1.1245 + } 1.1246 + 1.1247 + // Check the cache, if not available, create new one and cache 1.1248 + const char *key = locale.getName(); 1.1249 + cacheEntry = (TZGNCoreRef *)uhash_get(gTZGNCoreCache, key); 1.1250 + if (cacheEntry == NULL) { 1.1251 + TZGNCore *tzgnCore = NULL; 1.1252 + char *newKey = NULL; 1.1253 + 1.1254 + tzgnCore = new TZGNCore(locale, status); 1.1255 + if (tzgnCore == NULL) { 1.1256 + status = U_MEMORY_ALLOCATION_ERROR; 1.1257 + } 1.1258 + if (U_SUCCESS(status)) { 1.1259 + newKey = (char *)uprv_malloc(uprv_strlen(key) + 1); 1.1260 + if (newKey == NULL) { 1.1261 + status = U_MEMORY_ALLOCATION_ERROR; 1.1262 + } else { 1.1263 + uprv_strcpy(newKey, key); 1.1264 + } 1.1265 + } 1.1266 + if (U_SUCCESS(status)) { 1.1267 + cacheEntry = (TZGNCoreRef *)uprv_malloc(sizeof(TZGNCoreRef)); 1.1268 + if (cacheEntry == NULL) { 1.1269 + status = U_MEMORY_ALLOCATION_ERROR; 1.1270 + } else { 1.1271 + cacheEntry->obj = tzgnCore; 1.1272 + cacheEntry->refCount = 1; 1.1273 + cacheEntry->lastAccess = (double)uprv_getUTCtime(); 1.1274 + 1.1275 + uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status); 1.1276 + } 1.1277 + } 1.1278 + if (U_FAILURE(status)) { 1.1279 + if (tzgnCore != NULL) { 1.1280 + delete tzgnCore; 1.1281 + } 1.1282 + if (newKey != NULL) { 1.1283 + uprv_free(newKey); 1.1284 + } 1.1285 + if (cacheEntry != NULL) { 1.1286 + uprv_free(cacheEntry); 1.1287 + } 1.1288 + cacheEntry = NULL; 1.1289 + } 1.1290 + } else { 1.1291 + // Update the reference count 1.1292 + cacheEntry->refCount++; 1.1293 + cacheEntry->lastAccess = (double)uprv_getUTCtime(); 1.1294 + } 1.1295 + gAccessCount++; 1.1296 + if (gAccessCount >= SWEEP_INTERVAL) { 1.1297 + // sweep 1.1298 + sweepCache(); 1.1299 + gAccessCount = 0; 1.1300 + } 1.1301 + } // End of mutex locked block 1.1302 + 1.1303 + if (cacheEntry == NULL) { 1.1304 + delete instance; 1.1305 + return NULL; 1.1306 + } 1.1307 + 1.1308 + instance->fRef = cacheEntry; 1.1309 + return instance; 1.1310 +} 1.1311 + 1.1312 +UBool 1.1313 +TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const { 1.1314 + // Just compare if the other object also use the same 1.1315 + // ref entry 1.1316 + return fRef == other.fRef; 1.1317 +} 1.1318 + 1.1319 +TimeZoneGenericNames* 1.1320 +TimeZoneGenericNames::clone() const { 1.1321 + TimeZoneGenericNames* other = new TimeZoneGenericNames(); 1.1322 + if (other) { 1.1323 + umtx_lock(&gTZGNLock); 1.1324 + { 1.1325 + // Just increments the reference count 1.1326 + fRef->refCount++; 1.1327 + other->fRef = fRef; 1.1328 + } 1.1329 + umtx_unlock(&gTZGNLock); 1.1330 + } 1.1331 + return other; 1.1332 +} 1.1333 + 1.1334 +UnicodeString& 1.1335 +TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, 1.1336 + UDate date, UnicodeString& name) const { 1.1337 + return fRef->obj->getDisplayName(tz, type, date, name); 1.1338 +} 1.1339 + 1.1340 +UnicodeString& 1.1341 +TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const { 1.1342 + return fRef->obj->getGenericLocationName(tzCanonicalID, name); 1.1343 +} 1.1344 + 1.1345 +int32_t 1.1346 +TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, 1.1347 + UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const { 1.1348 + return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status); 1.1349 +} 1.1350 + 1.1351 +U_NAMESPACE_END 1.1352 +#endif