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

     1 /*
     2 *******************************************************************************
     3 * Copyright (C) 2011-2013, International Business Machines Corporation and    *
     4 * others. All Rights Reserved.                                                *
     5 *******************************************************************************
     6 */
     8 #include "unicode/utypes.h"
    10 #if !UCONFIG_NO_FORMATTING
    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"
    27 U_NAMESPACE_BEGIN
    29 // TimeZoneNames object cache handling
    30 static UMutex gTimeZoneNamesLock = U_MUTEX_INITIALIZER;
    31 static UHashtable *gTimeZoneNamesCache = NULL;
    32 static UBool gTimeZoneNamesCacheInitialized = FALSE;
    34 // Access count - incremented every time up to SWEEP_INTERVAL,
    35 // then reset to 0
    36 static int32_t gAccessCount = 0;
    38 // Interval for calling the cache sweep function - every 100 times
    39 #define SWEEP_INTERVAL 100
    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
    47 typedef struct TimeZoneNamesCacheEntry {
    48     TimeZoneNames*  names;
    49     int32_t         refCount;
    50     double          lastAccess;
    51 } TimeZoneNamesCacheEntry;
    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 }
    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
    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 = -1;
    85     const UHashElement* elem;
    86     double now = (double)uprv_getUTCtime();
    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 }
    97 // ---------------------------------------------------
    98 // TimeZoneNamesDelegate
    99 // ---------------------------------------------------
   100 class TimeZoneNamesDelegate : public TimeZoneNames {
   101 public:
   102     TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status);
   103     virtual ~TimeZoneNamesDelegate();
   105     virtual UBool operator==(const TimeZoneNames& other) const;
   106     virtual UBool operator!=(const TimeZoneNames& other) const {return !operator==(other);};
   107     virtual TimeZoneNames* clone() const;
   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;
   114     UnicodeString& getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const;
   115     UnicodeString& getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const;
   117     UnicodeString& getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const;
   119     MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
   120 private:
   121     TimeZoneNamesDelegate();
   122     TimeZoneNamesCacheEntry*    fTZnamesCacheEntry;
   123 };
   125 TimeZoneNamesDelegate::TimeZoneNamesDelegate()
   126 : fTZnamesCacheEntry(0) {
   127 }
   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     }
   142     if (U_FAILURE(status)) {
   143         return;
   144     }
   146     // Check the cache, if not available, create new one and cache
   147     TimeZoneNamesCacheEntry *cacheEntry = NULL;
   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;
   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();
   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 }
   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 }
   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 }
   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 }
   246 StringEnumeration*
   247 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const {
   248     return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status);
   249 }
   251 StringEnumeration*
   252 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
   253     return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status);
   254 }
   256 UnicodeString&
   257 TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
   258     return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID);
   259 }
   261 UnicodeString&
   262 TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
   263     return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID);
   264 }
   266 UnicodeString&
   267 TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const {
   268     return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name);
   269 }
   271 UnicodeString&
   272 TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
   273     return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name);
   274 }
   276 UnicodeString&
   277 TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
   278     return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name);
   279 }
   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 }
   286 // ---------------------------------------------------
   287 // TimeZoneNames base class
   288 // ---------------------------------------------------
   289 TimeZoneNames::~TimeZoneNames() {
   290 }
   292 TimeZoneNames*
   293 TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) {
   294     return new TimeZoneNamesDelegate(locale, status);
   295 }
   297 UnicodeString&
   298 TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
   299     return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name);
   300 }
   302 UnicodeString&
   303 TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const {
   304     getTimeZoneDisplayName(tzID, type, name);
   305     if (name.isEmpty()) {
   306         UnicodeString mzID;
   307         getMetaZoneID(tzID, date, mzID);
   308         getMetaZoneDisplayName(mzID, type, name);
   309     }
   310     return name;
   311 }
   314 struct MatchInfo : UMemory {
   315     UTimeZoneNameType nameType;
   316     UnicodeString id;
   317     int32_t matchLength;
   318     UBool isTZID;
   320     MatchInfo(UTimeZoneNameType nameType, int32_t matchLength, const UnicodeString* tzID, const UnicodeString* mzID) {
   321         this->nameType = nameType;
   322         this->matchLength = matchLength;
   323         if (tzID != NULL) {
   324             this->id.setTo(*tzID);
   325             this->isTZID = TRUE;
   326         } else {
   327             this->id.setTo(*mzID);
   328             this->isTZID = FALSE;
   329         }
   330     }
   331 };
   333 U_CDECL_BEGIN
   334 static void U_CALLCONV
   335 deleteMatchInfo(void *obj) {
   336     delete static_cast<MatchInfo *>(obj);
   337 }
   338 U_CDECL_END
   340 // ---------------------------------------------------
   341 // MatchInfoCollection class
   342 // ---------------------------------------------------
   343 TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
   344 : fMatches(NULL) {
   345 }
   347 TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
   348     if (fMatches != NULL) {
   349         delete fMatches;
   350     }
   351 }
   353 void
   354 TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType, int32_t matchLength,
   355             const UnicodeString& tzID, UErrorCode& status) {
   356     if (U_FAILURE(status)) {
   357         return;
   358     }
   359     MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, &tzID, NULL);
   360     if (matchInfo == NULL) {
   361         status = U_MEMORY_ALLOCATION_ERROR;
   362         return;
   363     }
   364     matches(status)->addElement(matchInfo, status);
   365     if (U_FAILURE(status)) {
   366         delete matchInfo;
   367     }
   368 }
   370 void
   371 TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType, int32_t matchLength,
   372             const UnicodeString& mzID, UErrorCode& status) {
   373     if (U_FAILURE(status)) {
   374         return;
   375     }
   376     MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, NULL, &mzID);
   377     if (matchInfo == NULL) {
   378         status = U_MEMORY_ALLOCATION_ERROR;
   379         return;
   380     }
   381     matches(status)->addElement(matchInfo, status);
   382     if (U_FAILURE(status)) {
   383         delete matchInfo;
   384     }
   385 }
   387 int32_t
   388 TimeZoneNames::MatchInfoCollection::size() const {
   389     if (fMatches == NULL) {
   390         return 0;
   391     }
   392     return fMatches->size();
   393 }
   395 UTimeZoneNameType
   396 TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx) const {
   397     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
   398     if (match) {
   399         return match->nameType;
   400     }
   401     return UTZNM_UNKNOWN;
   402 }
   404 int32_t
   405 TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx) const {
   406     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
   407     if (match) {
   408         return match->matchLength;
   409     }
   410     return 0;
   411 }
   413 UBool
   414 TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx, UnicodeString& tzID) const {
   415     tzID.remove();
   416     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
   417     if (match && match->isTZID) {
   418         tzID.setTo(match->id);
   419         return TRUE;
   420     }
   421     return FALSE;
   422 }
   424 UBool
   425 TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx, UnicodeString& mzID) const {
   426     mzID.remove();
   427     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
   428     if (match && !match->isTZID) {
   429         mzID.setTo(match->id);
   430         return TRUE;
   431     }
   432     return FALSE;
   433 }
   435 UVector*
   436 TimeZoneNames::MatchInfoCollection::matches(UErrorCode& status) {
   437     if (U_FAILURE(status)) {
   438         return NULL;
   439     }
   440     if (fMatches != NULL) {
   441         return fMatches;
   442     }
   443     fMatches = new UVector(deleteMatchInfo, NULL, status);
   444     if (fMatches == NULL) {
   445         status = U_MEMORY_ALLOCATION_ERROR;
   446     } else if (U_FAILURE(status)) {
   447         delete fMatches;
   448         fMatches = NULL;
   449     }
   450     return fMatches;
   451 }
   454 U_NAMESPACE_END
   455 #endif

mercurial