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