1 /*
2 *******************************************************************************
3 * Copyright (C) 2011-2013, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
6 */
7
8 #include "unicode/utypes.h"
9
10 #if !UCONFIG_NO_FORMATTING
11
12 #include "unicode/locid.h"
13 #include "unicode/tznames.h"
14 #include "unicode/uenum.h"
15 #include "cmemory.h"
16 #include "cstring.h"
17 #include "mutex.h"
18 #include "putilimp.h"
19 #include "tznames_impl.h"
20 #include "uassert.h"
21 #include "ucln_in.h"
22 #include "uhash.h"
23 #include "umutex.h"
24 #include "uvector.h"
25
26
27 U_NAMESPACE_BEGIN
28
29 // TimeZoneNames object cache handling
30 static UMutex gTimeZoneNamesLock = U_MUTEX_INITIALIZER;
31 static UHashtable *gTimeZoneNamesCache = NULL;
32 static UBool gTimeZoneNamesCacheInitialized = FALSE;
33
34 // Access count - incremented every time up to SWEEP_INTERVAL,
35 // then reset to 0
36 static int32_t gAccessCount = 0;
37
38 // Interval for calling the cache sweep function - every 100 times
39 #define SWEEP_INTERVAL 100
40
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
46
47 typedef struct TimeZoneNamesCacheEntry {
48 TimeZoneNames* names;
49 int32_t refCount;
50 double lastAccess;
51 } TimeZoneNamesCacheEntry;
52
53 U_CDECL_BEGIN
54 /**
55 * Cleanup callback func
56 */
timeZoneNames_cleanup(void)57 static UBool U_CALLCONV timeZoneNames_cleanup(void)
58 {
59 if (gTimeZoneNamesCache != NULL) {
60 uhash_close(gTimeZoneNamesCache);
61 gTimeZoneNamesCache = NULL;
62 }
63 gTimeZoneNamesCacheInitialized = FALSE;
64 return TRUE;
65 }
66
67 /**
68 * Deleter for TimeZoneNamesCacheEntry
69 */
70 static void U_CALLCONV
deleteTimeZoneNamesCacheEntry(void * obj)71 deleteTimeZoneNamesCacheEntry(void *obj) {
72 icu::TimeZoneNamesCacheEntry *entry = (icu::TimeZoneNamesCacheEntry*)obj;
73 delete (icu::TimeZoneNamesImpl*) entry->names;
74 uprv_free(entry);
75 }
76 U_CDECL_END
77
78 /**
79 * Function used for removing unreferrenced cache entries exceeding
80 * the expiration time. This function must be called with in the mutex
81 * block.
82 */
sweepCache()83 static void sweepCache() {
84 int32_t pos = -1;
85 const UHashElement* elem;
86 double now = (double)uprv_getUTCtime();
87
88 while ((elem = uhash_nextElement(gTimeZoneNamesCache, &pos))) {
89 TimeZoneNamesCacheEntry *entry = (TimeZoneNamesCacheEntry *)elem->value.pointer;
90 if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
91 // delete this entry
92 uhash_removeElement(gTimeZoneNamesCache, elem);
93 }
94 }
95 }
96
97 // ---------------------------------------------------
98 // TimeZoneNamesDelegate
99 // ---------------------------------------------------
100 class TimeZoneNamesDelegate : public TimeZoneNames {
101 public:
102 TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status);
103 virtual ~TimeZoneNamesDelegate();
104
105 virtual UBool operator==(const TimeZoneNames& other) const;
operator !=(const TimeZoneNames & other) const106 virtual UBool operator!=(const TimeZoneNames& other) const {return !operator==(other);};
107 virtual TimeZoneNames* clone() const;
108
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;
113
114 UnicodeString& getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const;
115 UnicodeString& getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const;
116
117 UnicodeString& getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const;
118
119 MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
120 private:
121 TimeZoneNamesDelegate();
122 TimeZoneNamesCacheEntry* fTZnamesCacheEntry;
123 };
124
TimeZoneNamesDelegate()125 TimeZoneNamesDelegate::TimeZoneNamesDelegate()
126 : fTZnamesCacheEntry(0) {
127 }
128
TimeZoneNamesDelegate(const Locale & locale,UErrorCode & status)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);
139 }
140 }
141
142 if (U_FAILURE(status)) {
143 return;
144 }
145
146 // Check the cache, if not available, create new one and cache
147 TimeZoneNamesCacheEntry *cacheEntry = NULL;
148
149 const char *key = locale.getName();
150 cacheEntry = (TimeZoneNamesCacheEntry *)uhash_get(gTimeZoneNamesCache, key);
151 if (cacheEntry == NULL) {
152 TimeZoneNames *tznames = NULL;
153 char *newKey = NULL;
154
155 tznames = new TimeZoneNamesImpl(locale, status);
156 if (tznames == NULL) {
157 status = U_MEMORY_ALLOCATION_ERROR;
158 }
159 if (U_SUCCESS(status)) {
160 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1);
161 if (newKey == NULL) {
162 status = U_MEMORY_ALLOCATION_ERROR;
163 } else {
164 uprv_strcpy(newKey, key);
165 }
166 }
167 if (U_SUCCESS(status)) {
168 cacheEntry = (TimeZoneNamesCacheEntry *)uprv_malloc(sizeof(TimeZoneNamesCacheEntry));
169 if (cacheEntry == NULL) {
170 status = U_MEMORY_ALLOCATION_ERROR;
171 } else {
172 cacheEntry->names = tznames;
173 cacheEntry->refCount = 1;
174 cacheEntry->lastAccess = (double)uprv_getUTCtime();
175
176 uhash_put(gTimeZoneNamesCache, newKey, cacheEntry, &status);
177 }
178 }
179 if (U_FAILURE(status)) {
180 if (tznames != NULL) {
181 delete tznames;
182 }
183 if (newKey != NULL) {
184 uprv_free(newKey);
185 }
186 if (cacheEntry != NULL) {
187 uprv_free(cacheEntry);
188 }
189 cacheEntry = NULL;
190 }
191 } else {
192 // Update the reference count
193 cacheEntry->refCount++;
194 cacheEntry->lastAccess = (double)uprv_getUTCtime();
195 }
196 gAccessCount++;
197 if (gAccessCount >= SWEEP_INTERVAL) {
198 // sweep
199 sweepCache();
200 gAccessCount = 0;
201 }
202 fTZnamesCacheEntry = cacheEntry;
203 }
204
~TimeZoneNamesDelegate()205 TimeZoneNamesDelegate::~TimeZoneNamesDelegate() {
206 umtx_lock(&gTimeZoneNamesLock);
207 {
208 if (fTZnamesCacheEntry) {
209 U_ASSERT(fTZnamesCacheEntry->refCount > 0);
210 // Just decrement the reference count
211 fTZnamesCacheEntry->refCount--;
212 }
213 }
214 umtx_unlock(&gTimeZoneNamesLock);
215 }
216
217 UBool
operator ==(const TimeZoneNames & other) const218 TimeZoneNamesDelegate::operator==(const TimeZoneNames& other) const {
219 if (this == &other) {
220 return TRUE;
221 }
222 // Just compare if the other object also use the same
223 // cache entry
224 const TimeZoneNamesDelegate* rhs = dynamic_cast<const TimeZoneNamesDelegate*>(&other);
225 if (rhs) {
226 return fTZnamesCacheEntry == rhs->fTZnamesCacheEntry;
227 }
228 return FALSE;
229 }
230
231 TimeZoneNames*
clone() const232 TimeZoneNamesDelegate::clone() const {
233 TimeZoneNamesDelegate* other = new TimeZoneNamesDelegate();
234 if (other != NULL) {
235 umtx_lock(&gTimeZoneNamesLock);
236 {
237 // Just increment the reference count
238 fTZnamesCacheEntry->refCount++;
239 other->fTZnamesCacheEntry = fTZnamesCacheEntry;
240 }
241 umtx_unlock(&gTimeZoneNamesLock);
242 }
243 return other;
244 }
245
246 StringEnumeration*
getAvailableMetaZoneIDs(UErrorCode & status) const247 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const {
248 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status);
249 }
250
251 StringEnumeration*
getAvailableMetaZoneIDs(const UnicodeString & tzID,UErrorCode & status) const252 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
253 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status);
254 }
255
256 UnicodeString&
getMetaZoneID(const UnicodeString & tzID,UDate date,UnicodeString & mzID) const257 TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
258 return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID);
259 }
260
261 UnicodeString&
getReferenceZoneID(const UnicodeString & mzID,const char * region,UnicodeString & tzID) const262 TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
263 return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID);
264 }
265
266 UnicodeString&
getMetaZoneDisplayName(const UnicodeString & mzID,UTimeZoneNameType type,UnicodeString & name) const267 TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const {
268 return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name);
269 }
270
271 UnicodeString&
getTimeZoneDisplayName(const UnicodeString & tzID,UTimeZoneNameType type,UnicodeString & name) const272 TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
273 return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name);
274 }
275
276 UnicodeString&
getExemplarLocationName(const UnicodeString & tzID,UnicodeString & name) const277 TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
278 return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name);
279 }
280
281 TimeZoneNames::MatchInfoCollection*
find(const UnicodeString & text,int32_t start,uint32_t types,UErrorCode & status) const282 TimeZoneNamesDelegate::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
283 return fTZnamesCacheEntry->names->find(text, start, types, status);
284 }
285
286 // ---------------------------------------------------
287 // TimeZoneNames base class
288 // ---------------------------------------------------
~TimeZoneNames()289 TimeZoneNames::~TimeZoneNames() {
290 }
291
292 TimeZoneNames*
createInstance(const Locale & locale,UErrorCode & status)293 TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) {
294 return new TimeZoneNamesDelegate(locale, status);
295 }
296
297 UnicodeString&
getExemplarLocationName(const UnicodeString & tzID,UnicodeString & name) const298 TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
299 return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name);
300 }
301
302 UnicodeString&
getDisplayName(const UnicodeString & tzID,UTimeZoneNameType type,UDate date,UnicodeString & name) const303 TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const {
304 getTimeZoneDisplayName(tzID, type, name);
305 if (name.isEmpty()) {
306 UnicodeString mzID;
307 getMetaZoneID(tzID, date, mzID);
308 getMetaZoneDisplayName(mzID, type, name);
309 }
310 return name;
311 }
312
313
314 struct MatchInfo : UMemory {
315 UTimeZoneNameType nameType;
316 UnicodeString id;
317 int32_t matchLength;
318 UBool isTZID;
319
MatchInfoMatchInfo320 MatchInfo(UTimeZoneNameType nameType, int32_t matchLength, const UnicodeString* tzID, const UnicodeString* mzID) {
321 this->nameType = nameType;
322 this->matchLength = matchLength;
323 if (tzID != NULL) {
324 this->id.setTo(*tzID);
325 this->isTZID = TRUE;
326 } else {
327 this->id.setTo(*mzID);
328 this->isTZID = FALSE;
329 }
330 }
331 };
332
333 U_CDECL_BEGIN
334 static void U_CALLCONV
deleteMatchInfo(void * obj)335 deleteMatchInfo(void *obj) {
336 delete static_cast<MatchInfo *>(obj);
337 }
338 U_CDECL_END
339
340 // ---------------------------------------------------
341 // MatchInfoCollection class
342 // ---------------------------------------------------
MatchInfoCollection()343 TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
344 : fMatches(NULL) {
345 }
346
~MatchInfoCollection()347 TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
348 if (fMatches != NULL) {
349 delete fMatches;
350 }
351 }
352
353 void
addZone(UTimeZoneNameType nameType,int32_t matchLength,const UnicodeString & tzID,UErrorCode & status)354 TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType, int32_t matchLength,
355 const UnicodeString& tzID, UErrorCode& status) {
356 if (U_FAILURE(status)) {
357 return;
358 }
359 MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, &tzID, NULL);
360 if (matchInfo == NULL) {
361 status = U_MEMORY_ALLOCATION_ERROR;
362 return;
363 }
364 matches(status)->addElement(matchInfo, status);
365 if (U_FAILURE(status)) {
366 delete matchInfo;
367 }
368 }
369
370 void
addMetaZone(UTimeZoneNameType nameType,int32_t matchLength,const UnicodeString & mzID,UErrorCode & status)371 TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType, int32_t matchLength,
372 const UnicodeString& mzID, UErrorCode& status) {
373 if (U_FAILURE(status)) {
374 return;
375 }
376 MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, NULL, &mzID);
377 if (matchInfo == NULL) {
378 status = U_MEMORY_ALLOCATION_ERROR;
379 return;
380 }
381 matches(status)->addElement(matchInfo, status);
382 if (U_FAILURE(status)) {
383 delete matchInfo;
384 }
385 }
386
387 int32_t
size() const388 TimeZoneNames::MatchInfoCollection::size() const {
389 if (fMatches == NULL) {
390 return 0;
391 }
392 return fMatches->size();
393 }
394
395 UTimeZoneNameType
getNameTypeAt(int32_t idx) const396 TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx) const {
397 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
398 if (match) {
399 return match->nameType;
400 }
401 return UTZNM_UNKNOWN;
402 }
403
404 int32_t
getMatchLengthAt(int32_t idx) const405 TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx) const {
406 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
407 if (match) {
408 return match->matchLength;
409 }
410 return 0;
411 }
412
413 UBool
getTimeZoneIDAt(int32_t idx,UnicodeString & tzID) const414 TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx, UnicodeString& tzID) const {
415 tzID.remove();
416 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
417 if (match && match->isTZID) {
418 tzID.setTo(match->id);
419 return TRUE;
420 }
421 return FALSE;
422 }
423
424 UBool
getMetaZoneIDAt(int32_t idx,UnicodeString & mzID) const425 TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx, UnicodeString& mzID) const {
426 mzID.remove();
427 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
428 if (match && !match->isTZID) {
429 mzID.setTo(match->id);
430 return TRUE;
431 }
432 return FALSE;
433 }
434
435 UVector*
matches(UErrorCode & status)436 TimeZoneNames::MatchInfoCollection::matches(UErrorCode& status) {
437 if (U_FAILURE(status)) {
438 return NULL;
439 }
440 if (fMatches != NULL) {
441 return fMatches;
442 }
443 fMatches = new UVector(deleteMatchInfo, NULL, status);
444 if (fMatches == NULL) {
445 status = U_MEMORY_ALLOCATION_ERROR;
446 } else if (U_FAILURE(status)) {
447 delete fMatches;
448 fMatches = NULL;
449 }
450 return fMatches;
451 }
452
453
454 U_NAMESPACE_END
455 #endif
456