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