1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/tznames.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,455 @@ 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 "unicode/locid.h" 1.16 +#include "unicode/tznames.h" 1.17 +#include "unicode/uenum.h" 1.18 +#include "cmemory.h" 1.19 +#include "cstring.h" 1.20 +#include "mutex.h" 1.21 +#include "putilimp.h" 1.22 +#include "tznames_impl.h" 1.23 +#include "uassert.h" 1.24 +#include "ucln_in.h" 1.25 +#include "uhash.h" 1.26 +#include "umutex.h" 1.27 +#include "uvector.h" 1.28 + 1.29 + 1.30 +U_NAMESPACE_BEGIN 1.31 + 1.32 +// TimeZoneNames object cache handling 1.33 +static UMutex gTimeZoneNamesLock = U_MUTEX_INITIALIZER; 1.34 +static UHashtable *gTimeZoneNamesCache = NULL; 1.35 +static UBool gTimeZoneNamesCacheInitialized = FALSE; 1.36 + 1.37 +// Access count - incremented every time up to SWEEP_INTERVAL, 1.38 +// then reset to 0 1.39 +static int32_t gAccessCount = 0; 1.40 + 1.41 +// Interval for calling the cache sweep function - every 100 times 1.42 +#define SWEEP_INTERVAL 100 1.43 + 1.44 +// Cache expiration in millisecond. When a cached entry is no 1.45 +// longer referenced and exceeding this threshold since last 1.46 +// access time, then the cache entry will be deleted by the sweep 1.47 +// function. For now, 3 minutes. 1.48 +#define CACHE_EXPIRATION 180000.0 1.49 + 1.50 +typedef struct TimeZoneNamesCacheEntry { 1.51 + TimeZoneNames* names; 1.52 + int32_t refCount; 1.53 + double lastAccess; 1.54 +} TimeZoneNamesCacheEntry; 1.55 + 1.56 +U_CDECL_BEGIN 1.57 +/** 1.58 + * Cleanup callback func 1.59 + */ 1.60 +static UBool U_CALLCONV timeZoneNames_cleanup(void) 1.61 +{ 1.62 + if (gTimeZoneNamesCache != NULL) { 1.63 + uhash_close(gTimeZoneNamesCache); 1.64 + gTimeZoneNamesCache = NULL; 1.65 + } 1.66 + gTimeZoneNamesCacheInitialized = FALSE; 1.67 + return TRUE; 1.68 +} 1.69 + 1.70 +/** 1.71 + * Deleter for TimeZoneNamesCacheEntry 1.72 + */ 1.73 +static void U_CALLCONV 1.74 +deleteTimeZoneNamesCacheEntry(void *obj) { 1.75 + icu::TimeZoneNamesCacheEntry *entry = (icu::TimeZoneNamesCacheEntry*)obj; 1.76 + delete (icu::TimeZoneNamesImpl*) entry->names; 1.77 + uprv_free(entry); 1.78 +} 1.79 +U_CDECL_END 1.80 + 1.81 +/** 1.82 + * Function used for removing unreferrenced cache entries exceeding 1.83 + * the expiration time. This function must be called with in the mutex 1.84 + * block. 1.85 + */ 1.86 +static void sweepCache() { 1.87 + int32_t pos = -1; 1.88 + const UHashElement* elem; 1.89 + double now = (double)uprv_getUTCtime(); 1.90 + 1.91 + while ((elem = uhash_nextElement(gTimeZoneNamesCache, &pos))) { 1.92 + TimeZoneNamesCacheEntry *entry = (TimeZoneNamesCacheEntry *)elem->value.pointer; 1.93 + if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) { 1.94 + // delete this entry 1.95 + uhash_removeElement(gTimeZoneNamesCache, elem); 1.96 + } 1.97 + } 1.98 +} 1.99 + 1.100 +// --------------------------------------------------- 1.101 +// TimeZoneNamesDelegate 1.102 +// --------------------------------------------------- 1.103 +class TimeZoneNamesDelegate : public TimeZoneNames { 1.104 +public: 1.105 + TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status); 1.106 + virtual ~TimeZoneNamesDelegate(); 1.107 + 1.108 + virtual UBool operator==(const TimeZoneNames& other) const; 1.109 + virtual UBool operator!=(const TimeZoneNames& other) const {return !operator==(other);}; 1.110 + virtual TimeZoneNames* clone() const; 1.111 + 1.112 + StringEnumeration* getAvailableMetaZoneIDs(UErrorCode& status) const; 1.113 + StringEnumeration* getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const; 1.114 + UnicodeString& getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const; 1.115 + UnicodeString& getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const; 1.116 + 1.117 + UnicodeString& getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const; 1.118 + UnicodeString& getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const; 1.119 + 1.120 + UnicodeString& getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const; 1.121 + 1.122 + MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const; 1.123 +private: 1.124 + TimeZoneNamesDelegate(); 1.125 + TimeZoneNamesCacheEntry* fTZnamesCacheEntry; 1.126 +}; 1.127 + 1.128 +TimeZoneNamesDelegate::TimeZoneNamesDelegate() 1.129 +: fTZnamesCacheEntry(0) { 1.130 +} 1.131 + 1.132 +TimeZoneNamesDelegate::TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status) { 1.133 + Mutex lock(&gTimeZoneNamesLock); 1.134 + if (!gTimeZoneNamesCacheInitialized) { 1.135 + // Create empty hashtable if it is not already initialized. 1.136 + gTimeZoneNamesCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); 1.137 + if (U_SUCCESS(status)) { 1.138 + uhash_setKeyDeleter(gTimeZoneNamesCache, uprv_free); 1.139 + uhash_setValueDeleter(gTimeZoneNamesCache, deleteTimeZoneNamesCacheEntry); 1.140 + gTimeZoneNamesCacheInitialized = TRUE; 1.141 + ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONENAMES, timeZoneNames_cleanup); 1.142 + } 1.143 + } 1.144 + 1.145 + if (U_FAILURE(status)) { 1.146 + return; 1.147 + } 1.148 + 1.149 + // Check the cache, if not available, create new one and cache 1.150 + TimeZoneNamesCacheEntry *cacheEntry = NULL; 1.151 + 1.152 + const char *key = locale.getName(); 1.153 + cacheEntry = (TimeZoneNamesCacheEntry *)uhash_get(gTimeZoneNamesCache, key); 1.154 + if (cacheEntry == NULL) { 1.155 + TimeZoneNames *tznames = NULL; 1.156 + char *newKey = NULL; 1.157 + 1.158 + tznames = new TimeZoneNamesImpl(locale, status); 1.159 + if (tznames == NULL) { 1.160 + status = U_MEMORY_ALLOCATION_ERROR; 1.161 + } 1.162 + if (U_SUCCESS(status)) { 1.163 + newKey = (char *)uprv_malloc(uprv_strlen(key) + 1); 1.164 + if (newKey == NULL) { 1.165 + status = U_MEMORY_ALLOCATION_ERROR; 1.166 + } else { 1.167 + uprv_strcpy(newKey, key); 1.168 + } 1.169 + } 1.170 + if (U_SUCCESS(status)) { 1.171 + cacheEntry = (TimeZoneNamesCacheEntry *)uprv_malloc(sizeof(TimeZoneNamesCacheEntry)); 1.172 + if (cacheEntry == NULL) { 1.173 + status = U_MEMORY_ALLOCATION_ERROR; 1.174 + } else { 1.175 + cacheEntry->names = tznames; 1.176 + cacheEntry->refCount = 1; 1.177 + cacheEntry->lastAccess = (double)uprv_getUTCtime(); 1.178 + 1.179 + uhash_put(gTimeZoneNamesCache, newKey, cacheEntry, &status); 1.180 + } 1.181 + } 1.182 + if (U_FAILURE(status)) { 1.183 + if (tznames != NULL) { 1.184 + delete tznames; 1.185 + } 1.186 + if (newKey != NULL) { 1.187 + uprv_free(newKey); 1.188 + } 1.189 + if (cacheEntry != NULL) { 1.190 + uprv_free(cacheEntry); 1.191 + } 1.192 + cacheEntry = NULL; 1.193 + } 1.194 + } else { 1.195 + // Update the reference count 1.196 + cacheEntry->refCount++; 1.197 + cacheEntry->lastAccess = (double)uprv_getUTCtime(); 1.198 + } 1.199 + gAccessCount++; 1.200 + if (gAccessCount >= SWEEP_INTERVAL) { 1.201 + // sweep 1.202 + sweepCache(); 1.203 + gAccessCount = 0; 1.204 + } 1.205 + fTZnamesCacheEntry = cacheEntry; 1.206 +} 1.207 + 1.208 +TimeZoneNamesDelegate::~TimeZoneNamesDelegate() { 1.209 + umtx_lock(&gTimeZoneNamesLock); 1.210 + { 1.211 + if (fTZnamesCacheEntry) { 1.212 + U_ASSERT(fTZnamesCacheEntry->refCount > 0); 1.213 + // Just decrement the reference count 1.214 + fTZnamesCacheEntry->refCount--; 1.215 + } 1.216 + } 1.217 + umtx_unlock(&gTimeZoneNamesLock); 1.218 +} 1.219 + 1.220 +UBool 1.221 +TimeZoneNamesDelegate::operator==(const TimeZoneNames& other) const { 1.222 + if (this == &other) { 1.223 + return TRUE; 1.224 + } 1.225 + // Just compare if the other object also use the same 1.226 + // cache entry 1.227 + const TimeZoneNamesDelegate* rhs = dynamic_cast<const TimeZoneNamesDelegate*>(&other); 1.228 + if (rhs) { 1.229 + return fTZnamesCacheEntry == rhs->fTZnamesCacheEntry; 1.230 + } 1.231 + return FALSE; 1.232 +} 1.233 + 1.234 +TimeZoneNames* 1.235 +TimeZoneNamesDelegate::clone() const { 1.236 + TimeZoneNamesDelegate* other = new TimeZoneNamesDelegate(); 1.237 + if (other != NULL) { 1.238 + umtx_lock(&gTimeZoneNamesLock); 1.239 + { 1.240 + // Just increment the reference count 1.241 + fTZnamesCacheEntry->refCount++; 1.242 + other->fTZnamesCacheEntry = fTZnamesCacheEntry; 1.243 + } 1.244 + umtx_unlock(&gTimeZoneNamesLock); 1.245 + } 1.246 + return other; 1.247 +} 1.248 + 1.249 +StringEnumeration* 1.250 +TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const { 1.251 + return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status); 1.252 +} 1.253 + 1.254 +StringEnumeration* 1.255 +TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const { 1.256 + return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status); 1.257 +} 1.258 + 1.259 +UnicodeString& 1.260 +TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const { 1.261 + return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID); 1.262 +} 1.263 + 1.264 +UnicodeString& 1.265 +TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const { 1.266 + return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID); 1.267 +} 1.268 + 1.269 +UnicodeString& 1.270 +TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const { 1.271 + return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name); 1.272 +} 1.273 + 1.274 +UnicodeString& 1.275 +TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const { 1.276 + return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name); 1.277 +} 1.278 + 1.279 +UnicodeString& 1.280 +TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const { 1.281 + return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name); 1.282 +} 1.283 + 1.284 +TimeZoneNames::MatchInfoCollection* 1.285 +TimeZoneNamesDelegate::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { 1.286 + return fTZnamesCacheEntry->names->find(text, start, types, status); 1.287 +} 1.288 + 1.289 +// --------------------------------------------------- 1.290 +// TimeZoneNames base class 1.291 +// --------------------------------------------------- 1.292 +TimeZoneNames::~TimeZoneNames() { 1.293 +} 1.294 + 1.295 +TimeZoneNames* 1.296 +TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) { 1.297 + return new TimeZoneNamesDelegate(locale, status); 1.298 +} 1.299 + 1.300 +UnicodeString& 1.301 +TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const { 1.302 + return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name); 1.303 +} 1.304 + 1.305 +UnicodeString& 1.306 +TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const { 1.307 + getTimeZoneDisplayName(tzID, type, name); 1.308 + if (name.isEmpty()) { 1.309 + UnicodeString mzID; 1.310 + getMetaZoneID(tzID, date, mzID); 1.311 + getMetaZoneDisplayName(mzID, type, name); 1.312 + } 1.313 + return name; 1.314 +} 1.315 + 1.316 + 1.317 +struct MatchInfo : UMemory { 1.318 + UTimeZoneNameType nameType; 1.319 + UnicodeString id; 1.320 + int32_t matchLength; 1.321 + UBool isTZID; 1.322 + 1.323 + MatchInfo(UTimeZoneNameType nameType, int32_t matchLength, const UnicodeString* tzID, const UnicodeString* mzID) { 1.324 + this->nameType = nameType; 1.325 + this->matchLength = matchLength; 1.326 + if (tzID != NULL) { 1.327 + this->id.setTo(*tzID); 1.328 + this->isTZID = TRUE; 1.329 + } else { 1.330 + this->id.setTo(*mzID); 1.331 + this->isTZID = FALSE; 1.332 + } 1.333 + } 1.334 +}; 1.335 + 1.336 +U_CDECL_BEGIN 1.337 +static void U_CALLCONV 1.338 +deleteMatchInfo(void *obj) { 1.339 + delete static_cast<MatchInfo *>(obj); 1.340 +} 1.341 +U_CDECL_END 1.342 + 1.343 +// --------------------------------------------------- 1.344 +// MatchInfoCollection class 1.345 +// --------------------------------------------------- 1.346 +TimeZoneNames::MatchInfoCollection::MatchInfoCollection() 1.347 +: fMatches(NULL) { 1.348 +} 1.349 + 1.350 +TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() { 1.351 + if (fMatches != NULL) { 1.352 + delete fMatches; 1.353 + } 1.354 +} 1.355 + 1.356 +void 1.357 +TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType, int32_t matchLength, 1.358 + const UnicodeString& tzID, UErrorCode& status) { 1.359 + if (U_FAILURE(status)) { 1.360 + return; 1.361 + } 1.362 + MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, &tzID, NULL); 1.363 + if (matchInfo == NULL) { 1.364 + status = U_MEMORY_ALLOCATION_ERROR; 1.365 + return; 1.366 + } 1.367 + matches(status)->addElement(matchInfo, status); 1.368 + if (U_FAILURE(status)) { 1.369 + delete matchInfo; 1.370 + } 1.371 +} 1.372 + 1.373 +void 1.374 +TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType, int32_t matchLength, 1.375 + const UnicodeString& mzID, UErrorCode& status) { 1.376 + if (U_FAILURE(status)) { 1.377 + return; 1.378 + } 1.379 + MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, NULL, &mzID); 1.380 + if (matchInfo == NULL) { 1.381 + status = U_MEMORY_ALLOCATION_ERROR; 1.382 + return; 1.383 + } 1.384 + matches(status)->addElement(matchInfo, status); 1.385 + if (U_FAILURE(status)) { 1.386 + delete matchInfo; 1.387 + } 1.388 +} 1.389 + 1.390 +int32_t 1.391 +TimeZoneNames::MatchInfoCollection::size() const { 1.392 + if (fMatches == NULL) { 1.393 + return 0; 1.394 + } 1.395 + return fMatches->size(); 1.396 +} 1.397 + 1.398 +UTimeZoneNameType 1.399 +TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx) const { 1.400 + const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx); 1.401 + if (match) { 1.402 + return match->nameType; 1.403 + } 1.404 + return UTZNM_UNKNOWN; 1.405 +} 1.406 + 1.407 +int32_t 1.408 +TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx) const { 1.409 + const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx); 1.410 + if (match) { 1.411 + return match->matchLength; 1.412 + } 1.413 + return 0; 1.414 +} 1.415 + 1.416 +UBool 1.417 +TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx, UnicodeString& tzID) const { 1.418 + tzID.remove(); 1.419 + const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx); 1.420 + if (match && match->isTZID) { 1.421 + tzID.setTo(match->id); 1.422 + return TRUE; 1.423 + } 1.424 + return FALSE; 1.425 +} 1.426 + 1.427 +UBool 1.428 +TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx, UnicodeString& mzID) const { 1.429 + mzID.remove(); 1.430 + const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx); 1.431 + if (match && !match->isTZID) { 1.432 + mzID.setTo(match->id); 1.433 + return TRUE; 1.434 + } 1.435 + return FALSE; 1.436 +} 1.437 + 1.438 +UVector* 1.439 +TimeZoneNames::MatchInfoCollection::matches(UErrorCode& status) { 1.440 + if (U_FAILURE(status)) { 1.441 + return NULL; 1.442 + } 1.443 + if (fMatches != NULL) { 1.444 + return fMatches; 1.445 + } 1.446 + fMatches = new UVector(deleteMatchInfo, NULL, status); 1.447 + if (fMatches == NULL) { 1.448 + status = U_MEMORY_ALLOCATION_ERROR; 1.449 + } else if (U_FAILURE(status)) { 1.450 + delete fMatches; 1.451 + fMatches = NULL; 1.452 + } 1.453 + return fMatches; 1.454 +} 1.455 + 1.456 + 1.457 +U_NAMESPACE_END 1.458 +#endif