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