]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/tznames.cpp
ICU-57166.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / tznames.cpp
CommitLineData
4388f060
A
1/*
2*******************************************************************************
b331163b 3* Copyright (C) 2011-2015, International Business Machines Corporation and *
4388f060
A
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"
57a6839d 17#include "mutex.h"
4388f060
A
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
27U_NAMESPACE_BEGIN
28
4388f060 29// TimeZoneNames object cache handling
51004dcb 30static UMutex gTimeZoneNamesLock = U_MUTEX_INITIALIZER;
4388f060
A
31static UHashtable *gTimeZoneNamesCache = NULL;
32static UBool gTimeZoneNamesCacheInitialized = FALSE;
33
34// Access count - incremented every time up to SWEEP_INTERVAL,
35// then reset to 0
36static 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
47typedef struct TimeZoneNamesCacheEntry {
48 TimeZoneNames* names;
49 int32_t refCount;
50 double lastAccess;
51} TimeZoneNamesCacheEntry;
52
53U_CDECL_BEGIN
54/**
55 * Cleanup callback func
56 */
57static UBool U_CALLCONV timeZoneNames_cleanup(void)
58{
4388f060
A
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 */
70static void U_CALLCONV
71deleteTimeZoneNamesCacheEntry(void *obj) {
72 icu::TimeZoneNamesCacheEntry *entry = (icu::TimeZoneNamesCacheEntry*)obj;
73 delete (icu::TimeZoneNamesImpl*) entry->names;
74 uprv_free(entry);
75}
76U_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 */
83static void sweepCache() {
b331163b 84 int32_t pos = UHASH_FIRST;
4388f060
A
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// ---------------------------------------------------
100class TimeZoneNamesDelegate : public TimeZoneNames {
101public:
102 TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status);
103 virtual ~TimeZoneNamesDelegate();
104
105 virtual UBool operator==(const TimeZoneNames& other) const;
106 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;
120private:
121 TimeZoneNamesDelegate();
122 TimeZoneNamesCacheEntry* fTZnamesCacheEntry;
123};
124
125TimeZoneNamesDelegate::TimeZoneNamesDelegate()
126: fTZnamesCacheEntry(0) {
127}
128
129TimeZoneNamesDelegate::TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status) {
57a6839d
A
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);
4388f060 139 }
57a6839d 140 }
4388f060 141
57a6839d
A
142 if (U_FAILURE(status)) {
143 return;
4388f060
A
144 }
145
146 // Check the cache, if not available, create new one and cache
147 TimeZoneNamesCacheEntry *cacheEntry = NULL;
57a6839d
A
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) {
4388f060 162 status = U_MEMORY_ALLOCATION_ERROR;
57a6839d
A
163 } else {
164 uprv_strcpy(newKey, key);
4388f060 165 }
57a6839d
A
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);
4388f060 177 }
57a6839d
A
178 }
179 if (U_FAILURE(status)) {
180 if (tznames != NULL) {
181 delete tznames;
4388f060 182 }
57a6839d
A
183 if (newKey != NULL) {
184 uprv_free(newKey);
4388f060 185 }
57a6839d
A
186 if (cacheEntry != NULL) {
187 uprv_free(cacheEntry);
188 }
189 cacheEntry = NULL;
4388f060 190 }
57a6839d
A
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;
4388f060 201 }
4388f060
A
202 fTZnamesCacheEntry = cacheEntry;
203}
204
205TimeZoneNamesDelegate::~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
217UBool
218TimeZoneNamesDelegate::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
231TimeZoneNames*
232TimeZoneNamesDelegate::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
246StringEnumeration*
247TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const {
248 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status);
249}
250
251StringEnumeration*
252TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
253 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status);
254}
255
256UnicodeString&
257TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
258 return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID);
259}
260
261UnicodeString&
262TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
263 return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID);
264}
265
266UnicodeString&
267TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const {
268 return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name);
269}
270
271UnicodeString&
272TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
273 return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name);
274}
275
276UnicodeString&
277TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
278 return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name);
279}
280
281TimeZoneNames::MatchInfoCollection*
282TimeZoneNamesDelegate::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// ---------------------------------------------------
4388f060
A
289TimeZoneNames::~TimeZoneNames() {
290}
291
292TimeZoneNames*
293TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) {
b331163b
A
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
304TimeZoneNames*
305TimeZoneNames::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;
4388f060
A
314}
315
316UnicodeString&
317TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
51004dcb 318 return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name);
4388f060
A
319}
320
321UnicodeString&
322TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const {
323 getTimeZoneDisplayName(tzID, type, name);
324 if (name.isEmpty()) {
b331163b
A
325 UChar mzIDBuf[32];
326 UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
4388f060
A
327 getMetaZoneID(tzID, date, mzID);
328 getMetaZoneDisplayName(mzID, type, name);
329 }
330 return name;
331}
332
333
334struct MatchInfo : UMemory {
335 UTimeZoneNameType nameType;
336 UnicodeString id;
337 int32_t matchLength;
338 UBool isTZID;
339
340 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
353U_CDECL_BEGIN
354static void U_CALLCONV
355deleteMatchInfo(void *obj) {
356 delete static_cast<MatchInfo *>(obj);
357}
358U_CDECL_END
359
360// ---------------------------------------------------
361// MatchInfoCollection class
362// ---------------------------------------------------
363TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
364: fMatches(NULL) {
365}
366
367TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
368 if (fMatches != NULL) {
369 delete fMatches;
370 }
371}
372
373void
374TimeZoneNames::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
390void
391TimeZoneNames::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
407int32_t
408TimeZoneNames::MatchInfoCollection::size() const {
409 if (fMatches == NULL) {
410 return 0;
411 }
412 return fMatches->size();
413}
414
415UTimeZoneNameType
416TimeZoneNames::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
424int32_t
425TimeZoneNames::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
433UBool
434TimeZoneNames::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
444UBool
445TimeZoneNames::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
455UVector*
456TimeZoneNames::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
474U_NAMESPACE_END
475#endif