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