• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 * Copyright (C) 2011-2012, 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     UBool isSingleCountry = FALSE;
550     UnicodeString usCountryCode;
551     ZoneMeta::getSingleCountry(tzCanonicalID, usCountryCode);
552     if (!usCountryCode.isEmpty()) {
553         isSingleCountry = TRUE;
554     } else {
555         ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode);
556     }
557 
558     if (!usCountryCode.isEmpty()) {
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         // Format
568         FieldPosition fpos;
569         if (isSingleCountry) {
570             // If this is only the single zone in the country, use the country name
571             Formattable param[] = {
572                 Formattable(country)
573             };
574             fRegionFormat->format(param, 1, name, fpos, status);
575         } else {
576             // If there are multiple zones including this in the country,
577             // use the exemplar city name
578 
579             // getExemplarLocationName should retur non-empty string
580             // if the time zone is associated with a region
581 
582             UnicodeString city;
583             fTimeZoneNames->getExemplarLocationName(tzCanonicalID, city);
584 
585             Formattable param[] = {
586                 Formattable(city),
587             };
588             fRegionFormat->format(param, 1, name, fpos, status);
589         }
590         if (U_FAILURE(status)) {
591             return NULL;
592         }
593     }
594 
595     locname = name.isEmpty() ? NULL : fStringPool.get(name, status);
596     if (U_SUCCESS(status)) {
597         // Cache the result
598         const UChar* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID);
599         U_ASSERT(cacheID != NULL);
600         if (locname == NULL) {
601             // gEmpty to indicate - no location name available
602             uhash_put(fLocationNamesMap, (void *)cacheID, (void *)gEmpty, &status);
603         } else {
604             uhash_put(fLocationNamesMap, (void *)cacheID, (void *)locname, &status);
605             if (U_FAILURE(status)) {
606                 locname = NULL;
607             } else {
608                 // put the name info into the trie
609                 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
610                 if (nameinfo != NULL) {
611                     nameinfo->type = UTZGNM_LOCATION;
612                     nameinfo->tzID = cacheID;
613                     fGNamesTrie.put(locname, nameinfo, status);
614                 }
615             }
616         }
617     }
618 
619     return locname;
620 }
621 
622 UnicodeString&
formatGenericNonLocationName(const TimeZone & tz,UTimeZoneGenericNameType type,UDate date,UnicodeString & name) const623 TZGNCore::formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
624     U_ASSERT(type == UTZGNM_LONG || type == UTZGNM_SHORT);
625     name.setToBogus();
626 
627     const UChar* uID = ZoneMeta::getCanonicalCLDRID(tz);
628     if (uID == NULL) {
629         return name;
630     }
631 
632     UnicodeString tzID(uID);
633 
634     // Try to get a name from time zone first
635     UTimeZoneNameType nameType = (type == UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC;
636     fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name);
637 
638     if (!name.isEmpty()) {
639         return name;
640     }
641 
642     // Try meta zone
643     UnicodeString mzID;
644     fTimeZoneNames->getMetaZoneID(tzID, date, mzID);
645     if (!mzID.isEmpty()) {
646         UErrorCode status = U_ZERO_ERROR;
647         UBool useStandard = FALSE;
648         int32_t raw, sav;
649 
650         tz.getOffset(date, FALSE, raw, sav, status);
651         if (U_FAILURE(status)) {
652             return name;
653         }
654 
655         if (sav == 0) {
656             useStandard = TRUE;
657 
658             TimeZone *tmptz = tz.clone();
659             // Check if the zone actually uses daylight saving time around the time
660             BasicTimeZone *btz = NULL;
661             if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL
662                 || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL
663                 || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL
664                 || dynamic_cast<VTimeZone *>(tmptz) != NULL) {
665                 btz = (BasicTimeZone*)tmptz;
666             }
667 
668             if (btz != NULL) {
669                 TimeZoneTransition before;
670                 UBool beforTrs = btz->getPreviousTransition(date, TRUE, before);
671                 if (beforTrs
672                         && (date - before.getTime() < kDstCheckRange)
673                         && before.getFrom()->getDSTSavings() != 0) {
674                     useStandard = FALSE;
675                 } else {
676                     TimeZoneTransition after;
677                     UBool afterTrs = btz->getNextTransition(date, FALSE, after);
678                     if (afterTrs
679                             && (after.getTime() - date < kDstCheckRange)
680                             && after.getTo()->getDSTSavings() != 0) {
681                         useStandard = FALSE;
682                     }
683                 }
684             } else {
685                 // If not BasicTimeZone... only if the instance is not an ICU's implementation.
686                 // We may get a wrong answer in edge case, but it should practically work OK.
687                 tmptz->getOffset(date - kDstCheckRange, FALSE, raw, sav, status);
688                 if (sav != 0) {
689                     useStandard = FALSE;
690                 } else {
691                     tmptz->getOffset(date + kDstCheckRange, FALSE, raw, sav, status);
692                     if (sav != 0){
693                         useStandard = FALSE;
694                     }
695                 }
696                 if (U_FAILURE(status)) {
697                     delete tmptz;
698                     return name;
699                 }
700             }
701             delete tmptz;
702         }
703         if (useStandard) {
704             UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC)
705                 ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD;
706             UnicodeString stdName;
707             fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName);
708             if (!stdName.isEmpty()) {
709                 name.setTo(stdName);
710 
711                 // TODO: revisit this issue later
712                 // In CLDR, a same display name is used for both generic and standard
713                 // for some meta zones in some locales.  This looks like a data bugs.
714                 // For now, we check if the standard name is different from its generic
715                 // name below.
716                 UnicodeString mzGenericName;
717                 fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName);
718                 if (stdName.caseCompare(mzGenericName, 0) == 0) {
719                     name.setToBogus();
720                 }
721             }
722         }
723         if (name.isEmpty()) {
724             // Get a name from meta zone
725             UnicodeString mzName;
726             fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName);
727             if (!mzName.isEmpty()) {
728                 // Check if we need to use a partial location format.
729                 // This check is done by comparing offset with the meta zone's
730                 // golden zone at the given date.
731                 UnicodeString goldenID;
732                 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, goldenID);
733                 if (!goldenID.isEmpty() && goldenID != tzID) {
734                     TimeZone *goldenZone = TimeZone::createTimeZone(goldenID);
735                     int32_t raw1, sav1;
736 
737                     // Check offset in the golden zone with wall time.
738                     // With getOffset(date, false, offsets1),
739                     // you may get incorrect results because of time overlap at DST->STD
740                     // transition.
741                     goldenZone->getOffset(date + raw + sav, TRUE, raw1, sav1, status);
742                     delete goldenZone;
743                     if (U_SUCCESS(status)) {
744                         if (raw != raw1 || sav != sav1) {
745                             // Now we need to use a partial location format
746                             getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name);
747                         } else {
748                             name.setTo(mzName);
749                         }
750                     }
751                 } else {
752                     name.setTo(mzName);
753                 }
754             }
755         }
756     }
757     return name;
758 }
759 
760 UnicodeString&
getPartialLocationName(const UnicodeString & tzCanonicalID,const UnicodeString & mzID,UBool isLong,const UnicodeString & mzDisplayName,UnicodeString & name) const761 TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
762                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
763                         UnicodeString& name) const {
764     name.setToBogus();
765     if (tzCanonicalID.isEmpty() || mzID.isEmpty() || mzDisplayName.isEmpty()) {
766         return name;
767     }
768 
769     const UChar *uplname = NULL;
770     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
771     umtx_lock(&gLock);
772     {
773         uplname = nonConstThis->getPartialLocationName(tzCanonicalID, mzID, isLong, mzDisplayName);
774     }
775     umtx_unlock(&gLock);
776 
777     if (uplname == NULL) {
778         name.setToBogus();
779     } else {
780         name.setTo(TRUE, uplname, -1);
781     }
782     return name;
783 }
784 
785 /*
786  * This method updates the cache and must be called with a lock
787  */
788 const UChar*
getPartialLocationName(const UnicodeString & tzCanonicalID,const UnicodeString & mzID,UBool isLong,const UnicodeString & mzDisplayName)789 TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
790                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) {
791     U_ASSERT(!tzCanonicalID.isEmpty());
792     U_ASSERT(!mzID.isEmpty());
793     U_ASSERT(!mzDisplayName.isEmpty());
794 
795     PartialLocationKey key;
796     key.tzID = ZoneMeta::findTimeZoneID(tzCanonicalID);
797     key.mzID = ZoneMeta::findMetaZoneID(mzID);
798     key.isLong = isLong;
799     U_ASSERT(key.tzID != NULL && key.mzID != NULL);
800 
801     const UChar* uplname = (const UChar*)uhash_get(fPartialLocationNamesMap, (void *)&key);
802     if (uplname != NULL) {
803         return uplname;
804     }
805 
806     UnicodeString location;
807     UnicodeString usCountryCode;
808     ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode);
809     if (!usCountryCode.isEmpty()) {
810         char countryCode[ULOC_COUNTRY_CAPACITY];
811         U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
812         int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
813         countryCode[ccLen] = 0;
814 
815         UnicodeString regionalGolden;
816         fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden);
817         if (tzCanonicalID == regionalGolden) {
818             // Use country name
819             fLocaleDisplayNames->regionDisplayName(countryCode, location);
820         } else {
821             // Otherwise, use exemplar city name
822             fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
823         }
824     } else {
825         fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
826         if (location.isEmpty()) {
827             // This could happen when the time zone is not associated with a country,
828             // and its ID is not hierarchical, for example, CST6CDT.
829             // We use the canonical ID itself as the location for this case.
830             location.setTo(tzCanonicalID);
831         }
832     }
833 
834     UErrorCode status = U_ZERO_ERROR;
835     UnicodeString name;
836 
837     FieldPosition fpos;
838     Formattable param[] = {
839         Formattable(location),
840         Formattable(mzDisplayName)
841     };
842     fFallbackFormat->format(param, 2, name, fpos, status);
843     if (U_FAILURE(status)) {
844         return NULL;
845     }
846 
847     uplname = fStringPool.get(name, status);
848     if (U_SUCCESS(status)) {
849         // Add the name to cache
850         PartialLocationKey* cacheKey = (PartialLocationKey *)uprv_malloc(sizeof(PartialLocationKey));
851         if (cacheKey != NULL) {
852             cacheKey->tzID = key.tzID;
853             cacheKey->mzID = key.mzID;
854             cacheKey->isLong = key.isLong;
855             uhash_put(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status);
856             if (U_FAILURE(status)) {
857                 uprv_free(cacheKey);
858             } else {
859                 // put the name to the local trie as well
860                 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
861                 if (nameinfo != NULL) {
862                     nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT;
863                     nameinfo->tzID = key.tzID;
864                     fGNamesTrie.put(uplname, nameinfo, status);
865                 }
866             }
867         }
868     }
869     return uplname;
870 }
871 
872 /*
873  * This method updates the cache and must be called with a lock,
874  * except initializer.
875  */
876 void
loadStrings(const UnicodeString & tzCanonicalID)877 TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) {
878     // load the generic location name
879     getGenericLocationName(tzCanonicalID);
880 
881     // partial location names
882     UErrorCode status = U_ZERO_ERROR;
883 
884     const UnicodeString *mzID;
885     UnicodeString goldenID;
886     UnicodeString mzGenName;
887     UTimeZoneNameType genNonLocTypes[] = {
888         UTZNM_LONG_GENERIC, UTZNM_SHORT_GENERIC,
889         UTZNM_UNKNOWN /*terminator*/
890     };
891 
892     StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status);
893     while ((mzID = mzIDs->snext(status))) {
894         if (U_FAILURE(status)) {
895             break;
896         }
897         // if this time zone is not the golden zone of the meta zone,
898         // partial location name (such as "PT (Los Angeles)") might be
899         // available.
900         fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion, goldenID);
901         if (tzCanonicalID != goldenID) {
902             for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) {
903                 fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName);
904                 if (!mzGenName.isEmpty()) {
905                     // getPartialLocationName formats a name and put it into the trie
906                     getPartialLocationName(tzCanonicalID, *mzID,
907                         (genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName);
908                 }
909             }
910         }
911     }
912     if (mzIDs != NULL) {
913         delete mzIDs;
914     }
915 }
916 
917 int32_t
findBestMatch(const UnicodeString & text,int32_t start,uint32_t types,UnicodeString & tzID,UTimeZoneFormatTimeType & timeType,UErrorCode & status) const918 TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
919         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
920     timeType = UTZFMT_TIME_TYPE_UNKNOWN;
921     tzID.setToBogus();
922 
923     if (U_FAILURE(status)) {
924         return 0;
925     }
926 
927     // Find matches in the TimeZoneNames first
928     TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status);
929     if (U_FAILURE(status)) {
930         return 0;
931     }
932 
933     int32_t bestMatchLen = 0;
934     UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
935     UnicodeString bestMatchTzID;
936     // UBool isLongStandard = FALSE;   // workaround - see the comments below
937     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
938 
939     if (tznamesMatches != NULL) {
940         UnicodeString mzID;
941         for (int32_t i = 0; i < tznamesMatches->size(); i++) {
942             int32_t len = tznamesMatches->getMatchLengthAt(i);
943             if (len > bestMatchLen) {
944                 bestMatchLen = len;
945                 if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) {
946                     // name for a meta zone
947                     if (tznamesMatches->getMetaZoneIDAt(i, mzID)) {
948                         fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, bestMatchTzID);
949                     }
950                 }
951                 UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i);
952                 if (U_FAILURE(status)) {
953                     break;
954                 }
955                 switch (nameType) {
956                 case UTZNM_LONG_STANDARD:
957                     // isLongStandard = TRUE;
958                 case UTZNM_SHORT_STANDARD:  // this one is never used for generic, but just in case
959                     isStandard = TRUE;      // TODO: Remove this later, see the comments above.
960                     bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD;
961                     break;
962                 case UTZNM_LONG_DAYLIGHT:
963                 case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case
964                     bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT;
965                     break;
966                 default:
967                     bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
968                 }
969             }
970         }
971         delete tznamesMatches;
972         if (U_FAILURE(status)) {
973             return 0;
974         }
975 
976         if (bestMatchLen == (text.length() - start)) {
977             // Full match
978 
979             //tzID.setTo(bestMatchTzID);
980             //timeType = bestMatchTimeType;
981             //return bestMatchLen;
982 
983             // TODO Some time zone uses a same name for the long standard name
984             // and the location name. When the match is a long standard name,
985             // then we need to check if the name is same with the location name.
986             // This is probably a data error or a design bug.
987 /*
988             if (!isLongStandard) {
989                 tzID.setTo(bestMatchTzID);
990                 timeType = bestMatchTimeType;
991                 return bestMatchLen;
992             }
993 */
994             // TODO The deprecation of commonlyUsed flag introduced the name
995             // conflict not only for long standard names, but short standard names too.
996             // These short names (found in zh_Hant) should be gone once we clean
997             // up CLDR time zone display name data. Once the short name conflict
998             // problem (with location name) is resolved, we should change the condition
999             // below back to the original one above. -Yoshito (2011-09-14)
1000             if (!isStandard) {
1001                 tzID.setTo(bestMatchTzID);
1002                 timeType = bestMatchTimeType;
1003                 return bestMatchLen;
1004             }
1005         }
1006     }
1007 
1008     // Find matches in the local trie
1009     TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status);
1010     if (U_FAILURE(status)) {
1011         return 0;
1012     }
1013     if (localMatches != NULL) {
1014         for (int32_t i = 0; i < localMatches->size(); i++) {
1015             int32_t len = localMatches->getMatchLength(i);
1016 
1017             // TODO See the above TODO. We use len >= bestMatchLen
1018             // because of the long standard/location name collision
1019             // problem. If it is also a location name, carrying
1020             // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a
1021             // problem in SimpleDateFormat
1022             if (len >= bestMatchLen) {
1023                 bestMatchLen = localMatches->getMatchLength(i);
1024                 bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;   // because generic
1025                 localMatches->getTimeZoneID(i, bestMatchTzID);
1026             }
1027         }
1028         delete localMatches;
1029     }
1030 
1031     if (bestMatchLen > 0) {
1032         timeType = bestMatchTimeType;
1033         tzID.setTo(bestMatchTzID);
1034     }
1035     return bestMatchLen;
1036 }
1037 
1038 TimeZoneGenericNameMatchInfo*
findLocal(const UnicodeString & text,int32_t start,uint32_t types,UErrorCode & status) const1039 TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
1040     GNameSearchHandler handler(types);
1041 
1042     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
1043 
1044     umtx_lock(&gLock);
1045     {
1046         fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
1047     }
1048     umtx_unlock(&gLock);
1049 
1050     if (U_FAILURE(status)) {
1051         return NULL;
1052     }
1053 
1054     TimeZoneGenericNameMatchInfo *gmatchInfo = NULL;
1055 
1056     int32_t maxLen = 0;
1057     UVector *results = handler.getMatches(maxLen);
1058     if (results != NULL && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) {
1059         // perfect match
1060         gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
1061         if (gmatchInfo == NULL) {
1062             status = U_MEMORY_ALLOCATION_ERROR;
1063             delete results;
1064             return NULL;
1065         }
1066         return gmatchInfo;
1067     }
1068 
1069     if (results != NULL) {
1070         delete results;
1071     }
1072 
1073     // All names are not yet loaded into the local trie.
1074     // Load all available names into the trie. This could be very heavy.
1075     umtx_lock(&gLock);
1076     {
1077         if (!fGNamesTrieFullyLoaded) {
1078             StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
1079             if (U_SUCCESS(status)) {
1080                 const UnicodeString *tzID;
1081                 while ((tzID = tzIDs->snext(status))) {
1082                     if (U_FAILURE(status)) {
1083                         break;
1084                     }
1085                     nonConstThis->loadStrings(*tzID);
1086                 }
1087             }
1088             if (tzIDs != NULL) {
1089                 delete tzIDs;
1090             }
1091 
1092             if (U_SUCCESS(status)) {
1093                 nonConstThis->fGNamesTrieFullyLoaded = TRUE;
1094             }
1095         }
1096     }
1097     umtx_unlock(&gLock);
1098 
1099     if (U_FAILURE(status)) {
1100         return NULL;
1101     }
1102 
1103     umtx_lock(&gLock);
1104     {
1105         // now try it again
1106         fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
1107     }
1108     umtx_unlock(&gLock);
1109 
1110     results = handler.getMatches(maxLen);
1111     if (results != NULL && maxLen > 0) {
1112         gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
1113         if (gmatchInfo == NULL) {
1114             status = U_MEMORY_ALLOCATION_ERROR;
1115             delete results;
1116             return NULL;
1117         }
1118     }
1119 
1120     return gmatchInfo;
1121 }
1122 
1123 TimeZoneNames::MatchInfoCollection*
findTimeZoneNames(const UnicodeString & text,int32_t start,uint32_t types,UErrorCode & status) const1124 TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
1125     // Check if the target name typs is really in the TimeZoneNames
1126     uint32_t nameTypes = 0;
1127     if (types & UTZGNM_LONG) {
1128         nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD);
1129     }
1130     if (types & UTZGNM_SHORT) {
1131         nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD);
1132     }
1133 
1134     if (types) {
1135         // Find matches in the TimeZoneNames
1136         return fTimeZoneNames->find(text, start, nameTypes, status);
1137     }
1138 
1139     return NULL;
1140 }
1141 
1142 typedef struct TZGNCoreRef {
1143     TZGNCore*       obj;
1144     int32_t         refCount;
1145     double          lastAccess;
1146 } TZGNCoreRef;
1147 
1148 // TZGNCore object cache handling
1149 static UMutex gTZGNLock = U_MUTEX_INITIALIZER;
1150 static UHashtable *gTZGNCoreCache = NULL;
1151 static UBool gTZGNCoreCacheInitialized = FALSE;
1152 
1153 // Access count - incremented every time up to SWEEP_INTERVAL,
1154 // then reset to 0
1155 static int32_t gAccessCount = 0;
1156 
1157 // Interval for calling the cache sweep function - every 100 times
1158 #define SWEEP_INTERVAL 100
1159 
1160 // Cache expiration in millisecond. When a cached entry is no
1161 // longer referenced and exceeding this threshold since last
1162 // access time, then the cache entry will be deleted by the sweep
1163 // function. For now, 3 minutes.
1164 #define CACHE_EXPIRATION 180000.0
1165 
1166 U_CDECL_BEGIN
1167 /**
1168  * Cleanup callback func
1169  */
tzgnCore_cleanup(void)1170 static UBool U_CALLCONV tzgnCore_cleanup(void)
1171 {
1172     if (gTZGNCoreCache != NULL) {
1173         uhash_close(gTZGNCoreCache);
1174         gTZGNCoreCache = NULL;
1175     }
1176     gTZGNCoreCacheInitialized = FALSE;
1177     return TRUE;
1178 }
1179 
1180 /**
1181  * Deleter for TZGNCoreRef
1182  */
1183 static void U_CALLCONV
deleteTZGNCoreRef(void * obj)1184 deleteTZGNCoreRef(void *obj) {
1185     icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj;
1186     delete (icu::TZGNCore*) entry->obj;
1187     uprv_free(entry);
1188 }
1189 U_CDECL_END
1190 
1191 /**
1192  * Function used for removing unreferrenced cache entries exceeding
1193  * the expiration time. This function must be called with in the mutex
1194  * block.
1195  */
sweepCache()1196 static void sweepCache() {
1197     int32_t pos = -1;
1198     const UHashElement* elem;
1199     double now = (double)uprv_getUTCtime();
1200 
1201     while ((elem = uhash_nextElement(gTZGNCoreCache, &pos))) {
1202         TZGNCoreRef *entry = (TZGNCoreRef *)elem->value.pointer;
1203         if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
1204             // delete this entry
1205             uhash_removeElement(gTZGNCoreCache, elem);
1206         }
1207     }
1208 }
1209 
TimeZoneGenericNames()1210 TimeZoneGenericNames::TimeZoneGenericNames()
1211 : fRef(0) {
1212 }
1213 
~TimeZoneGenericNames()1214 TimeZoneGenericNames::~TimeZoneGenericNames() {
1215     umtx_lock(&gTZGNLock);
1216     {
1217         U_ASSERT(fRef->refCount > 0);
1218         // Just decrement the reference count
1219         fRef->refCount--;
1220     }
1221     umtx_unlock(&gTZGNLock);
1222 }
1223 
1224 TimeZoneGenericNames*
createInstance(const Locale & locale,UErrorCode & status)1225 TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) {
1226     if (U_FAILURE(status)) {
1227         return NULL;
1228     }
1229     TimeZoneGenericNames* instance = new TimeZoneGenericNames();
1230     if (instance == NULL) {
1231         status = U_MEMORY_ALLOCATION_ERROR;
1232         return NULL;
1233     }
1234 
1235     UBool initialized;
1236     UMTX_CHECK(&gTZGNLock, gTZGNCoreCacheInitialized, initialized);
1237     if (!initialized) {
1238         // Create empty hashtable
1239         umtx_lock(&gTZGNLock);
1240         {
1241             if (!gTZGNCoreCacheInitialized) {
1242                 gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
1243                 if (U_SUCCESS(status)) {
1244                     uhash_setKeyDeleter(gTZGNCoreCache, uprv_free);
1245                     uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef);
1246                     gTZGNCoreCacheInitialized = TRUE;
1247                     ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup);
1248                 }
1249             }
1250         }
1251         umtx_unlock(&gTZGNLock);
1252 
1253         if (U_FAILURE(status)) {
1254             return NULL;
1255         }
1256     }
1257 
1258     // Check the cache, if not available, create new one and cache
1259     TZGNCoreRef *cacheEntry = NULL;
1260     umtx_lock(&gTZGNLock);
1261     {
1262         const char *key = locale.getName();
1263         cacheEntry = (TZGNCoreRef *)uhash_get(gTZGNCoreCache, key);
1264         if (cacheEntry == NULL) {
1265             TZGNCore *tzgnCore = NULL;
1266             char *newKey = NULL;
1267 
1268             tzgnCore = new TZGNCore(locale, status);
1269             if (tzgnCore == NULL) {
1270                 status = U_MEMORY_ALLOCATION_ERROR;
1271             }
1272             if (U_SUCCESS(status)) {
1273                 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1);
1274                 if (newKey == NULL) {
1275                     status = U_MEMORY_ALLOCATION_ERROR;
1276                 } else {
1277                     uprv_strcpy(newKey, key);
1278                 }
1279             }
1280             if (U_SUCCESS(status)) {
1281                 cacheEntry = (TZGNCoreRef *)uprv_malloc(sizeof(TZGNCoreRef));
1282                 if (cacheEntry == NULL) {
1283                     status = U_MEMORY_ALLOCATION_ERROR;
1284                 } else {
1285                     cacheEntry->obj = tzgnCore;
1286                     cacheEntry->refCount = 1;
1287                     cacheEntry->lastAccess = (double)uprv_getUTCtime();
1288 
1289                     uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status);
1290                 }
1291             }
1292             if (U_FAILURE(status)) {
1293                 if (tzgnCore != NULL) {
1294                     delete tzgnCore;
1295                 }
1296                 if (newKey != NULL) {
1297                     uprv_free(newKey);
1298                 }
1299                 if (cacheEntry != NULL) {
1300                     uprv_free(cacheEntry);
1301                 }
1302                 cacheEntry = NULL;
1303             }
1304         } else {
1305             // Update the reference count
1306             cacheEntry->refCount++;
1307             cacheEntry->lastAccess = (double)uprv_getUTCtime();
1308         }
1309         gAccessCount++;
1310         if (gAccessCount >= SWEEP_INTERVAL) {
1311             // sweep
1312             sweepCache();
1313             gAccessCount = 0;
1314         }
1315     }
1316     umtx_unlock(&gTZGNLock);
1317 
1318     if (cacheEntry == NULL) {
1319         delete instance;
1320         return NULL;
1321     }
1322 
1323     instance->fRef = cacheEntry;
1324     return instance;
1325 }
1326 
1327 UBool
operator ==(const TimeZoneGenericNames & other) const1328 TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const {
1329     // Just compare if the other object also use the same
1330     // ref entry
1331     return fRef == other.fRef;
1332 }
1333 
1334 TimeZoneGenericNames*
clone() const1335 TimeZoneGenericNames::clone() const {
1336     TimeZoneGenericNames* other = new TimeZoneGenericNames();
1337     if (other) {
1338         umtx_lock(&gTZGNLock);
1339         {
1340             // Just increments the reference count
1341             fRef->refCount++;
1342             other->fRef = fRef;
1343         }
1344         umtx_unlock(&gTZGNLock);
1345     }
1346     return other;
1347 }
1348 
1349 UnicodeString&
getDisplayName(const TimeZone & tz,UTimeZoneGenericNameType type,UDate date,UnicodeString & name) const1350 TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
1351                         UDate date, UnicodeString& name) const {
1352     return fRef->obj->getDisplayName(tz, type, date, name);
1353 }
1354 
1355 UnicodeString&
getGenericLocationName(const UnicodeString & tzCanonicalID,UnicodeString & name) const1356 TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
1357     return fRef->obj->getGenericLocationName(tzCanonicalID, name);
1358 }
1359 
1360 int32_t
findBestMatch(const UnicodeString & text,int32_t start,uint32_t types,UnicodeString & tzID,UTimeZoneFormatTimeType & timeType,UErrorCode & status) const1361 TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
1362         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
1363     return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status);
1364 }
1365 
1366 U_NAMESPACE_END
1367 #endif
1368