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