]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/tznames.cpp
ICU-64243.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / tznames.cpp
CommitLineData
f3c0d7a5
A
1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
4388f060
A
3/*
4*******************************************************************************
b331163b 5* Copyright (C) 2011-2015, International Business Machines Corporation and *
4388f060
A
6* others. All Rights Reserved. *
7*******************************************************************************
8*/
9
10#include "unicode/utypes.h"
11
12#if !UCONFIG_NO_FORMATTING
13
14#include "unicode/locid.h"
15#include "unicode/tznames.h"
16#include "unicode/uenum.h"
17#include "cmemory.h"
18#include "cstring.h"
57a6839d 19#include "mutex.h"
4388f060
A
20#include "putilimp.h"
21#include "tznames_impl.h"
22#include "uassert.h"
23#include "ucln_in.h"
24#include "uhash.h"
25#include "umutex.h"
26#include "uvector.h"
27
28
29U_NAMESPACE_BEGIN
30
4388f060 31// TimeZoneNames object cache handling
3d1f044b
A
32static UMutex *gTimeZoneNamesLock() {
33 static UMutex *m = STATIC_NEW(UMutex);
34 return m;
35}
4388f060
A
36static UHashtable *gTimeZoneNamesCache = NULL;
37static UBool gTimeZoneNamesCacheInitialized = FALSE;
38
39// Access count - incremented every time up to SWEEP_INTERVAL,
40// then reset to 0
41static int32_t gAccessCount = 0;
42
43// Interval for calling the cache sweep function - every 100 times
44#define SWEEP_INTERVAL 100
45
46// Cache expiration in millisecond. When a cached entry is no
47// longer referenced and exceeding this threshold since last
48// access time, then the cache entry will be deleted by the sweep
49// function. For now, 3 minutes.
50#define CACHE_EXPIRATION 180000.0
51
52typedef struct TimeZoneNamesCacheEntry {
53 TimeZoneNames* names;
54 int32_t refCount;
55 double lastAccess;
56} TimeZoneNamesCacheEntry;
57
58U_CDECL_BEGIN
59/**
60 * Cleanup callback func
61 */
62static UBool U_CALLCONV timeZoneNames_cleanup(void)
63{
4388f060
A
64 if (gTimeZoneNamesCache != NULL) {
65 uhash_close(gTimeZoneNamesCache);
66 gTimeZoneNamesCache = NULL;
67 }
68 gTimeZoneNamesCacheInitialized = FALSE;
69 return TRUE;
70}
71
72/**
73 * Deleter for TimeZoneNamesCacheEntry
74 */
75static void U_CALLCONV
76deleteTimeZoneNamesCacheEntry(void *obj) {
77 icu::TimeZoneNamesCacheEntry *entry = (icu::TimeZoneNamesCacheEntry*)obj;
78 delete (icu::TimeZoneNamesImpl*) entry->names;
79 uprv_free(entry);
80}
81U_CDECL_END
82
83/**
84 * Function used for removing unreferrenced cache entries exceeding
85 * the expiration time. This function must be called with in the mutex
86 * block.
87 */
88static void sweepCache() {
b331163b 89 int32_t pos = UHASH_FIRST;
4388f060
A
90 const UHashElement* elem;
91 double now = (double)uprv_getUTCtime();
92
3d1f044b 93 while ((elem = uhash_nextElement(gTimeZoneNamesCache, &pos)) != 0) {
4388f060
A
94 TimeZoneNamesCacheEntry *entry = (TimeZoneNamesCacheEntry *)elem->value.pointer;
95 if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
96 // delete this entry
97 uhash_removeElement(gTimeZoneNamesCache, elem);
98 }
99 }
100}
101
102// ---------------------------------------------------
103// TimeZoneNamesDelegate
104// ---------------------------------------------------
105class TimeZoneNamesDelegate : public TimeZoneNames {
106public:
107 TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status);
108 virtual ~TimeZoneNamesDelegate();
109
110 virtual UBool operator==(const TimeZoneNames& other) const;
3d1f044b 111 virtual UBool operator!=(const TimeZoneNames& other) const {return !operator==(other);}
4388f060
A
112 virtual TimeZoneNames* clone() const;
113
114 StringEnumeration* getAvailableMetaZoneIDs(UErrorCode& status) const;
115 StringEnumeration* getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const;
116 UnicodeString& getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const;
117 UnicodeString& getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const;
118
119 UnicodeString& getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const;
120 UnicodeString& getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const;
121
122 UnicodeString& getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const;
123
f3c0d7a5
A
124 void loadAllDisplayNames(UErrorCode& status);
125 void getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[], UErrorCode& status) const;
126
4388f060
A
127 MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
128private:
129 TimeZoneNamesDelegate();
130 TimeZoneNamesCacheEntry* fTZnamesCacheEntry;
131};
132
133TimeZoneNamesDelegate::TimeZoneNamesDelegate()
134: fTZnamesCacheEntry(0) {
135}
136
137TimeZoneNamesDelegate::TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status) {
3d1f044b 138 Mutex lock(gTimeZoneNamesLock());
57a6839d
A
139 if (!gTimeZoneNamesCacheInitialized) {
140 // Create empty hashtable if it is not already initialized.
141 gTimeZoneNamesCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
142 if (U_SUCCESS(status)) {
143 uhash_setKeyDeleter(gTimeZoneNamesCache, uprv_free);
144 uhash_setValueDeleter(gTimeZoneNamesCache, deleteTimeZoneNamesCacheEntry);
145 gTimeZoneNamesCacheInitialized = TRUE;
146 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONENAMES, timeZoneNames_cleanup);
4388f060 147 }
57a6839d 148 }
4388f060 149
57a6839d
A
150 if (U_FAILURE(status)) {
151 return;
4388f060
A
152 }
153
154 // Check the cache, if not available, create new one and cache
155 TimeZoneNamesCacheEntry *cacheEntry = NULL;
57a6839d
A
156
157 const char *key = locale.getName();
158 cacheEntry = (TimeZoneNamesCacheEntry *)uhash_get(gTimeZoneNamesCache, key);
159 if (cacheEntry == NULL) {
160 TimeZoneNames *tznames = NULL;
161 char *newKey = NULL;
162
163 tznames = new TimeZoneNamesImpl(locale, status);
164 if (tznames == NULL) {
165 status = U_MEMORY_ALLOCATION_ERROR;
166 }
167 if (U_SUCCESS(status)) {
168 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1);
169 if (newKey == NULL) {
4388f060 170 status = U_MEMORY_ALLOCATION_ERROR;
57a6839d
A
171 } else {
172 uprv_strcpy(newKey, key);
4388f060 173 }
57a6839d
A
174 }
175 if (U_SUCCESS(status)) {
176 cacheEntry = (TimeZoneNamesCacheEntry *)uprv_malloc(sizeof(TimeZoneNamesCacheEntry));
177 if (cacheEntry == NULL) {
178 status = U_MEMORY_ALLOCATION_ERROR;
179 } else {
180 cacheEntry->names = tznames;
181 cacheEntry->refCount = 1;
182 cacheEntry->lastAccess = (double)uprv_getUTCtime();
183
184 uhash_put(gTimeZoneNamesCache, newKey, cacheEntry, &status);
4388f060 185 }
57a6839d
A
186 }
187 if (U_FAILURE(status)) {
188 if (tznames != NULL) {
189 delete tznames;
4388f060 190 }
57a6839d
A
191 if (newKey != NULL) {
192 uprv_free(newKey);
4388f060 193 }
57a6839d
A
194 if (cacheEntry != NULL) {
195 uprv_free(cacheEntry);
196 }
197 cacheEntry = NULL;
4388f060 198 }
57a6839d
A
199 } else {
200 // Update the reference count
201 cacheEntry->refCount++;
202 cacheEntry->lastAccess = (double)uprv_getUTCtime();
203 }
204 gAccessCount++;
205 if (gAccessCount >= SWEEP_INTERVAL) {
206 // sweep
207 sweepCache();
208 gAccessCount = 0;
4388f060 209 }
4388f060
A
210 fTZnamesCacheEntry = cacheEntry;
211}
212
213TimeZoneNamesDelegate::~TimeZoneNamesDelegate() {
3d1f044b 214 umtx_lock(gTimeZoneNamesLock());
4388f060
A
215 {
216 if (fTZnamesCacheEntry) {
217 U_ASSERT(fTZnamesCacheEntry->refCount > 0);
218 // Just decrement the reference count
219 fTZnamesCacheEntry->refCount--;
220 }
221 }
3d1f044b 222 umtx_unlock(gTimeZoneNamesLock());
4388f060
A
223}
224
225UBool
226TimeZoneNamesDelegate::operator==(const TimeZoneNames& other) const {
227 if (this == &other) {
228 return TRUE;
229 }
230 // Just compare if the other object also use the same
231 // cache entry
232 const TimeZoneNamesDelegate* rhs = dynamic_cast<const TimeZoneNamesDelegate*>(&other);
233 if (rhs) {
234 return fTZnamesCacheEntry == rhs->fTZnamesCacheEntry;
235 }
236 return FALSE;
237}
238
239TimeZoneNames*
240TimeZoneNamesDelegate::clone() const {
241 TimeZoneNamesDelegate* other = new TimeZoneNamesDelegate();
242 if (other != NULL) {
3d1f044b 243 umtx_lock(gTimeZoneNamesLock());
4388f060
A
244 {
245 // Just increment the reference count
246 fTZnamesCacheEntry->refCount++;
247 other->fTZnamesCacheEntry = fTZnamesCacheEntry;
248 }
3d1f044b 249 umtx_unlock(gTimeZoneNamesLock());
4388f060
A
250 }
251 return other;
252}
253
254StringEnumeration*
255TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const {
256 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status);
257}
258
259StringEnumeration*
260TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
261 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status);
262}
263
264UnicodeString&
265TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
266 return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID);
267}
268
269UnicodeString&
270TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
271 return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID);
272}
273
274UnicodeString&
275TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const {
276 return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name);
277}
278
279UnicodeString&
280TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
281 return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name);
282}
283
284UnicodeString&
285TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
286 return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name);
287}
288
f3c0d7a5
A
289void
290TimeZoneNamesDelegate::loadAllDisplayNames(UErrorCode& status) {
291 fTZnamesCacheEntry->names->loadAllDisplayNames(status);
292}
293
294void
295TimeZoneNamesDelegate::getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[], UErrorCode& status) const {
296 fTZnamesCacheEntry->names->getDisplayNames(tzID, types, numTypes, date, dest, status);
297}
298
4388f060
A
299TimeZoneNames::MatchInfoCollection*
300TimeZoneNamesDelegate::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
301 return fTZnamesCacheEntry->names->find(text, start, types, status);
302}
303
304// ---------------------------------------------------
305// TimeZoneNames base class
306// ---------------------------------------------------
4388f060
A
307TimeZoneNames::~TimeZoneNames() {
308}
309
310TimeZoneNames*
311TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) {
b331163b
A
312 TimeZoneNames *instance = NULL;
313 if (U_SUCCESS(status)) {
314 instance = new TimeZoneNamesDelegate(locale, status);
315 if (instance == NULL && U_SUCCESS(status)) {
316 status = U_MEMORY_ALLOCATION_ERROR;
317 }
318 }
319 return instance;
320}
321
322TimeZoneNames*
323TimeZoneNames::createTZDBInstance(const Locale& locale, UErrorCode& status) {
324 TimeZoneNames *instance = NULL;
325 if (U_SUCCESS(status)) {
326 instance = new TZDBTimeZoneNames(locale);
327 if (instance == NULL && U_SUCCESS(status)) {
328 status = U_MEMORY_ALLOCATION_ERROR;
329 }
330 }
331 return instance;
4388f060
A
332}
333
334UnicodeString&
335TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
51004dcb 336 return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name);
4388f060
A
337}
338
339UnicodeString&
340TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const {
341 getTimeZoneDisplayName(tzID, type, name);
342 if (name.isEmpty()) {
b331163b
A
343 UChar mzIDBuf[32];
344 UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
4388f060
A
345 getMetaZoneID(tzID, date, mzID);
346 getMetaZoneDisplayName(mzID, type, name);
347 }
348 return name;
349}
350
f3c0d7a5
A
351// Empty default implementation, to be overriden in tznames_impl.cpp.
352void
353TimeZoneNames::loadAllDisplayNames(UErrorCode& /*status*/) {
354}
355
356// A default, lightweight implementation of getDisplayNames.
357// Overridden in tznames_impl.cpp.
358void
359TimeZoneNames::getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[], UErrorCode& status) const {
360 if (U_FAILURE(status)) { return; }
361 if (tzID.isEmpty()) { return; }
362 UnicodeString mzID;
363 for (int i = 0; i < numTypes; i++) {
364 getTimeZoneDisplayName(tzID, types[i], dest[i]);
365 if (dest[i].isEmpty()) {
366 if (mzID.isEmpty()) {
367 getMetaZoneID(tzID, date, mzID);
368 }
369 getMetaZoneDisplayName(mzID, types[i], dest[i]);
370 }
371 }
372}
373
4388f060
A
374
375struct MatchInfo : UMemory {
376 UTimeZoneNameType nameType;
377 UnicodeString id;
378 int32_t matchLength;
379 UBool isTZID;
380
381 MatchInfo(UTimeZoneNameType nameType, int32_t matchLength, const UnicodeString* tzID, const UnicodeString* mzID) {
382 this->nameType = nameType;
383 this->matchLength = matchLength;
384 if (tzID != NULL) {
385 this->id.setTo(*tzID);
386 this->isTZID = TRUE;
387 } else {
388 this->id.setTo(*mzID);
389 this->isTZID = FALSE;
390 }
391 }
392};
393
394U_CDECL_BEGIN
395static void U_CALLCONV
396deleteMatchInfo(void *obj) {
397 delete static_cast<MatchInfo *>(obj);
398}
399U_CDECL_END
400
401// ---------------------------------------------------
402// MatchInfoCollection class
403// ---------------------------------------------------
404TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
405: fMatches(NULL) {
406}
407
408TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
409 if (fMatches != NULL) {
410 delete fMatches;
411 }
412}
413
414void
415TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType, int32_t matchLength,
416 const UnicodeString& tzID, UErrorCode& status) {
417 if (U_FAILURE(status)) {
418 return;
419 }
420 MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, &tzID, NULL);
421 if (matchInfo == NULL) {
422 status = U_MEMORY_ALLOCATION_ERROR;
423 return;
424 }
425 matches(status)->addElement(matchInfo, status);
426 if (U_FAILURE(status)) {
427 delete matchInfo;
428 }
429}
430
431void
432TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType, int32_t matchLength,
433 const UnicodeString& mzID, UErrorCode& status) {
434 if (U_FAILURE(status)) {
435 return;
436 }
437 MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, NULL, &mzID);
438 if (matchInfo == NULL) {
439 status = U_MEMORY_ALLOCATION_ERROR;
440 return;
441 }
442 matches(status)->addElement(matchInfo, status);
443 if (U_FAILURE(status)) {
444 delete matchInfo;
445 }
446}
447
448int32_t
449TimeZoneNames::MatchInfoCollection::size() const {
450 if (fMatches == NULL) {
451 return 0;
452 }
453 return fMatches->size();
454}
455
456UTimeZoneNameType
457TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx) const {
458 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
459 if (match) {
460 return match->nameType;
461 }
462 return UTZNM_UNKNOWN;
463}
464
465int32_t
466TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx) const {
467 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
468 if (match) {
469 return match->matchLength;
470 }
471 return 0;
472}
473
474UBool
475TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx, UnicodeString& tzID) const {
476 tzID.remove();
477 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
478 if (match && match->isTZID) {
479 tzID.setTo(match->id);
480 return TRUE;
481 }
482 return FALSE;
483}
484
485UBool
486TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx, UnicodeString& mzID) const {
487 mzID.remove();
488 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
489 if (match && !match->isTZID) {
490 mzID.setTo(match->id);
491 return TRUE;
492 }
493 return FALSE;
494}
495
496UVector*
497TimeZoneNames::MatchInfoCollection::matches(UErrorCode& status) {
498 if (U_FAILURE(status)) {
499 return NULL;
500 }
501 if (fMatches != NULL) {
502 return fMatches;
503 }
504 fMatches = new UVector(deleteMatchInfo, NULL, status);
505 if (fMatches == NULL) {
506 status = U_MEMORY_ALLOCATION_ERROR;
507 } else if (U_FAILURE(status)) {
508 delete fMatches;
509 fMatches = NULL;
510 }
511 return fMatches;
512}
513
514
515U_NAMESPACE_END
516#endif