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