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