]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/tznames.cpp
ICU-64260.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / tznames.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-2015, 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 "unicode/locid.h"
15 #include "unicode/tznames.h"
16 #include "unicode/uenum.h"
17 #include "cmemory.h"
18 #include "cstring.h"
19 #include "mutex.h"
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
29 U_NAMESPACE_BEGIN
30
31 // TimeZoneNames object cache handling
32 static UMutex *gTimeZoneNamesLock() {
33 static UMutex *m = STATIC_NEW(UMutex);
34 return m;
35 }
36 static UHashtable *gTimeZoneNamesCache = NULL;
37 static UBool gTimeZoneNamesCacheInitialized = FALSE;
38
39 // Access count - incremented every time up to SWEEP_INTERVAL,
40 // then reset to 0
41 static 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
52 typedef struct TimeZoneNamesCacheEntry {
53 TimeZoneNames* names;
54 int32_t refCount;
55 double lastAccess;
56 } TimeZoneNamesCacheEntry;
57
58 U_CDECL_BEGIN
59 /**
60 * Cleanup callback func
61 */
62 static UBool U_CALLCONV timeZoneNames_cleanup(void)
63 {
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 */
75 static void U_CALLCONV
76 deleteTimeZoneNamesCacheEntry(void *obj) {
77 icu::TimeZoneNamesCacheEntry *entry = (icu::TimeZoneNamesCacheEntry*)obj;
78 delete (icu::TimeZoneNamesImpl*) entry->names;
79 uprv_free(entry);
80 }
81 U_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 */
88 static void sweepCache() {
89 int32_t pos = UHASH_FIRST;
90 const UHashElement* elem;
91 double now = (double)uprv_getUTCtime();
92
93 while ((elem = uhash_nextElement(gTimeZoneNamesCache, &pos)) != 0) {
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 // ---------------------------------------------------
105 class TimeZoneNamesDelegate : public TimeZoneNames {
106 public:
107 TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status);
108 virtual ~TimeZoneNamesDelegate();
109
110 virtual UBool operator==(const TimeZoneNames& other) const;
111 virtual UBool operator!=(const TimeZoneNames& other) const {return !operator==(other);}
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
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
127 MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
128 private:
129 TimeZoneNamesDelegate();
130 TimeZoneNamesCacheEntry* fTZnamesCacheEntry;
131 };
132
133 TimeZoneNamesDelegate::TimeZoneNamesDelegate()
134 : fTZnamesCacheEntry(0) {
135 }
136
137 TimeZoneNamesDelegate::TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status) {
138 Mutex lock(gTimeZoneNamesLock());
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);
147 }
148 }
149
150 if (U_FAILURE(status)) {
151 return;
152 }
153
154 // Check the cache, if not available, create new one and cache
155 TimeZoneNamesCacheEntry *cacheEntry = NULL;
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) {
170 status = U_MEMORY_ALLOCATION_ERROR;
171 } else {
172 uprv_strcpy(newKey, key);
173 }
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);
185 }
186 }
187 if (U_FAILURE(status)) {
188 if (tznames != NULL) {
189 delete tznames;
190 }
191 if (newKey != NULL) {
192 uprv_free(newKey);
193 }
194 if (cacheEntry != NULL) {
195 uprv_free(cacheEntry);
196 }
197 cacheEntry = NULL;
198 }
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;
209 }
210 fTZnamesCacheEntry = cacheEntry;
211 }
212
213 TimeZoneNamesDelegate::~TimeZoneNamesDelegate() {
214 umtx_lock(gTimeZoneNamesLock());
215 {
216 if (fTZnamesCacheEntry) {
217 U_ASSERT(fTZnamesCacheEntry->refCount > 0);
218 // Just decrement the reference count
219 fTZnamesCacheEntry->refCount--;
220 }
221 }
222 umtx_unlock(gTimeZoneNamesLock());
223 }
224
225 UBool
226 TimeZoneNamesDelegate::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
239 TimeZoneNames*
240 TimeZoneNamesDelegate::clone() const {
241 TimeZoneNamesDelegate* other = new TimeZoneNamesDelegate();
242 if (other != NULL) {
243 umtx_lock(gTimeZoneNamesLock());
244 {
245 // Just increment the reference count
246 fTZnamesCacheEntry->refCount++;
247 other->fTZnamesCacheEntry = fTZnamesCacheEntry;
248 }
249 umtx_unlock(gTimeZoneNamesLock());
250 }
251 return other;
252 }
253
254 StringEnumeration*
255 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const {
256 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status);
257 }
258
259 StringEnumeration*
260 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
261 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status);
262 }
263
264 UnicodeString&
265 TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
266 return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID);
267 }
268
269 UnicodeString&
270 TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
271 return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID);
272 }
273
274 UnicodeString&
275 TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const {
276 return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name);
277 }
278
279 UnicodeString&
280 TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
281 return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name);
282 }
283
284 UnicodeString&
285 TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
286 return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name);
287 }
288
289 void
290 TimeZoneNamesDelegate::loadAllDisplayNames(UErrorCode& status) {
291 fTZnamesCacheEntry->names->loadAllDisplayNames(status);
292 }
293
294 void
295 TimeZoneNamesDelegate::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
299 TimeZoneNames::MatchInfoCollection*
300 TimeZoneNamesDelegate::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 // ---------------------------------------------------
307 TimeZoneNames::~TimeZoneNames() {
308 }
309
310 TimeZoneNames*
311 TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) {
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
322 TimeZoneNames*
323 TimeZoneNames::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;
332 }
333
334 UnicodeString&
335 TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
336 return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name);
337 }
338
339 UnicodeString&
340 TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const {
341 getTimeZoneDisplayName(tzID, type, name);
342 if (name.isEmpty()) {
343 UChar mzIDBuf[32];
344 UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
345 getMetaZoneID(tzID, date, mzID);
346 getMetaZoneDisplayName(mzID, type, name);
347 }
348 return name;
349 }
350
351 // Empty default implementation, to be overriden in tznames_impl.cpp.
352 void
353 TimeZoneNames::loadAllDisplayNames(UErrorCode& /*status*/) {
354 }
355
356 // A default, lightweight implementation of getDisplayNames.
357 // Overridden in tznames_impl.cpp.
358 void
359 TimeZoneNames::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
374
375 struct 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
394 U_CDECL_BEGIN
395 static void U_CALLCONV
396 deleteMatchInfo(void *obj) {
397 delete static_cast<MatchInfo *>(obj);
398 }
399 U_CDECL_END
400
401 // ---------------------------------------------------
402 // MatchInfoCollection class
403 // ---------------------------------------------------
404 TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
405 : fMatches(NULL) {
406 }
407
408 TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
409 if (fMatches != NULL) {
410 delete fMatches;
411 }
412 }
413
414 void
415 TimeZoneNames::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
431 void
432 TimeZoneNames::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
448 int32_t
449 TimeZoneNames::MatchInfoCollection::size() const {
450 if (fMatches == NULL) {
451 return 0;
452 }
453 return fMatches->size();
454 }
455
456 UTimeZoneNameType
457 TimeZoneNames::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
465 int32_t
466 TimeZoneNames::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
474 UBool
475 TimeZoneNames::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
485 UBool
486 TimeZoneNames::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
496 UVector*
497 TimeZoneNames::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
515 U_NAMESPACE_END
516 #endif