]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/tznames.cpp
ICU-511.35.tar.gz
[apple/icu.git] / icuSources / i18n / tznames.cpp
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 */
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
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 */
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;
105 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
124 TimeZoneNamesDelegate::TimeZoneNamesDelegate()
125 : fTZnamesCacheEntry(0) {
126 }
127
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
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
228 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*
242 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*
257 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const {
258 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status);
259 }
260
261 StringEnumeration*
262 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
263 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status);
264 }
265
266 UnicodeString&
267 TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
268 return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID);
269 }
270
271 UnicodeString&
272 TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
273 return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID);
274 }
275
276 UnicodeString&
277 TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const {
278 return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name);
279 }
280
281 UnicodeString&
282 TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
283 return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name);
284 }
285
286 UnicodeString&
287 TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
288 return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name);
289 }
290
291 TimeZoneNames::MatchInfoCollection*
292 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 // ---------------------------------------------------
299 TimeZoneNames::~TimeZoneNames() {
300 }
301
302 TimeZoneNames*
303 TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) {
304 return new TimeZoneNamesDelegate(locale, status);
305 }
306
307 UnicodeString&
308 TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
309 return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name);
310 }
311
312 UnicodeString&
313 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
330 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
345 deleteMatchInfo(void *obj) {
346 delete static_cast<MatchInfo *>(obj);
347 }
348 U_CDECL_END
349
350 // ---------------------------------------------------
351 // MatchInfoCollection class
352 // ---------------------------------------------------
353 TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
354 : fMatches(NULL) {
355 }
356
357 TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
358 if (fMatches != NULL) {
359 delete fMatches;
360 }
361 }
362
363 void
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
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
398 TimeZoneNames::MatchInfoCollection::size() const {
399 if (fMatches == NULL) {
400 return 0;
401 }
402 return fMatches->size();
403 }
404
405 UTimeZoneNameType
406 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
415 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
424 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
435 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*
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