• 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 "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