1/* 2******************************************************************************* 3* Copyright (C) 2011-2013, International Business Machines Corporation and * 4* others. All Rights Reserved. * 5******************************************************************************* 6*/ 7 8#include "unicode/utypes.h" 9 10#if !UCONFIG_NO_FORMATTING 11 12#include "unicode/locid.h" 13#include "unicode/tznames.h" 14#include "unicode/uenum.h" 15#include "cmemory.h" 16#include "cstring.h" 17#include "putilimp.h" 18#include "tznames_impl.h" 19#include "uassert.h" 20#include "ucln_in.h" 21#include "uhash.h" 22#include "umutex.h" 23#include "uvector.h" 24 25 26U_NAMESPACE_BEGIN 27 28// TimeZoneNames object cache handling 29static UMutex gTimeZoneNamesLock = U_MUTEX_INITIALIZER; 30static UHashtable *gTimeZoneNamesCache = NULL; 31static UBool gTimeZoneNamesCacheInitialized = FALSE; 32 33// Access count - incremented every time up to SWEEP_INTERVAL, 34// then reset to 0 35static int32_t gAccessCount = 0; 36 37// Interval for calling the cache sweep function - every 100 times 38#define SWEEP_INTERVAL 100 39 40// Cache expiration in millisecond. When a cached entry is no 41// longer referenced and exceeding this threshold since last 42// access time, then the cache entry will be deleted by the sweep 43// function. For now, 3 minutes. 44#define CACHE_EXPIRATION 180000.0 45 46typedef struct TimeZoneNamesCacheEntry { 47 TimeZoneNames* names; 48 int32_t refCount; 49 double lastAccess; 50} TimeZoneNamesCacheEntry; 51 52U_CDECL_BEGIN 53/** 54 * Cleanup callback func 55 */ 56static UBool U_CALLCONV timeZoneNames_cleanup(void) 57{ 58 if (gTimeZoneNamesCache != NULL) { 59 uhash_close(gTimeZoneNamesCache); 60 gTimeZoneNamesCache = NULL; 61 } 62 gTimeZoneNamesCacheInitialized = FALSE; 63 return TRUE; 64} 65 66/** 67 * Deleter for TimeZoneNamesCacheEntry 68 */ 69static void U_CALLCONV 70deleteTimeZoneNamesCacheEntry(void *obj) { 71 icu::TimeZoneNamesCacheEntry *entry = (icu::TimeZoneNamesCacheEntry*)obj; 72 delete (icu::TimeZoneNamesImpl*) entry->names; 73 uprv_free(entry); 74} 75U_CDECL_END 76 77/** 78 * Function used for removing unreferrenced cache entries exceeding 79 * the expiration time. This function must be called with in the mutex 80 * block. 81 */ 82static void sweepCache() { 83 int32_t pos = -1; 84 const UHashElement* elem; 85 double now = (double)uprv_getUTCtime(); 86 87 while ((elem = uhash_nextElement(gTimeZoneNamesCache, &pos))) { 88 TimeZoneNamesCacheEntry *entry = (TimeZoneNamesCacheEntry *)elem->value.pointer; 89 if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) { 90 // delete this entry 91 uhash_removeElement(gTimeZoneNamesCache, elem); 92 } 93 } 94} 95 96// --------------------------------------------------- 97// TimeZoneNamesDelegate 98// --------------------------------------------------- 99class TimeZoneNamesDelegate : public TimeZoneNames { 100public: 101 TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status); 102 virtual ~TimeZoneNamesDelegate(); 103 104 virtual UBool operator==(const TimeZoneNames& other) const; 105 virtual UBool operator!=(const TimeZoneNames& other) const {return !operator==(other);}; 106 virtual TimeZoneNames* clone() const; 107 108 StringEnumeration* getAvailableMetaZoneIDs(UErrorCode& status) const; 109 StringEnumeration* getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const; 110 UnicodeString& getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const; 111 UnicodeString& getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const; 112 113 UnicodeString& getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const; 114 UnicodeString& getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const; 115 116 UnicodeString& getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const; 117 118 MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const; 119private: 120 TimeZoneNamesDelegate(); 121 TimeZoneNamesCacheEntry* fTZnamesCacheEntry; 122}; 123 124TimeZoneNamesDelegate::TimeZoneNamesDelegate() 125: fTZnamesCacheEntry(0) { 126} 127 128TimeZoneNamesDelegate::TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status) { 129 UBool initialized; 130 UMTX_CHECK(&gTimeZoneNamesLock, gTimeZoneNamesCacheInitialized, initialized); 131 if (!initialized) { 132 // Create empty hashtable 133 umtx_lock(&gTimeZoneNamesLock); 134 { 135 if (!gTimeZoneNamesCacheInitialized) { 136 gTimeZoneNamesCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); 137 if (U_SUCCESS(status)) { 138 uhash_setKeyDeleter(gTimeZoneNamesCache, uprv_free); 139 uhash_setValueDeleter(gTimeZoneNamesCache, deleteTimeZoneNamesCacheEntry); 140 gTimeZoneNamesCacheInitialized = TRUE; 141 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONENAMES, timeZoneNames_cleanup); 142 } 143 } 144 } 145 umtx_unlock(&gTimeZoneNamesLock); 146 147 if (U_FAILURE(status)) { 148 return; 149 } 150 } 151 152 // Check the cache, if not available, create new one and cache 153 TimeZoneNamesCacheEntry *cacheEntry = NULL; 154 umtx_lock(&gTimeZoneNamesLock); 155 { 156 const char *key = locale.getName(); 157 cacheEntry = (TimeZoneNamesCacheEntry *)uhash_get(gTimeZoneNamesCache, key); 158 if (cacheEntry == NULL) { 159 TimeZoneNames *tznames = NULL; 160 char *newKey = NULL; 161 162 tznames = new TimeZoneNamesImpl(locale, status); 163 if (tznames == NULL) { 164 status = U_MEMORY_ALLOCATION_ERROR; 165 } 166 if (U_SUCCESS(status)) { 167 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1); 168 if (newKey == NULL) { 169 status = U_MEMORY_ALLOCATION_ERROR; 170 } else { 171 uprv_strcpy(newKey, key); 172 } 173 } 174 if (U_SUCCESS(status)) { 175 cacheEntry = (TimeZoneNamesCacheEntry *)uprv_malloc(sizeof(TimeZoneNamesCacheEntry)); 176 if (cacheEntry == NULL) { 177 status = U_MEMORY_ALLOCATION_ERROR; 178 } else { 179 cacheEntry->names = tznames; 180 cacheEntry->refCount = 1; 181 cacheEntry->lastAccess = (double)uprv_getUTCtime(); 182 183 uhash_put(gTimeZoneNamesCache, newKey, cacheEntry, &status); 184 } 185 } 186 if (U_FAILURE(status)) { 187 if (tznames != NULL) { 188 delete tznames; 189 } 190 if (newKey != NULL) { 191 uprv_free(newKey); 192 } 193 if (cacheEntry != NULL) { 194 uprv_free(cacheEntry); 195 } 196 cacheEntry = NULL; 197 } 198 } else { 199 // Update the reference count 200 cacheEntry->refCount++; 201 cacheEntry->lastAccess = (double)uprv_getUTCtime(); 202 } 203 gAccessCount++; 204 if (gAccessCount >= SWEEP_INTERVAL) { 205 // sweep 206 sweepCache(); 207 gAccessCount = 0; 208 } 209 } 210 umtx_unlock(&gTimeZoneNamesLock); 211 212 fTZnamesCacheEntry = cacheEntry; 213} 214 215TimeZoneNamesDelegate::~TimeZoneNamesDelegate() { 216 umtx_lock(&gTimeZoneNamesLock); 217 { 218 if (fTZnamesCacheEntry) { 219 U_ASSERT(fTZnamesCacheEntry->refCount > 0); 220 // Just decrement the reference count 221 fTZnamesCacheEntry->refCount--; 222 } 223 } 224 umtx_unlock(&gTimeZoneNamesLock); 225} 226 227UBool 228TimeZoneNamesDelegate::operator==(const TimeZoneNames& other) const { 229 if (this == &other) { 230 return TRUE; 231 } 232 // Just compare if the other object also use the same 233 // cache entry 234 const TimeZoneNamesDelegate* rhs = dynamic_cast<const TimeZoneNamesDelegate*>(&other); 235 if (rhs) { 236 return fTZnamesCacheEntry == rhs->fTZnamesCacheEntry; 237 } 238 return FALSE; 239} 240 241TimeZoneNames* 242TimeZoneNamesDelegate::clone() const { 243 TimeZoneNamesDelegate* other = new TimeZoneNamesDelegate(); 244 if (other != NULL) { 245 umtx_lock(&gTimeZoneNamesLock); 246 { 247 // Just increment the reference count 248 fTZnamesCacheEntry->refCount++; 249 other->fTZnamesCacheEntry = fTZnamesCacheEntry; 250 } 251 umtx_unlock(&gTimeZoneNamesLock); 252 } 253 return other; 254} 255 256StringEnumeration* 257TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const { 258 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status); 259} 260 261StringEnumeration* 262TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const { 263 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status); 264} 265 266UnicodeString& 267TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const { 268 return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID); 269} 270 271UnicodeString& 272TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const { 273 return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID); 274} 275 276UnicodeString& 277TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const { 278 return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name); 279} 280 281UnicodeString& 282TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const { 283 return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name); 284} 285 286UnicodeString& 287TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const { 288 return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name); 289} 290 291TimeZoneNames::MatchInfoCollection* 292TimeZoneNamesDelegate::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { 293 return fTZnamesCacheEntry->names->find(text, start, types, status); 294} 295 296// --------------------------------------------------- 297// TimeZoneNames base class 298// --------------------------------------------------- 299TimeZoneNames::~TimeZoneNames() { 300} 301 302TimeZoneNames* 303TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) { 304 return new TimeZoneNamesDelegate(locale, status); 305} 306 307UnicodeString& 308TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const { 309 return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name); 310} 311 312UnicodeString& 313TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const { 314 getTimeZoneDisplayName(tzID, type, name); 315 if (name.isEmpty()) { 316 UnicodeString mzID; 317 getMetaZoneID(tzID, date, mzID); 318 getMetaZoneDisplayName(mzID, type, name); 319 } 320 return name; 321} 322 323 324struct MatchInfo : UMemory { 325 UTimeZoneNameType nameType; 326 UnicodeString id; 327 int32_t matchLength; 328 UBool isTZID; 329 330 MatchInfo(UTimeZoneNameType nameType, int32_t matchLength, const UnicodeString* tzID, const UnicodeString* mzID) { 331 this->nameType = nameType; 332 this->matchLength = matchLength; 333 if (tzID != NULL) { 334 this->id.setTo(*tzID); 335 this->isTZID = TRUE; 336 } else { 337 this->id.setTo(*mzID); 338 this->isTZID = FALSE; 339 } 340 } 341}; 342 343U_CDECL_BEGIN 344static void U_CALLCONV 345deleteMatchInfo(void *obj) { 346 delete static_cast<MatchInfo *>(obj); 347} 348U_CDECL_END 349 350// --------------------------------------------------- 351// MatchInfoCollection class 352// --------------------------------------------------- 353TimeZoneNames::MatchInfoCollection::MatchInfoCollection() 354: fMatches(NULL) { 355} 356 357TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() { 358 if (fMatches != NULL) { 359 delete fMatches; 360 } 361} 362 363void 364TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType, int32_t matchLength, 365 const UnicodeString& tzID, UErrorCode& status) { 366 if (U_FAILURE(status)) { 367 return; 368 } 369 MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, &tzID, NULL); 370 if (matchInfo == NULL) { 371 status = U_MEMORY_ALLOCATION_ERROR; 372 return; 373 } 374 matches(status)->addElement(matchInfo, status); 375 if (U_FAILURE(status)) { 376 delete matchInfo; 377 } 378} 379 380void 381TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType, int32_t matchLength, 382 const UnicodeString& mzID, UErrorCode& status) { 383 if (U_FAILURE(status)) { 384 return; 385 } 386 MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, NULL, &mzID); 387 if (matchInfo == NULL) { 388 status = U_MEMORY_ALLOCATION_ERROR; 389 return; 390 } 391 matches(status)->addElement(matchInfo, status); 392 if (U_FAILURE(status)) { 393 delete matchInfo; 394 } 395} 396 397int32_t 398TimeZoneNames::MatchInfoCollection::size() const { 399 if (fMatches == NULL) { 400 return 0; 401 } 402 return fMatches->size(); 403} 404 405UTimeZoneNameType 406TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx) const { 407 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx); 408 if (match) { 409 return match->nameType; 410 } 411 return UTZNM_UNKNOWN; 412} 413 414int32_t 415TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx) const { 416 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx); 417 if (match) { 418 return match->matchLength; 419 } 420 return 0; 421} 422 423UBool 424TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx, UnicodeString& tzID) const { 425 tzID.remove(); 426 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx); 427 if (match && match->isTZID) { 428 tzID.setTo(match->id); 429 return TRUE; 430 } 431 return FALSE; 432} 433 434UBool 435TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx, UnicodeString& mzID) const { 436 mzID.remove(); 437 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx); 438 if (match && !match->isTZID) { 439 mzID.setTo(match->id); 440 return TRUE; 441 } 442 return FALSE; 443} 444 445UVector* 446TimeZoneNames::MatchInfoCollection::matches(UErrorCode& status) { 447 if (U_FAILURE(status)) { 448 return NULL; 449 } 450 if (fMatches != NULL) { 451 return fMatches; 452 } 453 fMatches = new UVector(deleteMatchInfo, NULL, status); 454 if (fMatches == NULL) { 455 status = U_MEMORY_ALLOCATION_ERROR; 456 } else if (U_FAILURE(status)) { 457 delete fMatches; 458 fMatches = NULL; 459 } 460 return fMatches; 461} 462 463 464U_NAMESPACE_END 465#endif 466