Bug Summary

File:out/../deps/icu-small/source/i18n/tzgnames.cpp
Warning:line 660, column 17
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

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