Home | History | Annotate | Download | only in i18n
      1 /*
      2 *******************************************************************************
      3 * Copyright (C) 2011-2015, 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 "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"
     25 
     26 
     27 U_NAMESPACE_BEGIN
     28 
     29 // TimeZoneNames object cache handling
     30 static UMutex gTimeZoneNamesLock = U_MUTEX_INITIALIZER;
     31 static UHashtable *gTimeZoneNamesCache = NULL;
     32 static UBool gTimeZoneNamesCacheInitialized = FALSE;
     33 
     34 // Access count - incremented every time up to SWEEP_INTERVAL,
     35 // then reset to 0
     36 static int32_t gAccessCount = 0;
     37 
     38 // Interval for calling the cache sweep function - every 100 times
     39 #define SWEEP_INTERVAL 100
     40 
     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
     46 
     47 typedef struct TimeZoneNamesCacheEntry {
     48     TimeZoneNames*  names;
     49     int32_t         refCount;
     50     double          lastAccess;
     51 } TimeZoneNamesCacheEntry;
     52 
     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 }
     66 
     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
     77 
     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 = UHASH_FIRST;
     85     const UHashElement* elem;
     86     double now = (double)uprv_getUTCtime();
     87 
     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 }
     96 
     97 // ---------------------------------------------------
     98 // TimeZoneNamesDelegate
     99 // ---------------------------------------------------
    100 class TimeZoneNamesDelegate : public TimeZoneNames {
    101 public:
    102     TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status);
    103     virtual ~TimeZoneNamesDelegate();
    104 
    105     virtual UBool operator==(const TimeZoneNames& other) const;
    106     virtual UBool operator!=(const TimeZoneNames& other) const {return !operator==(other);};
    107     virtual TimeZoneNames* clone() const;
    108 
    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;
    113 
    114     UnicodeString& getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const;
    115     UnicodeString& getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const;
    116 
    117     UnicodeString& getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const;
    118 
    119     MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
    120 private:
    121     TimeZoneNamesDelegate();
    122     TimeZoneNamesCacheEntry*    fTZnamesCacheEntry;
    123 };
    124 
    125 TimeZoneNamesDelegate::TimeZoneNamesDelegate()
    126 : fTZnamesCacheEntry(0) {
    127 }
    128 
    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     }
    141 
    142     if (U_FAILURE(status)) {
    143         return;
    144     }
    145 
    146     // Check the cache, if not available, create new one and cache
    147     TimeZoneNamesCacheEntry *cacheEntry = NULL;
    148 
    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;
    154 
    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();
    175 
    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 }
    204 
    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 }
    216 
    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 }
    230 
    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 }
    245 
    246 StringEnumeration*
    247 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const {
    248     return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status);
    249 }
    250 
    251 StringEnumeration*
    252 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
    253     return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status);
    254 }
    255 
    256 UnicodeString&
    257 TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
    258     return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID);
    259 }
    260 
    261 UnicodeString&
    262 TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
    263     return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID);
    264 }
    265 
    266 UnicodeString&
    267 TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const {
    268     return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name);
    269 }
    270 
    271 UnicodeString&
    272 TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
    273     return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name);
    274 }
    275 
    276 UnicodeString&
    277 TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
    278     return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name);
    279 }
    280 
    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 }
    285 
    286 // ---------------------------------------------------
    287 // TimeZoneNames base class
    288 // ---------------------------------------------------
    289 TimeZoneNames::~TimeZoneNames() {
    290 }
    291 
    292 TimeZoneNames*
    293 TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) {
    294     TimeZoneNames *instance = NULL;
    295     if (U_SUCCESS(status)) {
    296         instance = new TimeZoneNamesDelegate(locale, status);
    297         if (instance == NULL && U_SUCCESS(status)) {
    298             status = U_MEMORY_ALLOCATION_ERROR;
    299         }
    300     }
    301     return instance;
    302 }
    303 
    304 TimeZoneNames*
    305 TimeZoneNames::createTZDBInstance(const Locale& locale, UErrorCode& status) {
    306     TimeZoneNames *instance = NULL;
    307     if (U_SUCCESS(status)) {
    308         instance = new TZDBTimeZoneNames(locale);
    309         if (instance == NULL && U_SUCCESS(status)) {
    310             status = U_MEMORY_ALLOCATION_ERROR;
    311         }
    312     }
    313     return instance;
    314 }
    315 
    316 UnicodeString&
    317 TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
    318     return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name);
    319 }
    320 
    321 UnicodeString&
    322 TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const {
    323     getTimeZoneDisplayName(tzID, type, name);
    324     if (name.isEmpty()) {
    325         UChar mzIDBuf[32];
    326         UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
    327         getMetaZoneID(tzID, date, mzID);
    328         getMetaZoneDisplayName(mzID, type, name);
    329     }
    330     return name;
    331 }
    332 
    333 
    334 struct MatchInfo : UMemory {
    335     UTimeZoneNameType nameType;
    336     UnicodeString id;
    337     int32_t matchLength;
    338     UBool isTZID;
    339 
    340     MatchInfo(UTimeZoneNameType nameType, int32_t matchLength, const UnicodeString* tzID, const UnicodeString* mzID) {
    341         this->nameType = nameType;
    342         this->matchLength = matchLength;
    343         if (tzID != NULL) {
    344             this->id.setTo(*tzID);
    345             this->isTZID = TRUE;
    346         } else {
    347             this->id.setTo(*mzID);
    348             this->isTZID = FALSE;
    349         }
    350     }
    351 };
    352 
    353 U_CDECL_BEGIN
    354 static void U_CALLCONV
    355 deleteMatchInfo(void *obj) {
    356     delete static_cast<MatchInfo *>(obj);
    357 }
    358 U_CDECL_END
    359 
    360 // ---------------------------------------------------
    361 // MatchInfoCollection class
    362 // ---------------------------------------------------
    363 TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
    364 : fMatches(NULL) {
    365 }
    366 
    367 TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
    368     if (fMatches != NULL) {
    369         delete fMatches;
    370     }
    371 }
    372 
    373 void
    374 TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType, int32_t matchLength,
    375             const UnicodeString& tzID, UErrorCode& status) {
    376     if (U_FAILURE(status)) {
    377         return;
    378     }
    379     MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, &tzID, NULL);
    380     if (matchInfo == NULL) {
    381         status = U_MEMORY_ALLOCATION_ERROR;
    382         return;
    383     }
    384     matches(status)->addElement(matchInfo, status);
    385     if (U_FAILURE(status)) {
    386         delete matchInfo;
    387     }
    388 }
    389 
    390 void
    391 TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType, int32_t matchLength,
    392             const UnicodeString& mzID, UErrorCode& status) {
    393     if (U_FAILURE(status)) {
    394         return;
    395     }
    396     MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, NULL, &mzID);
    397     if (matchInfo == NULL) {
    398         status = U_MEMORY_ALLOCATION_ERROR;
    399         return;
    400     }
    401     matches(status)->addElement(matchInfo, status);
    402     if (U_FAILURE(status)) {
    403         delete matchInfo;
    404     }
    405 }
    406 
    407 int32_t
    408 TimeZoneNames::MatchInfoCollection::size() const {
    409     if (fMatches == NULL) {
    410         return 0;
    411     }
    412     return fMatches->size();
    413 }
    414 
    415 UTimeZoneNameType
    416 TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx) const {
    417     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
    418     if (match) {
    419         return match->nameType;
    420     }
    421     return UTZNM_UNKNOWN;
    422 }
    423 
    424 int32_t
    425 TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx) const {
    426     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
    427     if (match) {
    428         return match->matchLength;
    429     }
    430     return 0;
    431 }
    432 
    433 UBool
    434 TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx, UnicodeString& tzID) const {
    435     tzID.remove();
    436     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
    437     if (match && match->isTZID) {
    438         tzID.setTo(match->id);
    439         return TRUE;
    440     }
    441     return FALSE;
    442 }
    443 
    444 UBool
    445 TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx, UnicodeString& mzID) const {
    446     mzID.remove();
    447     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
    448     if (match && !match->isTZID) {
    449         mzID.setTo(match->id);
    450         return TRUE;
    451     }
    452     return FALSE;
    453 }
    454 
    455 UVector*
    456 TimeZoneNames::MatchInfoCollection::matches(UErrorCode& status) {
    457     if (U_FAILURE(status)) {
    458         return NULL;
    459     }
    460     if (fMatches != NULL) {
    461         return fMatches;
    462     }
    463     fMatches = new UVector(deleteMatchInfo, NULL, status);
    464     if (fMatches == NULL) {
    465         status = U_MEMORY_ALLOCATION_ERROR;
    466     } else if (U_FAILURE(status)) {
    467         delete fMatches;
    468         fMatches = NULL;
    469     }
    470     return fMatches;
    471 }
    472 
    473 
    474 U_NAMESPACE_END
    475 #endif
    476