intl/icu/source/i18n/tznames.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial