2 *******************************************************************************
3 * Copyright (C) 2011-2015, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
8 #include "unicode/utypes.h"
10 #if !UCONFIG_NO_FORMATTING
12 #include "unicode/locid.h"
13 #include "unicode/tznames.h"
14 #include "unicode/uenum.h"
19 #include "tznames_impl.h"
29 // TimeZoneNames object cache handling
30 static UMutex gTimeZoneNamesLock
= U_MUTEX_INITIALIZER
;
31 static UHashtable
*gTimeZoneNamesCache
= NULL
;
32 static UBool gTimeZoneNamesCacheInitialized
= FALSE
;
34 // Access count - incremented every time up to SWEEP_INTERVAL,
36 static int32_t gAccessCount
= 0;
38 // Interval for calling the cache sweep function - every 100 times
39 #define SWEEP_INTERVAL 100
41 // Cache expiration in millisecond. When a cached entry is no
42 // longer referenced and exceeding this threshold since last
43 // access time, then the cache entry will be deleted by the sweep
44 // function. For now, 3 minutes.
45 #define CACHE_EXPIRATION 180000.0
47 typedef struct TimeZoneNamesCacheEntry
{
51 } TimeZoneNamesCacheEntry
;
55 * Cleanup callback func
57 static UBool U_CALLCONV
timeZoneNames_cleanup(void)
59 if (gTimeZoneNamesCache
!= NULL
) {
60 uhash_close(gTimeZoneNamesCache
);
61 gTimeZoneNamesCache
= NULL
;
63 gTimeZoneNamesCacheInitialized
= FALSE
;
68 * Deleter for TimeZoneNamesCacheEntry
70 static void U_CALLCONV
71 deleteTimeZoneNamesCacheEntry(void *obj
) {
72 icu::TimeZoneNamesCacheEntry
*entry
= (icu::TimeZoneNamesCacheEntry
*)obj
;
73 delete (icu::TimeZoneNamesImpl
*) entry
->names
;
79 * Function used for removing unreferrenced cache entries exceeding
80 * the expiration time. This function must be called with in the mutex
83 static void sweepCache() {
84 int32_t pos
= UHASH_FIRST
;
85 const UHashElement
* elem
;
86 double now
= (double)uprv_getUTCtime();
88 while ((elem
= uhash_nextElement(gTimeZoneNamesCache
, &pos
))) {
89 TimeZoneNamesCacheEntry
*entry
= (TimeZoneNamesCacheEntry
*)elem
->value
.pointer
;
90 if (entry
->refCount
<= 0 && (now
- entry
->lastAccess
) > CACHE_EXPIRATION
) {
92 uhash_removeElement(gTimeZoneNamesCache
, elem
);
97 // ---------------------------------------------------
98 // TimeZoneNamesDelegate
99 // ---------------------------------------------------
100 class TimeZoneNamesDelegate
: public TimeZoneNames
{
102 TimeZoneNamesDelegate(const Locale
& locale
, UErrorCode
& status
);
103 virtual ~TimeZoneNamesDelegate();
105 virtual UBool
operator==(const TimeZoneNames
& other
) const;
106 virtual UBool
operator!=(const TimeZoneNames
& other
) const {return !operator==(other
);};
107 virtual TimeZoneNames
* clone() const;
109 StringEnumeration
* getAvailableMetaZoneIDs(UErrorCode
& status
) const;
110 StringEnumeration
* getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const;
111 UnicodeString
& getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const;
112 UnicodeString
& getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const;
114 UnicodeString
& getMetaZoneDisplayName(const UnicodeString
& mzID
, UTimeZoneNameType type
, UnicodeString
& name
) const;
115 UnicodeString
& getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const;
117 UnicodeString
& getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const;
119 MatchInfoCollection
* find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const;
121 TimeZoneNamesDelegate();
122 TimeZoneNamesCacheEntry
* fTZnamesCacheEntry
;
125 TimeZoneNamesDelegate::TimeZoneNamesDelegate()
126 : fTZnamesCacheEntry(0) {
129 TimeZoneNamesDelegate::TimeZoneNamesDelegate(const Locale
& locale
, UErrorCode
& status
) {
130 Mutex
lock(&gTimeZoneNamesLock
);
131 if (!gTimeZoneNamesCacheInitialized
) {
132 // Create empty hashtable if it is not already initialized.
133 gTimeZoneNamesCache
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
134 if (U_SUCCESS(status
)) {
135 uhash_setKeyDeleter(gTimeZoneNamesCache
, uprv_free
);
136 uhash_setValueDeleter(gTimeZoneNamesCache
, deleteTimeZoneNamesCacheEntry
);
137 gTimeZoneNamesCacheInitialized
= TRUE
;
138 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONENAMES
, timeZoneNames_cleanup
);
142 if (U_FAILURE(status
)) {
146 // Check the cache, if not available, create new one and cache
147 TimeZoneNamesCacheEntry
*cacheEntry
= NULL
;
149 const char *key
= locale
.getName();
150 cacheEntry
= (TimeZoneNamesCacheEntry
*)uhash_get(gTimeZoneNamesCache
, key
);
151 if (cacheEntry
== NULL
) {
152 TimeZoneNames
*tznames
= NULL
;
155 tznames
= new TimeZoneNamesImpl(locale
, status
);
156 if (tznames
== NULL
) {
157 status
= U_MEMORY_ALLOCATION_ERROR
;
159 if (U_SUCCESS(status
)) {
160 newKey
= (char *)uprv_malloc(uprv_strlen(key
) + 1);
161 if (newKey
== NULL
) {
162 status
= U_MEMORY_ALLOCATION_ERROR
;
164 uprv_strcpy(newKey
, key
);
167 if (U_SUCCESS(status
)) {
168 cacheEntry
= (TimeZoneNamesCacheEntry
*)uprv_malloc(sizeof(TimeZoneNamesCacheEntry
));
169 if (cacheEntry
== NULL
) {
170 status
= U_MEMORY_ALLOCATION_ERROR
;
172 cacheEntry
->names
= tznames
;
173 cacheEntry
->refCount
= 1;
174 cacheEntry
->lastAccess
= (double)uprv_getUTCtime();
176 uhash_put(gTimeZoneNamesCache
, newKey
, cacheEntry
, &status
);
179 if (U_FAILURE(status
)) {
180 if (tznames
!= NULL
) {
183 if (newKey
!= NULL
) {
186 if (cacheEntry
!= NULL
) {
187 uprv_free(cacheEntry
);
192 // Update the reference count
193 cacheEntry
->refCount
++;
194 cacheEntry
->lastAccess
= (double)uprv_getUTCtime();
197 if (gAccessCount
>= SWEEP_INTERVAL
) {
202 fTZnamesCacheEntry
= cacheEntry
;
205 TimeZoneNamesDelegate::~TimeZoneNamesDelegate() {
206 umtx_lock(&gTimeZoneNamesLock
);
208 if (fTZnamesCacheEntry
) {
209 U_ASSERT(fTZnamesCacheEntry
->refCount
> 0);
210 // Just decrement the reference count
211 fTZnamesCacheEntry
->refCount
--;
214 umtx_unlock(&gTimeZoneNamesLock
);
218 TimeZoneNamesDelegate::operator==(const TimeZoneNames
& other
) const {
219 if (this == &other
) {
222 // Just compare if the other object also use the same
224 const TimeZoneNamesDelegate
* rhs
= dynamic_cast<const TimeZoneNamesDelegate
*>(&other
);
226 return fTZnamesCacheEntry
== rhs
->fTZnamesCacheEntry
;
232 TimeZoneNamesDelegate::clone() const {
233 TimeZoneNamesDelegate
* other
= new TimeZoneNamesDelegate();
235 umtx_lock(&gTimeZoneNamesLock
);
237 // Just increment the reference count
238 fTZnamesCacheEntry
->refCount
++;
239 other
->fTZnamesCacheEntry
= fTZnamesCacheEntry
;
241 umtx_unlock(&gTimeZoneNamesLock
);
247 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
248 return fTZnamesCacheEntry
->names
->getAvailableMetaZoneIDs(status
);
252 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
253 return fTZnamesCacheEntry
->names
->getAvailableMetaZoneIDs(tzID
, status
);
257 TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
258 return fTZnamesCacheEntry
->names
->getMetaZoneID(tzID
, date
, mzID
);
262 TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
263 return fTZnamesCacheEntry
->names
->getReferenceZoneID(mzID
, region
, tzID
);
267 TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString
& mzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
268 return fTZnamesCacheEntry
->names
->getMetaZoneDisplayName(mzID
, type
, name
);
272 TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
273 return fTZnamesCacheEntry
->names
->getTimeZoneDisplayName(tzID
, type
, name
);
277 TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
278 return fTZnamesCacheEntry
->names
->getExemplarLocationName(tzID
, name
);
281 TimeZoneNames::MatchInfoCollection
*
282 TimeZoneNamesDelegate::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
283 return fTZnamesCacheEntry
->names
->find(text
, start
, types
, status
);
286 // ---------------------------------------------------
287 // TimeZoneNames base class
288 // ---------------------------------------------------
289 TimeZoneNames::~TimeZoneNames() {
293 TimeZoneNames::createInstance(const Locale
& locale
, UErrorCode
& status
) {
294 TimeZoneNames
*instance
= NULL
;
295 if (U_SUCCESS(status
)) {
296 instance
= new TimeZoneNamesDelegate(locale
, status
);
297 if (instance
== NULL
&& U_SUCCESS(status
)) {
298 status
= U_MEMORY_ALLOCATION_ERROR
;
305 TimeZoneNames::createTZDBInstance(const Locale
& locale
, UErrorCode
& status
) {
306 TimeZoneNames
*instance
= NULL
;
307 if (U_SUCCESS(status
)) {
308 instance
= new TZDBTimeZoneNames(locale
);
309 if (instance
== NULL
&& U_SUCCESS(status
)) {
310 status
= U_MEMORY_ALLOCATION_ERROR
;
317 TimeZoneNames::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
318 return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID
, name
);
322 TimeZoneNames::getDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UDate date
, UnicodeString
& name
) const {
323 getTimeZoneDisplayName(tzID
, type
, name
);
324 if (name
.isEmpty()) {
326 UnicodeString
mzID(mzIDBuf
, 0, UPRV_LENGTHOF(mzIDBuf
));
327 getMetaZoneID(tzID
, date
, mzID
);
328 getMetaZoneDisplayName(mzID
, type
, name
);
334 struct MatchInfo
: UMemory
{
335 UTimeZoneNameType nameType
;
340 MatchInfo(UTimeZoneNameType nameType
, int32_t matchLength
, const UnicodeString
* tzID
, const UnicodeString
* mzID
) {
341 this->nameType
= nameType
;
342 this->matchLength
= matchLength
;
344 this->id
.setTo(*tzID
);
347 this->id
.setTo(*mzID
);
348 this->isTZID
= FALSE
;
354 static void U_CALLCONV
355 deleteMatchInfo(void *obj
) {
356 delete static_cast<MatchInfo
*>(obj
);
360 // ---------------------------------------------------
361 // MatchInfoCollection class
362 // ---------------------------------------------------
363 TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
367 TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
368 if (fMatches
!= NULL
) {
374 TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType
, int32_t matchLength
,
375 const UnicodeString
& tzID
, UErrorCode
& status
) {
376 if (U_FAILURE(status
)) {
379 MatchInfo
* matchInfo
= new MatchInfo(nameType
, matchLength
, &tzID
, NULL
);
380 if (matchInfo
== NULL
) {
381 status
= U_MEMORY_ALLOCATION_ERROR
;
384 matches(status
)->addElement(matchInfo
, status
);
385 if (U_FAILURE(status
)) {
391 TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType
, int32_t matchLength
,
392 const UnicodeString
& mzID
, UErrorCode
& status
) {
393 if (U_FAILURE(status
)) {
396 MatchInfo
* matchInfo
= new MatchInfo(nameType
, matchLength
, NULL
, &mzID
);
397 if (matchInfo
== NULL
) {
398 status
= U_MEMORY_ALLOCATION_ERROR
;
401 matches(status
)->addElement(matchInfo
, status
);
402 if (U_FAILURE(status
)) {
408 TimeZoneNames::MatchInfoCollection::size() const {
409 if (fMatches
== NULL
) {
412 return fMatches
->size();
416 TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx
) const {
417 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
419 return match
->nameType
;
421 return UTZNM_UNKNOWN
;
425 TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx
) const {
426 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
428 return match
->matchLength
;
434 TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx
, UnicodeString
& tzID
) const {
436 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
437 if (match
&& match
->isTZID
) {
438 tzID
.setTo(match
->id
);
445 TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx
, UnicodeString
& mzID
) const {
447 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
448 if (match
&& !match
->isTZID
) {
449 mzID
.setTo(match
->id
);
456 TimeZoneNames::MatchInfoCollection::matches(UErrorCode
& status
) {
457 if (U_FAILURE(status
)) {
460 if (fMatches
!= NULL
) {
463 fMatches
= new UVector(deleteMatchInfo
, NULL
, status
);
464 if (fMatches
== NULL
) {
465 status
= U_MEMORY_ALLOCATION_ERROR
;
466 } else if (U_FAILURE(status
)) {