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 26 U_NAMESPACE_BEGIN 27 28 // TimeZoneNames object cache handling 29 static UMutex gTimeZoneNamesLock = U_MUTEX_INITIALIZER; 30 static UHashtable *gTimeZoneNamesCache = NULL; 31 static UBool gTimeZoneNamesCacheInitialized = FALSE; 32 33 // Access count - incremented every time up to SWEEP_INTERVAL, 34 // then reset to 0 35 static 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 46 typedef struct TimeZoneNamesCacheEntry { 47 TimeZoneNames* names; 48 int32_t refCount; 49 double lastAccess; 50 } TimeZoneNamesCacheEntry; 51 52 U_CDECL_BEGIN 53 /** 54 * Cleanup callback func 55 */ 56 static 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 */ 69 static void U_CALLCONV 70 deleteTimeZoneNamesCacheEntry(void *obj) { 71 icu::TimeZoneNamesCacheEntry *entry = (icu::TimeZoneNamesCacheEntry*)obj; 72 delete (icu::TimeZoneNamesImpl*) entry->names; 73 uprv_free(entry); 74 } 75 U_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 */ 82 static 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 // --------------------------------------------------- 99 class TimeZoneNamesDelegate : public TimeZoneNames { 100 public: 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; 119 private: 120 TimeZoneNamesDelegate(); 121 TimeZoneNamesCacheEntry* fTZnamesCacheEntry; 122 }; 123 124 TimeZoneNamesDelegate::TimeZoneNamesDelegate() 125 : fTZnamesCacheEntry(0) { 126 } 127 128 TimeZoneNamesDelegate::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 215 TimeZoneNamesDelegate::~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 227 UBool 228 TimeZoneNamesDelegate::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 241 TimeZoneNames* 242 TimeZoneNamesDelegate::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 256 StringEnumeration* 257 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const { 258 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status); 259 } 260 261 StringEnumeration* 262 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const { 263 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status); 264 } 265 266 UnicodeString& 267 TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const { 268 return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID); 269 } 270 271 UnicodeString& 272 TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const { 273 return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID); 274 } 275 276 UnicodeString& 277 TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const { 278 return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name); 279 } 280 281 UnicodeString& 282 TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const { 283 return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name); 284 } 285 286 UnicodeString& 287 TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const { 288 return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name); 289 } 290 291 TimeZoneNames::MatchInfoCollection* 292 TimeZoneNamesDelegate::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 // --------------------------------------------------- 299 TimeZoneNames::~TimeZoneNames() { 300 } 301 302 TimeZoneNames* 303 TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) { 304 return new TimeZoneNamesDelegate(locale, status); 305 } 306 307 UnicodeString& 308 TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const { 309 return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name); 310 } 311 312 UnicodeString& 313 TimeZoneNames::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 324 struct 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 343 U_CDECL_BEGIN 344 static void U_CALLCONV 345 deleteMatchInfo(void *obj) { 346 delete static_cast<MatchInfo *>(obj); 347 } 348 U_CDECL_END 349 350 // --------------------------------------------------- 351 // MatchInfoCollection class 352 // --------------------------------------------------- 353 TimeZoneNames::MatchInfoCollection::MatchInfoCollection() 354 : fMatches(NULL) { 355 } 356 357 TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() { 358 if (fMatches != NULL) { 359 delete fMatches; 360 } 361 } 362 363 void 364 TimeZoneNames::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 380 void 381 TimeZoneNames::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 397 int32_t 398 TimeZoneNames::MatchInfoCollection::size() const { 399 if (fMatches == NULL) { 400 return 0; 401 } 402 return fMatches->size(); 403 } 404 405 UTimeZoneNameType 406 TimeZoneNames::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 414 int32_t 415 TimeZoneNames::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 423 UBool 424 TimeZoneNames::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 434 UBool 435 TimeZoneNames::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 445 UVector* 446 TimeZoneNames::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 464 U_NAMESPACE_END 465 #endif 466