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