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-2016, 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 "tzgnames.h"
     15 
     16 #include "unicode/basictz.h"
     17 #include "unicode/locdspnm.h"
     18 #include "unicode/rbtz.h"
     19 #include "unicode/simpleformatter.h"
     20 #include "unicode/simpletz.h"
     21 #include "unicode/strenum.h"
     22 #include "unicode/vtzone.h"
     23 
     24 #include "cmemory.h"
     25 #include "cstring.h"
     26 #include "mutex.h"
     27 #include "uhash.h"
     28 #include "uassert.h"
     29 #include "umutex.h"
     30 #include "uresimp.h"
     31 #include "ureslocs.h"
     32 #include "zonemeta.h"
     33 #include "tznames_impl.h"
     34 #include "olsontz.h"
     35 #include "ucln_in.h"
     36 
     37 U_NAMESPACE_BEGIN
     38 
     39 #define ZID_KEY_MAX  128
     40 
     41 static const char gZoneStrings[]                = "zoneStrings";
     42 
     43 static const char gRegionFormatTag[]            = "regionFormat";
     44 static const char gFallbackFormatTag[]          = "fallbackFormat";
     45 
     46 static const UChar gEmpty[]                     = {0x00};
     47 
     48 static const UChar gDefRegionPattern[]          = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
     49 static const UChar gDefFallbackPattern[]        = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
     50 
     51 static const double kDstCheckRange      = (double)184*U_MILLIS_PER_DAY;
     52 
     53 
     54 
     55 U_CDECL_BEGIN
     56 
     57 typedef struct PartialLocationKey {
     58     const UChar* tzID;
     59     const UChar* mzID;
     60     UBool isLong;
     61 } PartialLocationKey;
     62 
     63 /**
     64  * Hash function for partial location name hash key
     65  */
     66 static int32_t U_CALLCONV
     67 hashPartialLocationKey(const UHashTok key) {
     68     // <tzID>&<mzID>#[L|S]
     69     PartialLocationKey *p = (PartialLocationKey *)key.pointer;
     70     UnicodeString str(p->tzID);
     71     str.append((UChar)0x26)
     72         .append(p->mzID, -1)
     73         .append((UChar)0x23)
     74         .append((UChar)(p->isLong ? 0x4C : 0x53));
     75     return str.hashCode();
     76 }
     77 
     78 /**
     79  * Comparer for partial location name hash key
     80  */
     81 static UBool U_CALLCONV
     82 comparePartialLocationKey(const UHashTok key1, const UHashTok key2) {
     83     PartialLocationKey *p1 = (PartialLocationKey *)key1.pointer;
     84     PartialLocationKey *p2 = (PartialLocationKey *)key2.pointer;
     85 
     86     if (p1 == p2) {
     87         return TRUE;
     88     }
     89     if (p1 == NULL || p2 == NULL) {
     90         return FALSE;
     91     }
     92     // We just check identity of tzID/mzID
     93     return (p1->tzID == p2->tzID && p1->mzID == p2->mzID && p1->isLong == p2->isLong);
     94 }
     95 
     96 /**
     97  * Deleter for GNameInfo
     98  */
     99 static void U_CALLCONV
    100 deleteGNameInfo(void *obj) {
    101     uprv_free(obj);
    102 }
    103 
    104 /**
    105  * GNameInfo stores zone name information in the local trie
    106  */
    107 typedef struct GNameInfo {
    108     UTimeZoneGenericNameType    type;
    109     const UChar*                tzID;
    110 } ZNameInfo;
    111 
    112 /**
    113  * GMatchInfo stores zone name match information used by find method
    114  */
    115 typedef struct GMatchInfo {
    116     const GNameInfo*    gnameInfo;
    117     int32_t             matchLength;
    118     UTimeZoneFormatTimeType   timeType;
    119 } ZMatchInfo;
    120 
    121 U_CDECL_END
    122 
    123 // ---------------------------------------------------
    124 // The class stores time zone generic name match information
    125 // ---------------------------------------------------
    126 class TimeZoneGenericNameMatchInfo : public UMemory {
    127 public:
    128     TimeZoneGenericNameMatchInfo(UVector* matches);
    129     ~TimeZoneGenericNameMatchInfo();
    130 
    131     int32_t size() const;
    132     UTimeZoneGenericNameType getGenericNameType(int32_t index) const;
    133     int32_t getMatchLength(int32_t index) const;
    134     UnicodeString& getTimeZoneID(int32_t index, UnicodeString& tzID) const;
    135 
    136 private:
    137     UVector* fMatches;  // vector of MatchEntry
    138 };
    139 
    140 TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches)
    141 : fMatches(matches) {
    142 }
    143 
    144 TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() {
    145     if (fMatches != NULL) {
    146         delete fMatches;
    147     }
    148 }
    149 
    150 int32_t
    151 TimeZoneGenericNameMatchInfo::size() const {
    152     if (fMatches == NULL) {
    153         return 0;
    154     }
    155     return fMatches->size();
    156 }
    157 
    158 UTimeZoneGenericNameType
    159 TimeZoneGenericNameMatchInfo::getGenericNameType(int32_t index) const {
    160     GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index);
    161     if (minfo != NULL) {
    162         return static_cast<UTimeZoneGenericNameType>(minfo->gnameInfo->type);
    163     }
    164     return UTZGNM_UNKNOWN;
    165 }
    166 
    167 int32_t
    168 TimeZoneGenericNameMatchInfo::getMatchLength(int32_t index) const {
    169     ZMatchInfo *minfo = (ZMatchInfo *)fMatches->elementAt(index);
    170     if (minfo != NULL) {
    171         return minfo->matchLength;
    172     }
    173     return -1;
    174 }
    175 
    176 UnicodeString&
    177 TimeZoneGenericNameMatchInfo::getTimeZoneID(int32_t index, UnicodeString& tzID) const {
    178     GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index);
    179     if (minfo != NULL && minfo->gnameInfo->tzID != NULL) {
    180         tzID.setTo(TRUE, minfo->gnameInfo->tzID, -1);
    181     } else {
    182         tzID.setToBogus();
    183     }
    184     return tzID;
    185 }
    186 
    187 // ---------------------------------------------------
    188 // GNameSearchHandler
    189 // ---------------------------------------------------
    190 class GNameSearchHandler : public TextTrieMapSearchResultHandler {
    191 public:
    192     GNameSearchHandler(uint32_t types);
    193     virtual ~GNameSearchHandler();
    194 
    195     UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
    196     UVector* getMatches(int32_t& maxMatchLen);
    197 
    198 private:
    199     uint32_t fTypes;
    200     UVector* fResults;
    201     int32_t fMaxMatchLen;
    202 };
    203 
    204 GNameSearchHandler::GNameSearchHandler(uint32_t types)
    205 : fTypes(types), fResults(NULL), fMaxMatchLen(0) {
    206 }
    207 
    208 GNameSearchHandler::~GNameSearchHandler() {
    209     if (fResults != NULL) {
    210         delete fResults;
    211     }
    212 }
    213 
    214 UBool
    215 GNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
    216     if (U_FAILURE(status)) {
    217         return FALSE;
    218     }
    219     if (node->hasValues()) {
    220         int32_t valuesCount = node->countValues();
    221         for (int32_t i = 0; i < valuesCount; i++) {
    222             GNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
    223             if (nameinfo == NULL) {
    224                 break;
    225             }
    226             if ((nameinfo->type & fTypes) != 0) {
    227                 // matches a requested type
    228                 if (fResults == NULL) {
    229                     fResults = new UVector(uprv_free, NULL, status);
    230                     if (fResults == NULL) {
    231                         status = U_MEMORY_ALLOCATION_ERROR;
    232                     }
    233                 }
    234                 if (U_SUCCESS(status)) {
    235                     U_ASSERT(fResults != NULL);
    236                     GMatchInfo *gmatch = (GMatchInfo *)uprv_malloc(sizeof(GMatchInfo));
    237                     if (gmatch == NULL) {
    238                         status = U_MEMORY_ALLOCATION_ERROR;
    239                     } else {
    240                         // add the match to the vector
    241                         gmatch->gnameInfo = nameinfo;
    242                         gmatch->matchLength = matchLength;
    243                         gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN;
    244                         fResults->addElement(gmatch, status);
    245                         if (U_FAILURE(status)) {
    246                             uprv_free(gmatch);
    247                         } else {
    248                             if (matchLength > fMaxMatchLen) {
    249                                 fMaxMatchLen = matchLength;
    250                             }
    251                         }
    252                     }
    253                 }
    254             }
    255         }
    256     }
    257     return TRUE;
    258 }
    259 
    260 UVector*
    261 GNameSearchHandler::getMatches(int32_t& maxMatchLen) {
    262     // give the ownership to the caller
    263     UVector *results = fResults;
    264     maxMatchLen = fMaxMatchLen;
    265 
    266     // reset
    267     fResults = NULL;
    268     fMaxMatchLen = 0;
    269     return results;
    270 }
    271 
    272 static UMutex gLock = U_MUTEX_INITIALIZER;
    273 
    274 class TZGNCore : public UMemory {
    275 public:
    276     TZGNCore(const Locale& locale, UErrorCode& status);
    277     virtual ~TZGNCore();
    278 
    279     UnicodeString& getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
    280                         UDate date, UnicodeString& name) const;
    281 
    282     UnicodeString& getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const;
    283 
    284     int32_t findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
    285         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const;
    286 
    287 private:
    288     Locale fLocale;
    289     const TimeZoneNames* fTimeZoneNames;
    290     UHashtable* fLocationNamesMap;
    291     UHashtable* fPartialLocationNamesMap;
    292 
    293     SimpleFormatter fRegionFormat;
    294     SimpleFormatter fFallbackFormat;
    295 
    296     LocaleDisplayNames* fLocaleDisplayNames;
    297     ZNStringPool fStringPool;
    298 
    299     TextTrieMap fGNamesTrie;
    300     UBool fGNamesTrieFullyLoaded;
    301 
    302     char fTargetRegion[ULOC_COUNTRY_CAPACITY];
    303 
    304     void initialize(const Locale& locale, UErrorCode& status);
    305     void cleanup();
    306 
    307     void loadStrings(const UnicodeString& tzCanonicalID);
    308 
    309     const UChar* getGenericLocationName(const UnicodeString& tzCanonicalID);
    310 
    311     UnicodeString& formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type,
    312                         UDate date, UnicodeString& name) const;
    313 
    314     UnicodeString& getPartialLocationName(const UnicodeString& tzCanonicalID,
    315                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
    316                         UnicodeString& name) const;
    317 
    318     const UChar* getPartialLocationName(const UnicodeString& tzCanonicalID,
    319                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName);
    320 
    321     TimeZoneGenericNameMatchInfo* findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
    322 
    323     TimeZoneNames::MatchInfoCollection* findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
    324 };
    325 
    326 
    327 // ---------------------------------------------------
    328 // TZGNCore - core implmentation of TimeZoneGenericNames
    329 //
    330 // TimeZoneGenericNames is parallel to TimeZoneNames,
    331 // but handles run-time generated time zone names.
    332 // This is the main part of this module.
    333 // ---------------------------------------------------
    334 TZGNCore::TZGNCore(const Locale& locale, UErrorCode& status)
    335 : fLocale(locale),
    336   fTimeZoneNames(NULL),
    337   fLocationNamesMap(NULL),
    338   fPartialLocationNamesMap(NULL),
    339   fLocaleDisplayNames(NULL),
    340   fStringPool(status),
    341   fGNamesTrie(TRUE, deleteGNameInfo),
    342   fGNamesTrieFullyLoaded(FALSE) {
    343     initialize(locale, status);
    344 }
    345 
    346 TZGNCore::~TZGNCore() {
    347     cleanup();
    348 }
    349 
    350 void
    351 TZGNCore::initialize(const Locale& locale, UErrorCode& status) {
    352     if (U_FAILURE(status)) {
    353         return;
    354     }
    355 
    356     // TimeZoneNames
    357     fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
    358     if (U_FAILURE(status)) {
    359         return;
    360     }
    361 
    362     // Initialize format patterns
    363     UnicodeString rpat(TRUE, gDefRegionPattern, -1);
    364     UnicodeString fpat(TRUE, gDefFallbackPattern, -1);
    365 
    366     UErrorCode tmpsts = U_ZERO_ERROR;   // OK with fallback warning..
    367     UResourceBundle *zoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
    368     zoneStrings = ures_getByKeyWithFallback(zoneStrings, gZoneStrings, zoneStrings, &tmpsts);
    369 
    370     if (U_SUCCESS(tmpsts)) {
    371         const UChar *regionPattern = ures_getStringByKeyWithFallback(zoneStrings, gRegionFormatTag, NULL, &tmpsts);
    372         if (U_SUCCESS(tmpsts) && u_strlen(regionPattern) > 0) {
    373             rpat.setTo(regionPattern, -1);
    374         }
    375         tmpsts = U_ZERO_ERROR;
    376         const UChar *fallbackPattern = ures_getStringByKeyWithFallback(zoneStrings, gFallbackFormatTag, NULL, &tmpsts);
    377         if (U_SUCCESS(tmpsts) && u_strlen(fallbackPattern) > 0) {
    378             fpat.setTo(fallbackPattern, -1);
    379         }
    380     }
    381     ures_close(zoneStrings);
    382 
    383     fRegionFormat.applyPatternMinMaxArguments(rpat, 1, 1, status);
    384     fFallbackFormat.applyPatternMinMaxArguments(fpat, 2, 2, status);
    385     if (U_FAILURE(status)) {
    386         cleanup();
    387         return;
    388     }
    389 
    390     // locale display names
    391     fLocaleDisplayNames = LocaleDisplayNames::createInstance(locale);
    392 
    393     // hash table for names - no key/value deleters
    394     fLocationNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
    395     if (U_FAILURE(status)) {
    396         cleanup();
    397         return;
    398     }
    399 
    400     fPartialLocationNamesMap = uhash_open(hashPartialLocationKey, comparePartialLocationKey, NULL, &status);
    401     if (U_FAILURE(status)) {
    402         cleanup();
    403         return;
    404     }
    405     uhash_setKeyDeleter(fPartialLocationNamesMap, uprv_free);
    406     // no value deleter
    407 
    408     // target region
    409     const char* region = fLocale.getCountry();
    410     int32_t regionLen = uprv_strlen(region);
    411     if (regionLen == 0) {
    412         char loc[ULOC_FULLNAME_CAPACITY];
    413         uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status);
    414 
    415         regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status);
    416         if (U_SUCCESS(status)) {
    417             fTargetRegion[regionLen] = 0;
    418         } else {
    419             cleanup();
    420             return;
    421         }
    422     } else if (regionLen < (int32_t)sizeof(fTargetRegion)) {
    423         uprv_strcpy(fTargetRegion, region);
    424     } else {
    425         fTargetRegion[0] = 0;
    426     }
    427 
    428     // preload generic names for the default zone
    429     TimeZone *tz = TimeZone::createDefault();
    430     const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
    431     if (tzID != NULL) {
    432         loadStrings(UnicodeString(TRUE, tzID, -1));
    433     }
    434     delete tz;
    435 }
    436 
    437 void
    438 TZGNCore::cleanup() {
    439     if (fLocaleDisplayNames != NULL) {
    440         delete fLocaleDisplayNames;
    441     }
    442     if (fTimeZoneNames != NULL) {
    443         delete fTimeZoneNames;
    444     }
    445 
    446     uhash_close(fLocationNamesMap);
    447     uhash_close(fPartialLocationNamesMap);
    448 }
    449 
    450 
    451 UnicodeString&
    452 TZGNCore::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
    453     name.setToBogus();
    454     switch (type) {
    455     case UTZGNM_LOCATION:
    456         {
    457             const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
    458             if (tzCanonicalID != NULL) {
    459                 getGenericLocationName(UnicodeString(TRUE, tzCanonicalID, -1), name);
    460             }
    461         }
    462         break;
    463     case UTZGNM_LONG:
    464     case UTZGNM_SHORT:
    465         formatGenericNonLocationName(tz, type, date, name);
    466         if (name.isEmpty()) {
    467             const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
    468             if (tzCanonicalID != NULL) {
    469                 getGenericLocationName(UnicodeString(TRUE, tzCanonicalID, -1), name);
    470             }
    471         }
    472         break;
    473     default:
    474         break;
    475     }
    476     return name;
    477 }
    478 
    479 UnicodeString&
    480 TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
    481     if (tzCanonicalID.isEmpty()) {
    482         name.setToBogus();
    483         return name;
    484     }
    485 
    486     const UChar *locname = NULL;
    487     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
    488     umtx_lock(&gLock);
    489     {
    490         locname = nonConstThis->getGenericLocationName(tzCanonicalID);
    491     }
    492     umtx_unlock(&gLock);
    493 
    494     if (locname == NULL) {
    495         name.setToBogus();
    496     } else {
    497         name.setTo(locname, u_strlen(locname));
    498     }
    499 
    500     return name;
    501 }
    502 
    503 /*
    504  * This method updates the cache and must be called with a lock
    505  */
    506 const UChar*
    507 TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID) {
    508     U_ASSERT(!tzCanonicalID.isEmpty());
    509     if (tzCanonicalID.length() > ZID_KEY_MAX) {
    510         return NULL;
    511     }
    512 
    513     UErrorCode status = U_ZERO_ERROR;
    514     UChar tzIDKey[ZID_KEY_MAX + 1];
    515     int32_t tzIDKeyLen = tzCanonicalID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
    516     U_ASSERT(status == U_ZERO_ERROR);   // already checked length above
    517     tzIDKey[tzIDKeyLen] = 0;
    518 
    519     const UChar *locname = (const UChar *)uhash_get(fLocationNamesMap, tzIDKey);
    520 
    521     if (locname != NULL) {
    522         // gEmpty indicate the name is not available
    523         if (locname == gEmpty) {
    524             return NULL;
    525         }
    526         return locname;
    527     }
    528 
    529     // Construct location name
    530     UnicodeString name;
    531     UnicodeString usCountryCode;
    532     UBool isPrimary = FALSE;
    533 
    534     ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode, &isPrimary);
    535 
    536     if (!usCountryCode.isEmpty()) {
    537         if (isPrimary) {
    538             // If this is the primary zone in the country, use the country name.
    539             char countryCode[ULOC_COUNTRY_CAPACITY];
    540             U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
    541             int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
    542             countryCode[ccLen] = 0;
    543 
    544             UnicodeString country;
    545             fLocaleDisplayNames->regionDisplayName(countryCode, country);
    546             fRegionFormat.format(country, name, status);
    547         } else {
    548             // If this is not the primary zone in the country,
    549             // use the exemplar city name.
    550 
    551             // getExemplarLocationName should retur non-empty string
    552             // if the time zone is associated with a region
    553 
    554             UnicodeString city;
    555             fTimeZoneNames->getExemplarLocationName(tzCanonicalID, city);
    556             fRegionFormat.format(city, name, status);
    557         }
    558         if (U_FAILURE(status)) {
    559             return NULL;
    560         }
    561     }
    562 
    563     locname = name.isEmpty() ? NULL : fStringPool.get(name, status);
    564     if (U_SUCCESS(status)) {
    565         // Cache the result
    566         const UChar* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID);
    567         U_ASSERT(cacheID != NULL);
    568         if (locname == NULL) {
    569             // gEmpty to indicate - no location name available
    570             uhash_put(fLocationNamesMap, (void *)cacheID, (void *)gEmpty, &status);
    571         } else {
    572             uhash_put(fLocationNamesMap, (void *)cacheID, (void *)locname, &status);
    573             if (U_FAILURE(status)) {
    574                 locname = NULL;
    575             } else {
    576                 // put the name info into the trie
    577                 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
    578                 if (nameinfo != NULL) {
    579                     nameinfo->type = UTZGNM_LOCATION;
    580                     nameinfo->tzID = cacheID;
    581                     fGNamesTrie.put(locname, nameinfo, status);
    582                 }
    583             }
    584         }
    585     }
    586 
    587     return locname;
    588 }
    589 
    590 UnicodeString&
    591 TZGNCore::formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
    592     U_ASSERT(type == UTZGNM_LONG || type == UTZGNM_SHORT);
    593     name.setToBogus();
    594 
    595     const UChar* uID = ZoneMeta::getCanonicalCLDRID(tz);
    596     if (uID == NULL) {
    597         return name;
    598     }
    599 
    600     UnicodeString tzID(TRUE, uID, -1);
    601 
    602     // Try to get a name from time zone first
    603     UTimeZoneNameType nameType = (type == UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC;
    604     fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name);
    605 
    606     if (!name.isEmpty()) {
    607         return name;
    608     }
    609 
    610     // Try meta zone
    611     UChar mzIDBuf[32];
    612     UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
    613     fTimeZoneNames->getMetaZoneID(tzID, date, mzID);
    614     if (!mzID.isEmpty()) {
    615         UErrorCode status = U_ZERO_ERROR;
    616         UBool useStandard = FALSE;
    617         int32_t raw, sav;
    618         UChar tmpNameBuf[ZONE_NAME_U16_MAX];
    619 
    620         tz.getOffset(date, FALSE, raw, sav, status);
    621         if (U_FAILURE(status)) {
    622             return name;
    623         }
    624 
    625         if (sav == 0) {
    626             useStandard = TRUE;
    627 
    628             TimeZone *tmptz = tz.clone();
    629             // Check if the zone actually uses daylight saving time around the time
    630             BasicTimeZone *btz = NULL;
    631             if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL
    632                 || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL
    633                 || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL
    634                 || dynamic_cast<VTimeZone *>(tmptz) != NULL) {
    635                 btz = (BasicTimeZone*)tmptz;
    636             }
    637 
    638             if (btz != NULL) {
    639                 TimeZoneTransition before;
    640                 UBool beforTrs = btz->getPreviousTransition(date, TRUE, before);
    641                 if (beforTrs
    642                         && (date - before.getTime() < kDstCheckRange)
    643                         && before.getFrom()->getDSTSavings() != 0) {
    644                     useStandard = FALSE;
    645                 } else {
    646                     TimeZoneTransition after;
    647                     UBool afterTrs = btz->getNextTransition(date, FALSE, after);
    648                     if (afterTrs
    649                             && (after.getTime() - date < kDstCheckRange)
    650                             && after.getTo()->getDSTSavings() != 0) {
    651                         useStandard = FALSE;
    652                     }
    653                 }
    654             } else {
    655                 // If not BasicTimeZone... only if the instance is not an ICU's implementation.
    656                 // We may get a wrong answer in edge case, but it should practically work OK.
    657                 tmptz->getOffset(date - kDstCheckRange, FALSE, raw, sav, status);
    658                 if (sav != 0) {
    659                     useStandard = FALSE;
    660                 } else {
    661                     tmptz->getOffset(date + kDstCheckRange, FALSE, raw, sav, status);
    662                     if (sav != 0){
    663                         useStandard = FALSE;
    664                     }
    665                 }
    666                 if (U_FAILURE(status)) {
    667                     delete tmptz;
    668                     return name;
    669                 }
    670             }
    671             delete tmptz;
    672         }
    673         if (useStandard) {
    674             UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC)
    675                 ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD;
    676             UnicodeString stdName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));
    677             fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName);
    678             if (!stdName.isEmpty()) {
    679                 name.setTo(stdName);
    680 
    681                 // TODO: revisit this issue later
    682                 // In CLDR, a same display name is used for both generic and standard
    683                 // for some meta zones in some locales.  This looks like a data bugs.
    684                 // For now, we check if the standard name is different from its generic
    685                 // name below.
    686                 UChar genNameBuf[ZONE_NAME_U16_MAX];
    687                 UnicodeString mzGenericName(genNameBuf, 0, UPRV_LENGTHOF(genNameBuf));
    688                 fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName);
    689                 if (stdName.caseCompare(mzGenericName, 0) == 0) {
    690                     name.setToBogus();
    691                 }
    692             }
    693         }
    694         if (name.isEmpty()) {
    695             // Get a name from meta zone
    696             UnicodeString mzName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));
    697             fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName);
    698             if (!mzName.isEmpty()) {
    699                 // Check if we need to use a partial location format.
    700                 // This check is done by comparing offset with the meta zone's
    701                 // golden zone at the given date.
    702                 UChar idBuf[32];
    703                 UnicodeString goldenID(idBuf, 0, UPRV_LENGTHOF(idBuf));
    704                 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, goldenID);
    705                 if (!goldenID.isEmpty() && goldenID != tzID) {
    706                     TimeZone *goldenZone = TimeZone::createTimeZone(goldenID);
    707                     int32_t raw1, sav1;
    708 
    709                     // Check offset in the golden zone with wall time.
    710                     // With getOffset(date, false, offsets1),
    711                     // you may get incorrect results because of time overlap at DST->STD
    712                     // transition.
    713                     goldenZone->getOffset(date + raw + sav, TRUE, raw1, sav1, status);
    714                     delete goldenZone;
    715                     if (U_SUCCESS(status)) {
    716                         if (raw != raw1 || sav != sav1) {
    717                             // Now we need to use a partial location format
    718                             getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name);
    719                         } else {
    720                             name.setTo(mzName);
    721                         }
    722                     }
    723                 } else {
    724                     name.setTo(mzName);
    725                 }
    726             }
    727         }
    728     }
    729     return name;
    730 }
    731 
    732 UnicodeString&
    733 TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
    734                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
    735                         UnicodeString& name) const {
    736     name.setToBogus();
    737     if (tzCanonicalID.isEmpty() || mzID.isEmpty() || mzDisplayName.isEmpty()) {
    738         return name;
    739     }
    740 
    741     const UChar *uplname = NULL;
    742     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
    743     umtx_lock(&gLock);
    744     {
    745         uplname = nonConstThis->getPartialLocationName(tzCanonicalID, mzID, isLong, mzDisplayName);
    746     }
    747     umtx_unlock(&gLock);
    748 
    749     if (uplname == NULL) {
    750         name.setToBogus();
    751     } else {
    752         name.setTo(TRUE, uplname, -1);
    753     }
    754     return name;
    755 }
    756 
    757 /*
    758  * This method updates the cache and must be called with a lock
    759  */
    760 const UChar*
    761 TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
    762                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) {
    763     U_ASSERT(!tzCanonicalID.isEmpty());
    764     U_ASSERT(!mzID.isEmpty());
    765     U_ASSERT(!mzDisplayName.isEmpty());
    766 
    767     PartialLocationKey key;
    768     key.tzID = ZoneMeta::findTimeZoneID(tzCanonicalID);
    769     key.mzID = ZoneMeta::findMetaZoneID(mzID);
    770     key.isLong = isLong;
    771     U_ASSERT(key.tzID != NULL && key.mzID != NULL);
    772 
    773     const UChar* uplname = (const UChar*)uhash_get(fPartialLocationNamesMap, (void *)&key);
    774     if (uplname != NULL) {
    775         return uplname;
    776     }
    777 
    778     UnicodeString location;
    779     UnicodeString usCountryCode;
    780     ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode);
    781     if (!usCountryCode.isEmpty()) {
    782         char countryCode[ULOC_COUNTRY_CAPACITY];
    783         U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
    784         int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
    785         countryCode[ccLen] = 0;
    786 
    787         UnicodeString regionalGolden;
    788         fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden);
    789         if (tzCanonicalID == regionalGolden) {
    790             // Use country name
    791             fLocaleDisplayNames->regionDisplayName(countryCode, location);
    792         } else {
    793             // Otherwise, use exemplar city name
    794             fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
    795         }
    796     } else {
    797         fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
    798         if (location.isEmpty()) {
    799             // This could happen when the time zone is not associated with a country,
    800             // and its ID is not hierarchical, for example, CST6CDT.
    801             // We use the canonical ID itself as the location for this case.
    802             location.setTo(tzCanonicalID);
    803         }
    804     }
    805 
    806     UErrorCode status = U_ZERO_ERROR;
    807     UnicodeString name;
    808     fFallbackFormat.format(location, mzDisplayName, name, status);
    809     if (U_FAILURE(status)) {
    810         return NULL;
    811     }
    812 
    813     uplname = fStringPool.get(name, status);
    814     if (U_SUCCESS(status)) {
    815         // Add the name to cache
    816         PartialLocationKey* cacheKey = (PartialLocationKey *)uprv_malloc(sizeof(PartialLocationKey));
    817         if (cacheKey != NULL) {
    818             cacheKey->tzID = key.tzID;
    819             cacheKey->mzID = key.mzID;
    820             cacheKey->isLong = key.isLong;
    821             uhash_put(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status);
    822             if (U_FAILURE(status)) {
    823                 uprv_free(cacheKey);
    824             } else {
    825                 // put the name to the local trie as well
    826                 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
    827                 if (nameinfo != NULL) {
    828                     nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT;
    829                     nameinfo->tzID = key.tzID;
    830                     fGNamesTrie.put(uplname, nameinfo, status);
    831                 }
    832             }
    833         }
    834     }
    835     return uplname;
    836 }
    837 
    838 /*
    839  * This method updates the cache and must be called with a lock,
    840  * except initializer.
    841  */
    842 void
    843 TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) {
    844     // load the generic location name
    845     getGenericLocationName(tzCanonicalID);
    846 
    847     // partial location names
    848     UErrorCode status = U_ZERO_ERROR;
    849 
    850     const UnicodeString *mzID;
    851     UnicodeString goldenID;
    852     UnicodeString mzGenName;
    853     UTimeZoneNameType genNonLocTypes[] = {
    854         UTZNM_LONG_GENERIC, UTZNM_SHORT_GENERIC,
    855         UTZNM_UNKNOWN /*terminator*/
    856     };
    857 
    858     StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status);
    859     while ((mzID = mzIDs->snext(status)) != NULL) {
    860         if (U_FAILURE(status)) {
    861             break;
    862         }
    863         // if this time zone is not the golden zone of the meta zone,
    864         // partial location name (such as "PT (Los Angeles)") might be
    865         // available.
    866         fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion, goldenID);
    867         if (tzCanonicalID != goldenID) {
    868             for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) {
    869                 fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName);
    870                 if (!mzGenName.isEmpty()) {
    871                     // getPartialLocationName formats a name and put it into the trie
    872                     getPartialLocationName(tzCanonicalID, *mzID,
    873                         (genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName);
    874                 }
    875             }
    876         }
    877     }
    878     if (mzIDs != NULL) {
    879         delete mzIDs;
    880     }
    881 }
    882 
    883 int32_t
    884 TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
    885         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
    886     timeType = UTZFMT_TIME_TYPE_UNKNOWN;
    887     tzID.setToBogus();
    888 
    889     if (U_FAILURE(status)) {
    890         return 0;
    891     }
    892 
    893     // Find matches in the TimeZoneNames first
    894     TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status);
    895     if (U_FAILURE(status)) {
    896         return 0;
    897     }
    898 
    899     int32_t bestMatchLen = 0;
    900     UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
    901     UnicodeString bestMatchTzID;
    902     // UBool isLongStandard = FALSE;   // workaround - see the comments below
    903     UBool isStandard = FALSE;       // TODO: Temporary hack (on hack) for short standard name/location name conflict (found in zh_Hant), should be removed after CLDR 21m1 integration
    904 
    905     if (tznamesMatches != NULL) {
    906         UnicodeString mzID;
    907         for (int32_t i = 0; i < tznamesMatches->size(); i++) {
    908             int32_t len = tznamesMatches->getMatchLengthAt(i);
    909             if (len > bestMatchLen) {
    910                 bestMatchLen = len;
    911                 if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) {
    912                     // name for a meta zone
    913                     if (tznamesMatches->getMetaZoneIDAt(i, mzID)) {
    914                         fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, bestMatchTzID);
    915                     }
    916                 }
    917                 UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i);
    918                 if (U_FAILURE(status)) {
    919                     break;
    920                 }
    921                 switch (nameType) {
    922                 case UTZNM_LONG_STANDARD:
    923                     // isLongStandard = TRUE;
    924                 case UTZNM_SHORT_STANDARD:  // this one is never used for generic, but just in case
    925                     isStandard = TRUE;      // TODO: Remove this later, see the comments above.
    926                     bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD;
    927                     break;
    928                 case UTZNM_LONG_DAYLIGHT:
    929                 case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case
    930                     bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT;
    931                     break;
    932                 default:
    933                     bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
    934                 }
    935             }
    936         }
    937         delete tznamesMatches;
    938         if (U_FAILURE(status)) {
    939             return 0;
    940         }
    941 
    942         if (bestMatchLen == (text.length() - start)) {
    943             // Full match
    944 
    945             //tzID.setTo(bestMatchTzID);
    946             //timeType = bestMatchTimeType;
    947             //return bestMatchLen;
    948 
    949             // TODO Some time zone uses a same name for the long standard name
    950             // and the location name. When the match is a long standard name,
    951             // then we need to check if the name is same with the location name.
    952             // This is probably a data error or a design bug.
    953 /*
    954             if (!isLongStandard) {
    955                 tzID.setTo(bestMatchTzID);
    956                 timeType = bestMatchTimeType;
    957                 return bestMatchLen;
    958             }
    959 */
    960             // TODO The deprecation of commonlyUsed flag introduced the name
    961             // conflict not only for long standard names, but short standard names too.
    962             // These short names (found in zh_Hant) should be gone once we clean
    963             // up CLDR time zone display name data. Once the short name conflict
    964             // problem (with location name) is resolved, we should change the condition
    965             // below back to the original one above. -Yoshito (2011-09-14)
    966             if (!isStandard) {
    967                 tzID.setTo(bestMatchTzID);
    968                 timeType = bestMatchTimeType;
    969                 return bestMatchLen;
    970             }
    971         }
    972     }
    973 
    974     // Find matches in the local trie
    975     TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status);
    976     if (U_FAILURE(status)) {
    977         return 0;
    978     }
    979     if (localMatches != NULL) {
    980         for (int32_t i = 0; i < localMatches->size(); i++) {
    981             int32_t len = localMatches->getMatchLength(i);
    982 
    983             // TODO See the above TODO. We use len >= bestMatchLen
    984             // because of the long standard/location name collision
    985             // problem. If it is also a location name, carrying
    986             // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a
    987             // problem in SimpleDateFormat
    988             if (len >= bestMatchLen) {
    989                 bestMatchLen = localMatches->getMatchLength(i);
    990                 bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;   // because generic
    991                 localMatches->getTimeZoneID(i, bestMatchTzID);
    992             }
    993         }
    994         delete localMatches;
    995     }
    996 
    997     if (bestMatchLen > 0) {
    998         timeType = bestMatchTimeType;
    999         tzID.setTo(bestMatchTzID);
   1000     }
   1001     return bestMatchLen;
   1002 }
   1003 
   1004 TimeZoneGenericNameMatchInfo*
   1005 TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
   1006     GNameSearchHandler handler(types);
   1007 
   1008     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
   1009 
   1010     umtx_lock(&gLock);
   1011     {
   1012         fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
   1013     }
   1014     umtx_unlock(&gLock);
   1015 
   1016     if (U_FAILURE(status)) {
   1017         return NULL;
   1018     }
   1019 
   1020     TimeZoneGenericNameMatchInfo *gmatchInfo = NULL;
   1021 
   1022     int32_t maxLen = 0;
   1023     UVector *results = handler.getMatches(maxLen);
   1024     if (results != NULL && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) {
   1025         // perfect match
   1026         gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
   1027         if (gmatchInfo == NULL) {
   1028             status = U_MEMORY_ALLOCATION_ERROR;
   1029             delete results;
   1030             return NULL;
   1031         }
   1032         return gmatchInfo;
   1033     }
   1034 
   1035     if (results != NULL) {
   1036         delete results;
   1037     }
   1038 
   1039     // All names are not yet loaded into the local trie.
   1040     // Load all available names into the trie. This could be very heavy.
   1041     umtx_lock(&gLock);
   1042     {
   1043         if (!fGNamesTrieFullyLoaded) {
   1044             StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
   1045             if (U_SUCCESS(status)) {
   1046                 const UnicodeString *tzID;
   1047                 while ((tzID = tzIDs->snext(status)) != NULL) {
   1048                     if (U_FAILURE(status)) {
   1049                         break;
   1050                     }
   1051                     nonConstThis->loadStrings(*tzID);
   1052                 }
   1053             }
   1054             if (tzIDs != NULL) {
   1055                 delete tzIDs;
   1056             }
   1057 
   1058             if (U_SUCCESS(status)) {
   1059                 nonConstThis->fGNamesTrieFullyLoaded = TRUE;
   1060             }
   1061         }
   1062     }
   1063     umtx_unlock(&gLock);
   1064 
   1065     if (U_FAILURE(status)) {
   1066         return NULL;
   1067     }
   1068 
   1069     umtx_lock(&gLock);
   1070     {
   1071         // now try it again
   1072         fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
   1073     }
   1074     umtx_unlock(&gLock);
   1075 
   1076     results = handler.getMatches(maxLen);
   1077     if (results != NULL && maxLen > 0) {
   1078         gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
   1079         if (gmatchInfo == NULL) {
   1080             status = U_MEMORY_ALLOCATION_ERROR;
   1081             delete results;
   1082             return NULL;
   1083         }
   1084     }
   1085 
   1086     return gmatchInfo;
   1087 }
   1088 
   1089 TimeZoneNames::MatchInfoCollection*
   1090 TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
   1091     // Check if the target name typs is really in the TimeZoneNames
   1092     uint32_t nameTypes = 0;
   1093     if (types & UTZGNM_LONG) {
   1094         nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD);
   1095     }
   1096     if (types & UTZGNM_SHORT) {
   1097         nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD);
   1098     }
   1099 
   1100     if (types) {
   1101         // Find matches in the TimeZoneNames
   1102         return fTimeZoneNames->find(text, start, nameTypes, status);
   1103     }
   1104 
   1105     return NULL;
   1106 }
   1107 
   1108 typedef struct TZGNCoreRef {
   1109     TZGNCore*       obj;
   1110     int32_t         refCount;
   1111     double          lastAccess;
   1112 } TZGNCoreRef;
   1113 
   1114 // TZGNCore object cache handling
   1115 static UMutex gTZGNLock = U_MUTEX_INITIALIZER;
   1116 static UHashtable *gTZGNCoreCache = NULL;
   1117 static UBool gTZGNCoreCacheInitialized = FALSE;
   1118 
   1119 // Access count - incremented every time up to SWEEP_INTERVAL,
   1120 // then reset to 0
   1121 static int32_t gAccessCount = 0;
   1122 
   1123 // Interval for calling the cache sweep function - every 100 times
   1124 #define SWEEP_INTERVAL 100
   1125 
   1126 // Cache expiration in millisecond. When a cached entry is no
   1127 // longer referenced and exceeding this threshold since last
   1128 // access time, then the cache entry will be deleted by the sweep
   1129 // function. For now, 3 minutes.
   1130 #define CACHE_EXPIRATION 180000.0
   1131 
   1132 U_CDECL_BEGIN
   1133 /**
   1134  * Cleanup callback func
   1135  */
   1136 static UBool U_CALLCONV tzgnCore_cleanup(void)
   1137 {
   1138     if (gTZGNCoreCache != NULL) {
   1139         uhash_close(gTZGNCoreCache);
   1140         gTZGNCoreCache = NULL;
   1141     }
   1142     gTZGNCoreCacheInitialized = FALSE;
   1143     return TRUE;
   1144 }
   1145 
   1146 /**
   1147  * Deleter for TZGNCoreRef
   1148  */
   1149 static void U_CALLCONV
   1150 deleteTZGNCoreRef(void *obj) {
   1151     icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj;
   1152     delete (icu::TZGNCore*) entry->obj;
   1153     uprv_free(entry);
   1154 }
   1155 U_CDECL_END
   1156 
   1157 /**
   1158  * Function used for removing unreferrenced cache entries exceeding
   1159  * the expiration time. This function must be called with in the mutex
   1160  * block.
   1161  */
   1162 static void sweepCache() {
   1163     int32_t pos = UHASH_FIRST;
   1164     const UHashElement* elem;
   1165     double now = (double)uprv_getUTCtime();
   1166 
   1167     while ((elem = uhash_nextElement(gTZGNCoreCache, &pos)) != NULL) {
   1168         TZGNCoreRef *entry = (TZGNCoreRef *)elem->value.pointer;
   1169         if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
   1170             // delete this entry
   1171             uhash_removeElement(gTZGNCoreCache, elem);
   1172         }
   1173     }
   1174 }
   1175 
   1176 TimeZoneGenericNames::TimeZoneGenericNames()
   1177 : fRef(0) {
   1178 }
   1179 
   1180 TimeZoneGenericNames::~TimeZoneGenericNames() {
   1181     umtx_lock(&gTZGNLock);
   1182     {
   1183         U_ASSERT(fRef->refCount > 0);
   1184         // Just decrement the reference count
   1185         fRef->refCount--;
   1186     }
   1187     umtx_unlock(&gTZGNLock);
   1188 }
   1189 
   1190 TimeZoneGenericNames*
   1191 TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) {
   1192     if (U_FAILURE(status)) {
   1193         return NULL;
   1194     }
   1195     TimeZoneGenericNames* instance = new TimeZoneGenericNames();
   1196     if (instance == NULL) {
   1197         status = U_MEMORY_ALLOCATION_ERROR;
   1198         return NULL;
   1199     }
   1200 
   1201     TZGNCoreRef *cacheEntry = NULL;
   1202     {
   1203         Mutex lock(&gTZGNLock);
   1204 
   1205         if (!gTZGNCoreCacheInitialized) {
   1206             // Create empty hashtable
   1207             gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
   1208             if (U_SUCCESS(status)) {
   1209                 uhash_setKeyDeleter(gTZGNCoreCache, uprv_free);
   1210                 uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef);
   1211                 gTZGNCoreCacheInitialized = TRUE;
   1212                 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup);
   1213             }
   1214         }
   1215         if (U_FAILURE(status)) {
   1216             return NULL;
   1217         }
   1218 
   1219         // Check the cache, if not available, create new one and cache
   1220         const char *key = locale.getName();
   1221         cacheEntry = (TZGNCoreRef *)uhash_get(gTZGNCoreCache, key);
   1222         if (cacheEntry == NULL) {
   1223             TZGNCore *tzgnCore = NULL;
   1224             char *newKey = NULL;
   1225 
   1226             tzgnCore = new TZGNCore(locale, status);
   1227             if (tzgnCore == NULL) {
   1228                 status = U_MEMORY_ALLOCATION_ERROR;
   1229             }
   1230             if (U_SUCCESS(status)) {
   1231                 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1);
   1232                 if (newKey == NULL) {
   1233                     status = U_MEMORY_ALLOCATION_ERROR;
   1234                 } else {
   1235                     uprv_strcpy(newKey, key);
   1236                 }
   1237             }
   1238             if (U_SUCCESS(status)) {
   1239                 cacheEntry = (TZGNCoreRef *)uprv_malloc(sizeof(TZGNCoreRef));
   1240                 if (cacheEntry == NULL) {
   1241                     status = U_MEMORY_ALLOCATION_ERROR;
   1242                 } else {
   1243                     cacheEntry->obj = tzgnCore;
   1244                     cacheEntry->refCount = 1;
   1245                     cacheEntry->lastAccess = (double)uprv_getUTCtime();
   1246 
   1247                     uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status);
   1248                 }
   1249             }
   1250             if (U_FAILURE(status)) {
   1251                 if (tzgnCore != NULL) {
   1252                     delete tzgnCore;
   1253                 }
   1254                 if (newKey != NULL) {
   1255                     uprv_free(newKey);
   1256                 }
   1257                 if (cacheEntry != NULL) {
   1258                     uprv_free(cacheEntry);
   1259                 }
   1260                 cacheEntry = NULL;
   1261             }
   1262         } else {
   1263             // Update the reference count
   1264             cacheEntry->refCount++;
   1265             cacheEntry->lastAccess = (double)uprv_getUTCtime();
   1266         }
   1267         gAccessCount++;
   1268         if (gAccessCount >= SWEEP_INTERVAL) {
   1269             // sweep
   1270             sweepCache();
   1271             gAccessCount = 0;
   1272         }
   1273     }  // End of mutex locked block
   1274 
   1275     if (cacheEntry == NULL) {
   1276         delete instance;
   1277         return NULL;
   1278     }
   1279 
   1280     instance->fRef = cacheEntry;
   1281     return instance;
   1282 }
   1283 
   1284 UBool
   1285 TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const {
   1286     // Just compare if the other object also use the same
   1287     // ref entry
   1288     return fRef == other.fRef;
   1289 }
   1290 
   1291 TimeZoneGenericNames*
   1292 TimeZoneGenericNames::clone() const {
   1293     TimeZoneGenericNames* other = new TimeZoneGenericNames();
   1294     if (other) {
   1295         umtx_lock(&gTZGNLock);
   1296         {
   1297             // Just increments the reference count
   1298             fRef->refCount++;
   1299             other->fRef = fRef;
   1300         }
   1301         umtx_unlock(&gTZGNLock);
   1302     }
   1303     return other;
   1304 }
   1305 
   1306 UnicodeString&
   1307 TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
   1308                         UDate date, UnicodeString& name) const {
   1309     return fRef->obj->getDisplayName(tz, type, date, name);
   1310 }
   1311 
   1312 UnicodeString&
   1313 TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
   1314     return fRef->obj->getGenericLocationName(tzCanonicalID, name);
   1315 }
   1316 
   1317 int32_t
   1318 TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
   1319         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
   1320     return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status);
   1321 }
   1322 
   1323 U_NAMESPACE_END
   1324 #endif
   1325