2 *******************************************************************************
3 * Copyright (C) 2011-2013, 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"
18 #include "tznames_impl.h"
28 // TimeZoneNames object cache handling
29 static UMutex gTimeZoneNamesLock
= U_MUTEX_INITIALIZER
;
30 static UHashtable
*gTimeZoneNamesCache
= NULL
;
31 static UBool gTimeZoneNamesCacheInitialized
= FALSE
;
33 // Access count - incremented every time up to SWEEP_INTERVAL,
35 static int32_t gAccessCount
= 0;
37 // Interval for calling the cache sweep function - every 100 times
38 #define SWEEP_INTERVAL 100
40 // Cache expiration in millisecond. When a cached entry is no
41 // longer referenced and exceeding this threshold since last
42 // access time, then the cache entry will be deleted by the sweep
43 // function. For now, 3 minutes.
44 #define CACHE_EXPIRATION 180000.0
46 typedef struct TimeZoneNamesCacheEntry
{
50 } TimeZoneNamesCacheEntry
;
54 * Cleanup callback func
56 static UBool U_CALLCONV
timeZoneNames_cleanup(void)
58 if (gTimeZoneNamesCache
!= NULL
) {
59 uhash_close(gTimeZoneNamesCache
);
60 gTimeZoneNamesCache
= NULL
;
62 gTimeZoneNamesCacheInitialized
= FALSE
;
67 * Deleter for TimeZoneNamesCacheEntry
69 static void U_CALLCONV
70 deleteTimeZoneNamesCacheEntry(void *obj
) {
71 icu::TimeZoneNamesCacheEntry
*entry
= (icu::TimeZoneNamesCacheEntry
*)obj
;
72 delete (icu::TimeZoneNamesImpl
*) entry
->names
;
78 * Function used for removing unreferrenced cache entries exceeding
79 * the expiration time. This function must be called with in the mutex
82 static void sweepCache() {
84 const UHashElement
* elem
;
85 double now
= (double)uprv_getUTCtime();
87 while ((elem
= uhash_nextElement(gTimeZoneNamesCache
, &pos
))) {
88 TimeZoneNamesCacheEntry
*entry
= (TimeZoneNamesCacheEntry
*)elem
->value
.pointer
;
89 if (entry
->refCount
<= 0 && (now
- entry
->lastAccess
) > CACHE_EXPIRATION
) {
91 uhash_removeElement(gTimeZoneNamesCache
, elem
);
96 // ---------------------------------------------------
97 // TimeZoneNamesDelegate
98 // ---------------------------------------------------
99 class TimeZoneNamesDelegate
: public TimeZoneNames
{
101 TimeZoneNamesDelegate(const Locale
& locale
, UErrorCode
& status
);
102 virtual ~TimeZoneNamesDelegate();
104 virtual UBool
operator==(const TimeZoneNames
& other
) const;
105 virtual UBool
operator!=(const TimeZoneNames
& other
) const {return !operator==(other
);};
106 virtual TimeZoneNames
* clone() const;
108 StringEnumeration
* getAvailableMetaZoneIDs(UErrorCode
& status
) const;
109 StringEnumeration
* getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const;
110 UnicodeString
& getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const;
111 UnicodeString
& getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const;
113 UnicodeString
& getMetaZoneDisplayName(const UnicodeString
& mzID
, UTimeZoneNameType type
, UnicodeString
& name
) const;
114 UnicodeString
& getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const;
116 UnicodeString
& getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const;
118 MatchInfoCollection
* find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const;
120 TimeZoneNamesDelegate();
121 TimeZoneNamesCacheEntry
* fTZnamesCacheEntry
;
124 TimeZoneNamesDelegate::TimeZoneNamesDelegate()
125 : fTZnamesCacheEntry(0) {
128 TimeZoneNamesDelegate::TimeZoneNamesDelegate(const Locale
& locale
, UErrorCode
& status
) {
130 UMTX_CHECK(&gTimeZoneNamesLock
, gTimeZoneNamesCacheInitialized
, initialized
);
132 // Create empty hashtable
133 umtx_lock(&gTimeZoneNamesLock
);
135 if (!gTimeZoneNamesCacheInitialized
) {
136 gTimeZoneNamesCache
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
137 if (U_SUCCESS(status
)) {
138 uhash_setKeyDeleter(gTimeZoneNamesCache
, uprv_free
);
139 uhash_setValueDeleter(gTimeZoneNamesCache
, deleteTimeZoneNamesCacheEntry
);
140 gTimeZoneNamesCacheInitialized
= TRUE
;
141 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONENAMES
, timeZoneNames_cleanup
);
145 umtx_unlock(&gTimeZoneNamesLock
);
147 if (U_FAILURE(status
)) {
152 // Check the cache, if not available, create new one and cache
153 TimeZoneNamesCacheEntry
*cacheEntry
= NULL
;
154 umtx_lock(&gTimeZoneNamesLock
);
156 const char *key
= locale
.getName();
157 cacheEntry
= (TimeZoneNamesCacheEntry
*)uhash_get(gTimeZoneNamesCache
, key
);
158 if (cacheEntry
== NULL
) {
159 TimeZoneNames
*tznames
= NULL
;
162 tznames
= new TimeZoneNamesImpl(locale
, status
);
163 if (tznames
== NULL
) {
164 status
= U_MEMORY_ALLOCATION_ERROR
;
166 if (U_SUCCESS(status
)) {
167 newKey
= (char *)uprv_malloc(uprv_strlen(key
) + 1);
168 if (newKey
== NULL
) {
169 status
= U_MEMORY_ALLOCATION_ERROR
;
171 uprv_strcpy(newKey
, key
);
174 if (U_SUCCESS(status
)) {
175 cacheEntry
= (TimeZoneNamesCacheEntry
*)uprv_malloc(sizeof(TimeZoneNamesCacheEntry
));
176 if (cacheEntry
== NULL
) {
177 status
= U_MEMORY_ALLOCATION_ERROR
;
179 cacheEntry
->names
= tznames
;
180 cacheEntry
->refCount
= 1;
181 cacheEntry
->lastAccess
= (double)uprv_getUTCtime();
183 uhash_put(gTimeZoneNamesCache
, newKey
, cacheEntry
, &status
);
186 if (U_FAILURE(status
)) {
187 if (tznames
!= NULL
) {
190 if (newKey
!= NULL
) {
193 if (cacheEntry
!= NULL
) {
194 uprv_free(cacheEntry
);
199 // Update the reference count
200 cacheEntry
->refCount
++;
201 cacheEntry
->lastAccess
= (double)uprv_getUTCtime();
204 if (gAccessCount
>= SWEEP_INTERVAL
) {
210 umtx_unlock(&gTimeZoneNamesLock
);
212 fTZnamesCacheEntry
= cacheEntry
;
215 TimeZoneNamesDelegate::~TimeZoneNamesDelegate() {
216 umtx_lock(&gTimeZoneNamesLock
);
218 if (fTZnamesCacheEntry
) {
219 U_ASSERT(fTZnamesCacheEntry
->refCount
> 0);
220 // Just decrement the reference count
221 fTZnamesCacheEntry
->refCount
--;
224 umtx_unlock(&gTimeZoneNamesLock
);
228 TimeZoneNamesDelegate::operator==(const TimeZoneNames
& other
) const {
229 if (this == &other
) {
232 // Just compare if the other object also use the same
234 const TimeZoneNamesDelegate
* rhs
= dynamic_cast<const TimeZoneNamesDelegate
*>(&other
);
236 return fTZnamesCacheEntry
== rhs
->fTZnamesCacheEntry
;
242 TimeZoneNamesDelegate::clone() const {
243 TimeZoneNamesDelegate
* other
= new TimeZoneNamesDelegate();
245 umtx_lock(&gTimeZoneNamesLock
);
247 // Just increment the reference count
248 fTZnamesCacheEntry
->refCount
++;
249 other
->fTZnamesCacheEntry
= fTZnamesCacheEntry
;
251 umtx_unlock(&gTimeZoneNamesLock
);
257 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
258 return fTZnamesCacheEntry
->names
->getAvailableMetaZoneIDs(status
);
262 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
263 return fTZnamesCacheEntry
->names
->getAvailableMetaZoneIDs(tzID
, status
);
267 TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
268 return fTZnamesCacheEntry
->names
->getMetaZoneID(tzID
, date
, mzID
);
272 TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
273 return fTZnamesCacheEntry
->names
->getReferenceZoneID(mzID
, region
, tzID
);
277 TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString
& mzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
278 return fTZnamesCacheEntry
->names
->getMetaZoneDisplayName(mzID
, type
, name
);
282 TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
283 return fTZnamesCacheEntry
->names
->getTimeZoneDisplayName(tzID
, type
, name
);
287 TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
288 return fTZnamesCacheEntry
->names
->getExemplarLocationName(tzID
, name
);
291 TimeZoneNames::MatchInfoCollection
*
292 TimeZoneNamesDelegate::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
293 return fTZnamesCacheEntry
->names
->find(text
, start
, types
, status
);
296 // ---------------------------------------------------
297 // TimeZoneNames base class
298 // ---------------------------------------------------
299 TimeZoneNames::~TimeZoneNames() {
303 TimeZoneNames::createInstance(const Locale
& locale
, UErrorCode
& status
) {
304 return new TimeZoneNamesDelegate(locale
, status
);
308 TimeZoneNames::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
309 return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID
, name
);
313 TimeZoneNames::getDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UDate date
, UnicodeString
& name
) const {
314 getTimeZoneDisplayName(tzID
, type
, name
);
315 if (name
.isEmpty()) {
317 getMetaZoneID(tzID
, date
, mzID
);
318 getMetaZoneDisplayName(mzID
, type
, name
);
324 struct MatchInfo
: UMemory
{
325 UTimeZoneNameType nameType
;
330 MatchInfo(UTimeZoneNameType nameType
, int32_t matchLength
, const UnicodeString
* tzID
, const UnicodeString
* mzID
) {
331 this->nameType
= nameType
;
332 this->matchLength
= matchLength
;
334 this->id
.setTo(*tzID
);
337 this->id
.setTo(*mzID
);
338 this->isTZID
= FALSE
;
344 static void U_CALLCONV
345 deleteMatchInfo(void *obj
) {
346 delete static_cast<MatchInfo
*>(obj
);
350 // ---------------------------------------------------
351 // MatchInfoCollection class
352 // ---------------------------------------------------
353 TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
357 TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
358 if (fMatches
!= NULL
) {
364 TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType
, int32_t matchLength
,
365 const UnicodeString
& tzID
, UErrorCode
& status
) {
366 if (U_FAILURE(status
)) {
369 MatchInfo
* matchInfo
= new MatchInfo(nameType
, matchLength
, &tzID
, NULL
);
370 if (matchInfo
== NULL
) {
371 status
= U_MEMORY_ALLOCATION_ERROR
;
374 matches(status
)->addElement(matchInfo
, status
);
375 if (U_FAILURE(status
)) {
381 TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType
, int32_t matchLength
,
382 const UnicodeString
& mzID
, UErrorCode
& status
) {
383 if (U_FAILURE(status
)) {
386 MatchInfo
* matchInfo
= new MatchInfo(nameType
, matchLength
, NULL
, &mzID
);
387 if (matchInfo
== NULL
) {
388 status
= U_MEMORY_ALLOCATION_ERROR
;
391 matches(status
)->addElement(matchInfo
, status
);
392 if (U_FAILURE(status
)) {
398 TimeZoneNames::MatchInfoCollection::size() const {
399 if (fMatches
== NULL
) {
402 return fMatches
->size();
406 TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx
) const {
407 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
409 return match
->nameType
;
411 return UTZNM_UNKNOWN
;
415 TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx
) const {
416 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
418 return match
->matchLength
;
424 TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx
, UnicodeString
& tzID
) const {
426 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
427 if (match
&& match
->isTZID
) {
428 tzID
.setTo(match
->id
);
435 TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx
, UnicodeString
& mzID
) const {
437 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
438 if (match
&& !match
->isTZID
) {
439 mzID
.setTo(match
->id
);
446 TimeZoneNames::MatchInfoCollection::matches(UErrorCode
& status
) {
447 if (U_FAILURE(status
)) {
450 if (fMatches
!= NULL
) {
453 fMatches
= new UVector(deleteMatchInfo
, NULL
, status
);
454 if (fMatches
== NULL
) {
455 status
= U_MEMORY_ALLOCATION_ERROR
;
456 } else if (U_FAILURE(status
)) {