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