Home | History | Annotate | Download | only in i18n
      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