1 /*
2 *******************************************************************************
3 * Copyright (C) 2011-2015, 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 = UHASH_FIRST;
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     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;
299         }
300     }
301     return instance;
302 }
303 
304 TimeZoneNames*
createTZDBInstance(const Locale & locale,UErrorCode & status)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;
311         }
312     }
313     return instance;
314 }
315 
316 UnicodeString&
getExemplarLocationName(const UnicodeString & tzID,UnicodeString & name) const317 TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
318     return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name);
319 }
320 
321 UnicodeString&
getDisplayName(const UnicodeString & tzID,UTimeZoneNameType type,UDate date,UnicodeString & name) const322 TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const {
323     getTimeZoneDisplayName(tzID, type, name);
324     if (name.isEmpty()) {
325         UChar mzIDBuf[32];
326         UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
327         getMetaZoneID(tzID, date, mzID);
328         getMetaZoneDisplayName(mzID, type, name);
329     }
330     return name;
331 }
332 
333 
334 struct MatchInfo : UMemory {
335     UTimeZoneNameType nameType;
336     UnicodeString id;
337     int32_t matchLength;
338     UBool isTZID;
339 
MatchInfoMatchInfo340     MatchInfo(UTimeZoneNameType nameType, int32_t matchLength, const UnicodeString* tzID, const UnicodeString* mzID) {
341         this->nameType = nameType;
342         this->matchLength = matchLength;
343         if (tzID != NULL) {
344             this->id.setTo(*tzID);
345             this->isTZID = TRUE;
346         } else {
347             this->id.setTo(*mzID);
348             this->isTZID = FALSE;
349         }
350     }
351 };
352 
353 U_CDECL_BEGIN
354 static void U_CALLCONV
deleteMatchInfo(void * obj)355 deleteMatchInfo(void *obj) {
356     delete static_cast<MatchInfo *>(obj);
357 }
358 U_CDECL_END
359 
360 // ---------------------------------------------------
361 // MatchInfoCollection class
362 // ---------------------------------------------------
MatchInfoCollection()363 TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
364 : fMatches(NULL) {
365 }
366 
~MatchInfoCollection()367 TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
368     if (fMatches != NULL) {
369         delete fMatches;
370     }
371 }
372 
373 void
addZone(UTimeZoneNameType nameType,int32_t matchLength,const UnicodeString & tzID,UErrorCode & status)374 TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType, int32_t matchLength,
375             const UnicodeString& tzID, UErrorCode& status) {
376     if (U_FAILURE(status)) {
377         return;
378     }
379     MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, &tzID, NULL);
380     if (matchInfo == NULL) {
381         status = U_MEMORY_ALLOCATION_ERROR;
382         return;
383     }
384     matches(status)->addElement(matchInfo, status);
385     if (U_FAILURE(status)) {
386         delete matchInfo;
387     }
388 }
389 
390 void
addMetaZone(UTimeZoneNameType nameType,int32_t matchLength,const UnicodeString & mzID,UErrorCode & status)391 TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType, int32_t matchLength,
392             const UnicodeString& mzID, UErrorCode& status) {
393     if (U_FAILURE(status)) {
394         return;
395     }
396     MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, NULL, &mzID);
397     if (matchInfo == NULL) {
398         status = U_MEMORY_ALLOCATION_ERROR;
399         return;
400     }
401     matches(status)->addElement(matchInfo, status);
402     if (U_FAILURE(status)) {
403         delete matchInfo;
404     }
405 }
406 
407 int32_t
size() const408 TimeZoneNames::MatchInfoCollection::size() const {
409     if (fMatches == NULL) {
410         return 0;
411     }
412     return fMatches->size();
413 }
414 
415 UTimeZoneNameType
getNameTypeAt(int32_t idx) const416 TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx) const {
417     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
418     if (match) {
419         return match->nameType;
420     }
421     return UTZNM_UNKNOWN;
422 }
423 
424 int32_t
getMatchLengthAt(int32_t idx) const425 TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx) const {
426     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
427     if (match) {
428         return match->matchLength;
429     }
430     return 0;
431 }
432 
433 UBool
getTimeZoneIDAt(int32_t idx,UnicodeString & tzID) const434 TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx, UnicodeString& tzID) const {
435     tzID.remove();
436     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
437     if (match && match->isTZID) {
438         tzID.setTo(match->id);
439         return TRUE;
440     }
441     return FALSE;
442 }
443 
444 UBool
getMetaZoneIDAt(int32_t idx,UnicodeString & mzID) const445 TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx, UnicodeString& mzID) const {
446     mzID.remove();
447     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
448     if (match && !match->isTZID) {
449         mzID.setTo(match->id);
450         return TRUE;
451     }
452     return FALSE;
453 }
454 
455 UVector*
matches(UErrorCode & status)456 TimeZoneNames::MatchInfoCollection::matches(UErrorCode& status) {
457     if (U_FAILURE(status)) {
458         return NULL;
459     }
460     if (fMatches != NULL) {
461         return fMatches;
462     }
463     fMatches = new UVector(deleteMatchInfo, NULL, status);
464     if (fMatches == NULL) {
465         status = U_MEMORY_ALLOCATION_ERROR;
466     } else if (U_FAILURE(status)) {
467         delete fMatches;
468         fMatches = NULL;
469     }
470     return fMatches;
471 }
472 
473 
474 U_NAMESPACE_END
475 #endif
476