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