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