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