• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 * Copyright (C) 2011-2012, 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 static const UChar gEtcPrefix[]         = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
29 static const int32_t gEtcPrefixLen      = 4;
30 static const UChar gSystemVPrefix[]     = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
31 static const int32_t gSystemVPrefixLen  = 8;
32 static const UChar gRiyadh8[]           = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
33 static const int32_t gRiyadh8Len       = 7;
34 
35 // TimeZoneNames object cache handling
36 static UMutex gTimeZoneNamesLock = U_MUTEX_INITIALIZER;
37 static UHashtable *gTimeZoneNamesCache = NULL;
38 static UBool gTimeZoneNamesCacheInitialized = FALSE;
39 
40 // Access count - incremented every time up to SWEEP_INTERVAL,
41 // then reset to 0
42 static int32_t gAccessCount = 0;
43 
44 // Interval for calling the cache sweep function - every 100 times
45 #define SWEEP_INTERVAL 100
46 
47 // Cache expiration in millisecond. When a cached entry is no
48 // longer referenced and exceeding this threshold since last
49 // access time, then the cache entry will be deleted by the sweep
50 // function. For now, 3 minutes.
51 #define CACHE_EXPIRATION 180000.0
52 
53 typedef struct TimeZoneNamesCacheEntry {
54     TimeZoneNames*  names;
55     int32_t         refCount;
56     double          lastAccess;
57 } TimeZoneNamesCacheEntry;
58 
59 U_CDECL_BEGIN
60 /**
61  * Cleanup callback func
62  */
timeZoneNames_cleanup(void)63 static UBool U_CALLCONV timeZoneNames_cleanup(void)
64 {
65     if (gTimeZoneNamesCache != NULL) {
66         uhash_close(gTimeZoneNamesCache);
67         gTimeZoneNamesCache = NULL;
68     }
69     gTimeZoneNamesCacheInitialized = FALSE;
70     return TRUE;
71 }
72 
73 /**
74  * Deleter for TimeZoneNamesCacheEntry
75  */
76 static void U_CALLCONV
deleteTimeZoneNamesCacheEntry(void * obj)77 deleteTimeZoneNamesCacheEntry(void *obj) {
78     icu::TimeZoneNamesCacheEntry *entry = (icu::TimeZoneNamesCacheEntry*)obj;
79     delete (icu::TimeZoneNamesImpl*) entry->names;
80     uprv_free(entry);
81 }
82 U_CDECL_END
83 
84 /**
85  * Function used for removing unreferrenced cache entries exceeding
86  * the expiration time. This function must be called with in the mutex
87  * block.
88  */
sweepCache()89 static void sweepCache() {
90     int32_t pos = -1;
91     const UHashElement* elem;
92     double now = (double)uprv_getUTCtime();
93 
94     while ((elem = uhash_nextElement(gTimeZoneNamesCache, &pos))) {
95         TimeZoneNamesCacheEntry *entry = (TimeZoneNamesCacheEntry *)elem->value.pointer;
96         if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
97             // delete this entry
98             uhash_removeElement(gTimeZoneNamesCache, elem);
99         }
100     }
101 }
102 
103 // ---------------------------------------------------
104 // TimeZoneNamesDelegate
105 // ---------------------------------------------------
106 class TimeZoneNamesDelegate : public TimeZoneNames {
107 public:
108     TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status);
109     virtual ~TimeZoneNamesDelegate();
110 
111     virtual UBool operator==(const TimeZoneNames& other) const;
operator !=(const TimeZoneNames & other) const112     virtual UBool operator!=(const TimeZoneNames& other) const {return !operator==(other);};
113     virtual TimeZoneNames* clone() const;
114 
115     StringEnumeration* getAvailableMetaZoneIDs(UErrorCode& status) const;
116     StringEnumeration* getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const;
117     UnicodeString& getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const;
118     UnicodeString& getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const;
119 
120     UnicodeString& getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const;
121     UnicodeString& getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const;
122 
123     UnicodeString& getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const;
124 
125     MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
126 private:
127     TimeZoneNamesDelegate();
128     TimeZoneNamesCacheEntry*    fTZnamesCacheEntry;
129 };
130 
TimeZoneNamesDelegate()131 TimeZoneNamesDelegate::TimeZoneNamesDelegate()
132 : fTZnamesCacheEntry(0) {
133 }
134 
TimeZoneNamesDelegate(const Locale & locale,UErrorCode & status)135 TimeZoneNamesDelegate::TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status) {
136     UBool initialized;
137     UMTX_CHECK(&gTimeZoneNamesLock, gTimeZoneNamesCacheInitialized, initialized);
138     if (!initialized) {
139         // Create empty hashtable
140         umtx_lock(&gTimeZoneNamesLock);
141         {
142             if (!gTimeZoneNamesCacheInitialized) {
143                 gTimeZoneNamesCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
144                 if (U_SUCCESS(status)) {
145                     uhash_setKeyDeleter(gTimeZoneNamesCache, uprv_free);
146                     uhash_setValueDeleter(gTimeZoneNamesCache, deleteTimeZoneNamesCacheEntry);
147                     gTimeZoneNamesCacheInitialized = TRUE;
148                     ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONENAMES, timeZoneNames_cleanup);
149                 }
150             }
151         }
152         umtx_unlock(&gTimeZoneNamesLock);
153 
154         if (U_FAILURE(status)) {
155             return;
156         }
157     }
158 
159     // Check the cache, if not available, create new one and cache
160     TimeZoneNamesCacheEntry *cacheEntry = NULL;
161     umtx_lock(&gTimeZoneNamesLock);
162     {
163         const char *key = locale.getName();
164         cacheEntry = (TimeZoneNamesCacheEntry *)uhash_get(gTimeZoneNamesCache, key);
165         if (cacheEntry == NULL) {
166             TimeZoneNames *tznames = NULL;
167             char *newKey = NULL;
168 
169             tznames = new TimeZoneNamesImpl(locale, status);
170             if (tznames == NULL) {
171                 status = U_MEMORY_ALLOCATION_ERROR;
172             }
173             if (U_SUCCESS(status)) {
174                 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1);
175                 if (newKey == NULL) {
176                     status = U_MEMORY_ALLOCATION_ERROR;
177                 } else {
178                     uprv_strcpy(newKey, key);
179                 }
180             }
181             if (U_SUCCESS(status)) {
182                 cacheEntry = (TimeZoneNamesCacheEntry *)uprv_malloc(sizeof(TimeZoneNamesCacheEntry));
183                 if (cacheEntry == NULL) {
184                     status = U_MEMORY_ALLOCATION_ERROR;
185                 } else {
186                     cacheEntry->names = tznames;
187                     cacheEntry->refCount = 1;
188                     cacheEntry->lastAccess = (double)uprv_getUTCtime();
189 
190                     uhash_put(gTimeZoneNamesCache, newKey, cacheEntry, &status);
191                 }
192             }
193             if (U_FAILURE(status)) {
194                 if (tznames != NULL) {
195                     delete tznames;
196                 }
197                 if (newKey != NULL) {
198                     uprv_free(newKey);
199                 }
200                 if (cacheEntry != NULL) {
201                     uprv_free(cacheEntry);
202                 }
203                 cacheEntry = NULL;
204             }
205         } else {
206             // Update the reference count
207             cacheEntry->refCount++;
208             cacheEntry->lastAccess = (double)uprv_getUTCtime();
209         }
210         gAccessCount++;
211         if (gAccessCount >= SWEEP_INTERVAL) {
212             // sweep
213             sweepCache();
214             gAccessCount = 0;
215         }
216     }
217     umtx_unlock(&gTimeZoneNamesLock);
218 
219     fTZnamesCacheEntry = cacheEntry;
220 }
221 
~TimeZoneNamesDelegate()222 TimeZoneNamesDelegate::~TimeZoneNamesDelegate() {
223     umtx_lock(&gTimeZoneNamesLock);
224     {
225         if (fTZnamesCacheEntry) {
226             U_ASSERT(fTZnamesCacheEntry->refCount > 0);
227             // Just decrement the reference count
228             fTZnamesCacheEntry->refCount--;
229         }
230     }
231     umtx_unlock(&gTimeZoneNamesLock);
232 }
233 
234 UBool
operator ==(const TimeZoneNames & other) const235 TimeZoneNamesDelegate::operator==(const TimeZoneNames& other) const {
236     if (this == &other) {
237         return TRUE;
238     }
239     // Just compare if the other object also use the same
240     // cache entry
241     const TimeZoneNamesDelegate* rhs = dynamic_cast<const TimeZoneNamesDelegate*>(&other);
242     if (rhs) {
243         return fTZnamesCacheEntry == rhs->fTZnamesCacheEntry;
244     }
245     return FALSE;
246 }
247 
248 TimeZoneNames*
clone() const249 TimeZoneNamesDelegate::clone() const {
250     TimeZoneNamesDelegate* other = new TimeZoneNamesDelegate();
251     if (other != NULL) {
252         umtx_lock(&gTimeZoneNamesLock);
253         {
254             // Just increment the reference count
255             fTZnamesCacheEntry->refCount++;
256             other->fTZnamesCacheEntry = fTZnamesCacheEntry;
257         }
258         umtx_unlock(&gTimeZoneNamesLock);
259     }
260     return other;
261 }
262 
263 StringEnumeration*
getAvailableMetaZoneIDs(UErrorCode & status) const264 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const {
265     return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status);
266 }
267 
268 StringEnumeration*
getAvailableMetaZoneIDs(const UnicodeString & tzID,UErrorCode & status) const269 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
270     return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status);
271 }
272 
273 UnicodeString&
getMetaZoneID(const UnicodeString & tzID,UDate date,UnicodeString & mzID) const274 TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
275     return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID);
276 }
277 
278 UnicodeString&
getReferenceZoneID(const UnicodeString & mzID,const char * region,UnicodeString & tzID) const279 TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
280     return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID);
281 }
282 
283 UnicodeString&
getMetaZoneDisplayName(const UnicodeString & mzID,UTimeZoneNameType type,UnicodeString & name) const284 TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const {
285     return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name);
286 }
287 
288 UnicodeString&
getTimeZoneDisplayName(const UnicodeString & tzID,UTimeZoneNameType type,UnicodeString & name) const289 TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
290     return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name);
291 }
292 
293 UnicodeString&
getExemplarLocationName(const UnicodeString & tzID,UnicodeString & name) const294 TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
295     return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name);
296 }
297 
298 TimeZoneNames::MatchInfoCollection*
find(const UnicodeString & text,int32_t start,uint32_t types,UErrorCode & status) const299 TimeZoneNamesDelegate::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
300     return fTZnamesCacheEntry->names->find(text, start, types, status);
301 }
302 
303 // ---------------------------------------------------
304 // TimeZoneNames base class
305 // ---------------------------------------------------
UOBJECT_DEFINE_NO_RTTI_IMPLEMENTATION(TimeZoneNames)306 UOBJECT_DEFINE_NO_RTTI_IMPLEMENTATION(TimeZoneNames)
307 
308 TimeZoneNames::~TimeZoneNames() {
309 }
310 
311 TimeZoneNames*
createInstance(const Locale & locale,UErrorCode & status)312 TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) {
313     return new TimeZoneNamesDelegate(locale, status);
314 }
315 
316 UnicodeString&
getExemplarLocationName(const UnicodeString & tzID,UnicodeString & name) const317 TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
318     if (tzID.isEmpty() || tzID.startsWith(gEtcPrefix, gEtcPrefixLen)
319         || tzID.startsWith(gSystemVPrefix, gSystemVPrefixLen) || tzID.indexOf(gRiyadh8, gRiyadh8Len, 0) > 0) {
320         name.setToBogus();
321         return name;
322     }
323 
324     int32_t sep = tzID.lastIndexOf((UChar)0x2F /* '/' */);
325     if (sep > 0 && sep + 1 < tzID.length()) {
326         name.setTo(tzID, sep + 1);
327         name.findAndReplace(UnicodeString((UChar)0x5f /* _ */),
328                             UnicodeString((UChar)0x20 /* space */));
329     } else {
330         name.setToBogus();
331     }
332     return name;
333 }
334 
335 UnicodeString&
getDisplayName(const UnicodeString & tzID,UTimeZoneNameType type,UDate date,UnicodeString & name) const336 TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const {
337     getTimeZoneDisplayName(tzID, type, name);
338     if (name.isEmpty()) {
339         UnicodeString mzID;
340         getMetaZoneID(tzID, date, mzID);
341         getMetaZoneDisplayName(mzID, type, name);
342     }
343     return name;
344 }
345 
346 
347 struct MatchInfo : UMemory {
348     UTimeZoneNameType nameType;
349     UnicodeString id;
350     int32_t matchLength;
351     UBool isTZID;
352 
MatchInfoMatchInfo353     MatchInfo(UTimeZoneNameType nameType, int32_t matchLength, const UnicodeString* tzID, const UnicodeString* mzID) {
354         this->nameType = nameType;
355         this->matchLength = matchLength;
356         if (tzID != NULL) {
357             this->id.setTo(*tzID);
358             this->isTZID = TRUE;
359         } else {
360             this->id.setTo(*mzID);
361             this->isTZID = FALSE;
362         }
363     }
364 };
365 
366 U_CDECL_BEGIN
367 static void U_CALLCONV
deleteMatchInfo(void * obj)368 deleteMatchInfo(void *obj) {
369     delete static_cast<MatchInfo *>(obj);
370 }
371 U_CDECL_END
372 
373 // ---------------------------------------------------
374 // MatchInfoCollection class
375 // ---------------------------------------------------
MatchInfoCollection()376 TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
377 : fMatches(NULL) {
378 }
379 
~MatchInfoCollection()380 TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
381     if (fMatches != NULL) {
382         delete fMatches;
383     }
384 }
385 
386 void
addZone(UTimeZoneNameType nameType,int32_t matchLength,const UnicodeString & tzID,UErrorCode & status)387 TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType, int32_t matchLength,
388             const UnicodeString& tzID, UErrorCode& status) {
389     if (U_FAILURE(status)) {
390         return;
391     }
392     MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, &tzID, NULL);
393     if (matchInfo == NULL) {
394         status = U_MEMORY_ALLOCATION_ERROR;
395         return;
396     }
397     matches(status)->addElement(matchInfo, status);
398     if (U_FAILURE(status)) {
399         delete matchInfo;
400     }
401 }
402 
403 void
addMetaZone(UTimeZoneNameType nameType,int32_t matchLength,const UnicodeString & mzID,UErrorCode & status)404 TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType, int32_t matchLength,
405             const UnicodeString& mzID, UErrorCode& status) {
406     if (U_FAILURE(status)) {
407         return;
408     }
409     MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, NULL, &mzID);
410     if (matchInfo == NULL) {
411         status = U_MEMORY_ALLOCATION_ERROR;
412         return;
413     }
414     matches(status)->addElement(matchInfo, status);
415     if (U_FAILURE(status)) {
416         delete matchInfo;
417     }
418 }
419 
420 int32_t
size() const421 TimeZoneNames::MatchInfoCollection::size() const {
422     if (fMatches == NULL) {
423         return 0;
424     }
425     return fMatches->size();
426 }
427 
428 UTimeZoneNameType
getNameTypeAt(int32_t idx) const429 TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx) const {
430     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
431     if (match) {
432         return match->nameType;
433     }
434     return UTZNM_UNKNOWN;
435 }
436 
437 int32_t
getMatchLengthAt(int32_t idx) const438 TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx) const {
439     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
440     if (match) {
441         return match->matchLength;
442     }
443     return 0;
444 }
445 
446 UBool
getTimeZoneIDAt(int32_t idx,UnicodeString & tzID) const447 TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx, UnicodeString& tzID) const {
448     tzID.remove();
449     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
450     if (match && match->isTZID) {
451         tzID.setTo(match->id);
452         return TRUE;
453     }
454     return FALSE;
455 }
456 
457 UBool
getMetaZoneIDAt(int32_t idx,UnicodeString & mzID) const458 TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx, UnicodeString& mzID) const {
459     mzID.remove();
460     const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
461     if (match && !match->isTZID) {
462         mzID.setTo(match->id);
463         return TRUE;
464     }
465     return FALSE;
466 }
467 
468 UVector*
matches(UErrorCode & status)469 TimeZoneNames::MatchInfoCollection::matches(UErrorCode& status) {
470     if (U_FAILURE(status)) {
471         return NULL;
472     }
473     if (fMatches != NULL) {
474         return fMatches;
475     }
476     fMatches = new UVector(deleteMatchInfo, NULL, status);
477     if (fMatches == NULL) {
478         status = U_MEMORY_ALLOCATION_ERROR;
479     } else if (U_FAILURE(status)) {
480         delete fMatches;
481         fMatches = NULL;
482     }
483     return fMatches;
484 }
485 
486 
487 U_NAMESPACE_END
488 #endif
489