/src/icu/source/i18n/tzgnames.cpp
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | // © 2016 and later: Unicode, Inc. and others.  | 
2  |  | // License & terms of use: http://www.unicode.org/copyright.html  | 
3  |  | /*  | 
4  |  | *******************************************************************************  | 
5  |  | * Copyright (C) 2011-2016, International Business Machines Corporation and  | 
6  |  | * others. All Rights Reserved.  | 
7  |  | *******************************************************************************  | 
8  |  | */  | 
9  |  |  | 
10  |  | #include "unicode/utypes.h"  | 
11  |  |  | 
12  |  | #if !UCONFIG_NO_FORMATTING  | 
13  |  |  | 
14  |  | #include "tzgnames.h"  | 
15  |  |  | 
16  |  | #include "unicode/basictz.h"  | 
17  |  | #include "unicode/locdspnm.h"  | 
18  |  | #include "unicode/rbtz.h"  | 
19  |  | #include "unicode/simpleformatter.h"  | 
20  |  | #include "unicode/simpletz.h"  | 
21  |  | #include "unicode/strenum.h"  | 
22  |  | #include "unicode/vtzone.h"  | 
23  |  |  | 
24  |  | #include "bytesinkutil.h"  | 
25  |  | #include "charstr.h"  | 
26  |  | #include "cmemory.h"  | 
27  |  | #include "cstring.h"  | 
28  |  | #include "mutex.h"  | 
29  |  | #include "uhash.h"  | 
30  |  | #include "uassert.h"  | 
31  |  | #include "umutex.h"  | 
32  |  | #include "ulocimp.h"  | 
33  |  | #include "uresimp.h"  | 
34  |  | #include "ureslocs.h"  | 
35  |  | #include "zonemeta.h"  | 
36  |  | #include "tznames_impl.h"  | 
37  |  | #include "olsontz.h"  | 
38  |  | #include "ucln_in.h"  | 
39  |  |  | 
40  |  | U_NAMESPACE_BEGIN  | 
41  |  |  | 
42  | 0  | #define ZID_KEY_MAX  128  | 
43  |  |  | 
44  |  | static const char gZoneStrings[]                = "zoneStrings";  | 
45  |  |  | 
46  |  | static const char gRegionFormatTag[]            = "regionFormat";  | 
47  |  | static const char gFallbackFormatTag[]          = "fallbackFormat";  | 
48  |  |  | 
49  |  | static const UChar gEmpty[]                     = {0x00}; | 
50  |  |  | 
51  |  | static const UChar gDefRegionPattern[]          = {0x7B, 0x30, 0x7D, 0x00}; // "{0}" | 
52  |  | static const UChar gDefFallbackPattern[]        = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})" | 
53  |  |  | 
54  |  | static const double kDstCheckRange      = (double)184*U_MILLIS_PER_DAY;  | 
55  |  |  | 
56  |  |  | 
57  |  |  | 
58  |  | U_CDECL_BEGIN  | 
59  |  |  | 
60  |  | typedef struct PartialLocationKey { | 
61  |  |     const UChar* tzID;  | 
62  |  |     const UChar* mzID;  | 
63  |  |     UBool isLong;  | 
64  |  | } PartialLocationKey;  | 
65  |  |  | 
66  |  | /**  | 
67  |  |  * Hash function for partial location name hash key  | 
68  |  |  */  | 
69  |  | static int32_t U_CALLCONV  | 
70  | 0  | hashPartialLocationKey(const UHashTok key) { | 
71  |  |     // <tzID>&<mzID>#[L|S]  | 
72  | 0  |     PartialLocationKey *p = (PartialLocationKey *)key.pointer;  | 
73  | 0  |     UnicodeString str(p->tzID);  | 
74  | 0  |     str.append((UChar)0x26)  | 
75  | 0  |         .append(p->mzID, -1)  | 
76  | 0  |         .append((UChar)0x23)  | 
77  | 0  |         .append((UChar)(p->isLong ? 0x4C : 0x53));  | 
78  | 0  |     return str.hashCode();  | 
79  | 0  | }  | 
80  |  |  | 
81  |  | /**  | 
82  |  |  * Comparer for partial location name hash key  | 
83  |  |  */  | 
84  |  | static UBool U_CALLCONV  | 
85  | 0  | comparePartialLocationKey(const UHashTok key1, const UHashTok key2) { | 
86  | 0  |     PartialLocationKey *p1 = (PartialLocationKey *)key1.pointer;  | 
87  | 0  |     PartialLocationKey *p2 = (PartialLocationKey *)key2.pointer;  | 
88  |  | 
  | 
89  | 0  |     if (p1 == p2) { | 
90  | 0  |         return TRUE;  | 
91  | 0  |     }  | 
92  | 0  |     if (p1 == NULL || p2 == NULL) { | 
93  | 0  |         return FALSE;  | 
94  | 0  |     }  | 
95  |  |     // We just check identity of tzID/mzID  | 
96  | 0  |     return (p1->tzID == p2->tzID && p1->mzID == p2->mzID && p1->isLong == p2->isLong);  | 
97  | 0  | }  | 
98  |  |  | 
99  |  | /**  | 
100  |  |  * Deleter for GNameInfo  | 
101  |  |  */  | 
102  |  | static void U_CALLCONV  | 
103  | 0  | deleteGNameInfo(void *obj) { | 
104  | 0  |     uprv_free(obj);  | 
105  | 0  | }  | 
106  |  |  | 
107  |  | /**  | 
108  |  |  * GNameInfo stores zone name information in the local trie  | 
109  |  |  */  | 
110  |  | typedef struct GNameInfo { | 
111  |  |     UTimeZoneGenericNameType    type;  | 
112  |  |     const UChar*                tzID;  | 
113  |  | } ZNameInfo;  | 
114  |  |  | 
115  |  | /**  | 
116  |  |  * GMatchInfo stores zone name match information used by find method  | 
117  |  |  */  | 
118  |  | typedef struct GMatchInfo { | 
119  |  |     const GNameInfo*    gnameInfo;  | 
120  |  |     int32_t             matchLength;  | 
121  |  |     UTimeZoneFormatTimeType   timeType;  | 
122  |  | } ZMatchInfo;  | 
123  |  |  | 
124  |  | U_CDECL_END  | 
125  |  |  | 
126  |  | // ---------------------------------------------------  | 
127  |  | // The class stores time zone generic name match information  | 
128  |  | // ---------------------------------------------------  | 
129  |  | class TimeZoneGenericNameMatchInfo : public UMemory { | 
130  |  | public:  | 
131  |  |     TimeZoneGenericNameMatchInfo(UVector* matches);  | 
132  |  |     ~TimeZoneGenericNameMatchInfo();  | 
133  |  |  | 
134  |  |     int32_t size() const;  | 
135  |  |     UTimeZoneGenericNameType getGenericNameType(int32_t index) const;  | 
136  |  |     int32_t getMatchLength(int32_t index) const;  | 
137  |  |     UnicodeString& getTimeZoneID(int32_t index, UnicodeString& tzID) const;  | 
138  |  |  | 
139  |  | private:  | 
140  |  |     UVector* fMatches;  // vector of MatchEntry  | 
141  |  | };  | 
142  |  |  | 
143  |  | TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches)  | 
144  | 0  | : fMatches(matches) { | 
145  | 0  | }  | 
146  |  |  | 
147  | 0  | TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() { | 
148  | 0  |     if (fMatches != NULL) { | 
149  | 0  |         delete fMatches;  | 
150  | 0  |     }  | 
151  | 0  | }  | 
152  |  |  | 
153  |  | int32_t  | 
154  | 0  | TimeZoneGenericNameMatchInfo::size() const { | 
155  | 0  |     if (fMatches == NULL) { | 
156  | 0  |         return 0;  | 
157  | 0  |     }  | 
158  | 0  |     return fMatches->size();  | 
159  | 0  | }  | 
160  |  |  | 
161  |  | UTimeZoneGenericNameType  | 
162  | 0  | TimeZoneGenericNameMatchInfo::getGenericNameType(int32_t index) const { | 
163  | 0  |     GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index);  | 
164  | 0  |     if (minfo != NULL) { | 
165  | 0  |         return static_cast<UTimeZoneGenericNameType>(minfo->gnameInfo->type);  | 
166  | 0  |     }  | 
167  | 0  |     return UTZGNM_UNKNOWN;  | 
168  | 0  | }  | 
169  |  |  | 
170  |  | int32_t  | 
171  | 0  | TimeZoneGenericNameMatchInfo::getMatchLength(int32_t index) const { | 
172  | 0  |     ZMatchInfo *minfo = (ZMatchInfo *)fMatches->elementAt(index);  | 
173  | 0  |     if (minfo != NULL) { | 
174  | 0  |         return minfo->matchLength;  | 
175  | 0  |     }  | 
176  | 0  |     return -1;  | 
177  | 0  | }  | 
178  |  |  | 
179  |  | UnicodeString&  | 
180  | 0  | TimeZoneGenericNameMatchInfo::getTimeZoneID(int32_t index, UnicodeString& tzID) const { | 
181  | 0  |     GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index);  | 
182  | 0  |     if (minfo != NULL && minfo->gnameInfo->tzID != NULL) { | 
183  | 0  |         tzID.setTo(TRUE, minfo->gnameInfo->tzID, -1);  | 
184  | 0  |     } else { | 
185  | 0  |         tzID.setToBogus();  | 
186  | 0  |     }  | 
187  | 0  |     return tzID;  | 
188  | 0  | }  | 
189  |  |  | 
190  |  | // ---------------------------------------------------  | 
191  |  | // GNameSearchHandler  | 
192  |  | // ---------------------------------------------------  | 
193  |  | class GNameSearchHandler : public TextTrieMapSearchResultHandler { | 
194  |  | public:  | 
195  |  |     GNameSearchHandler(uint32_t types);  | 
196  |  |     virtual ~GNameSearchHandler();  | 
197  |  |  | 
198  |  |     UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);  | 
199  |  |     UVector* getMatches(int32_t& maxMatchLen);  | 
200  |  |  | 
201  |  | private:  | 
202  |  |     uint32_t fTypes;  | 
203  |  |     UVector* fResults;  | 
204  |  |     int32_t fMaxMatchLen;  | 
205  |  | };  | 
206  |  |  | 
207  |  | GNameSearchHandler::GNameSearchHandler(uint32_t types)  | 
208  | 0  | : fTypes(types), fResults(NULL), fMaxMatchLen(0) { | 
209  | 0  | }  | 
210  |  |  | 
211  | 0  | GNameSearchHandler::~GNameSearchHandler() { | 
212  | 0  |     if (fResults != NULL) { | 
213  | 0  |         delete fResults;  | 
214  | 0  |     }  | 
215  | 0  | }  | 
216  |  |  | 
217  |  | UBool  | 
218  | 0  | GNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) { | 
219  | 0  |     if (U_FAILURE(status)) { | 
220  | 0  |         return FALSE;  | 
221  | 0  |     }  | 
222  | 0  |     if (node->hasValues()) { | 
223  | 0  |         int32_t valuesCount = node->countValues();  | 
224  | 0  |         for (int32_t i = 0; i < valuesCount; i++) { | 
225  | 0  |             GNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);  | 
226  | 0  |             if (nameinfo == NULL) { | 
227  | 0  |                 break;  | 
228  | 0  |             }  | 
229  | 0  |             if ((nameinfo->type & fTypes) != 0) { | 
230  |  |                 // matches a requested type  | 
231  | 0  |                 if (fResults == NULL) { | 
232  | 0  |                     fResults = new UVector(uprv_free, NULL, status);  | 
233  | 0  |                     if (fResults == NULL) { | 
234  | 0  |                         status = U_MEMORY_ALLOCATION_ERROR;  | 
235  | 0  |                     }  | 
236  | 0  |                 }  | 
237  | 0  |                 if (U_SUCCESS(status)) { | 
238  | 0  |                     U_ASSERT(fResults != NULL);  | 
239  | 0  |                     GMatchInfo *gmatch = (GMatchInfo *)uprv_malloc(sizeof(GMatchInfo));  | 
240  | 0  |                     if (gmatch == NULL) { | 
241  | 0  |                         status = U_MEMORY_ALLOCATION_ERROR;  | 
242  | 0  |                     } else { | 
243  |  |                         // add the match to the vector  | 
244  | 0  |                         gmatch->gnameInfo = nameinfo;  | 
245  | 0  |                         gmatch->matchLength = matchLength;  | 
246  | 0  |                         gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN;  | 
247  | 0  |                         fResults->addElementX(gmatch, status);  | 
248  | 0  |                         if (U_FAILURE(status)) { | 
249  | 0  |                             uprv_free(gmatch);  | 
250  | 0  |                         } else { | 
251  | 0  |                             if (matchLength > fMaxMatchLen) { | 
252  | 0  |                                 fMaxMatchLen = matchLength;  | 
253  | 0  |                             }  | 
254  | 0  |                         }  | 
255  | 0  |                     }  | 
256  | 0  |                 }  | 
257  | 0  |             }  | 
258  | 0  |         }  | 
259  | 0  |     }  | 
260  | 0  |     return TRUE;  | 
261  | 0  | }  | 
262  |  |  | 
263  |  | UVector*  | 
264  | 0  | GNameSearchHandler::getMatches(int32_t& maxMatchLen) { | 
265  |  |     // give the ownership to the caller  | 
266  | 0  |     UVector *results = fResults;  | 
267  | 0  |     maxMatchLen = fMaxMatchLen;  | 
268  |  |  | 
269  |  |     // reset  | 
270  | 0  |     fResults = NULL;  | 
271  | 0  |     fMaxMatchLen = 0;  | 
272  | 0  |     return results;  | 
273  | 0  | }  | 
274  |  |  | 
275  |  | static UMutex gLock;  | 
276  |  |  | 
277  |  | class TZGNCore : public UMemory { | 
278  |  | public:  | 
279  |  |     TZGNCore(const Locale& locale, UErrorCode& status);  | 
280  |  |     virtual ~TZGNCore();  | 
281  |  |  | 
282  |  |     UnicodeString& getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,  | 
283  |  |                         UDate date, UnicodeString& name) const;  | 
284  |  |  | 
285  |  |     UnicodeString& getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const;  | 
286  |  |  | 
287  |  |     int32_t findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,  | 
288  |  |         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const;  | 
289  |  |  | 
290  |  | private:  | 
291  |  |     Locale fLocale;  | 
292  |  |     const TimeZoneNames* fTimeZoneNames;  | 
293  |  |     UHashtable* fLocationNamesMap;  | 
294  |  |     UHashtable* fPartialLocationNamesMap;  | 
295  |  |  | 
296  |  |     SimpleFormatter fRegionFormat;  | 
297  |  |     SimpleFormatter fFallbackFormat;  | 
298  |  |  | 
299  |  |     LocaleDisplayNames* fLocaleDisplayNames;  | 
300  |  |     ZNStringPool fStringPool;  | 
301  |  |  | 
302  |  |     TextTrieMap fGNamesTrie;  | 
303  |  |     UBool fGNamesTrieFullyLoaded;  | 
304  |  |  | 
305  |  |     char fTargetRegion[ULOC_COUNTRY_CAPACITY];  | 
306  |  |  | 
307  |  |     void initialize(const Locale& locale, UErrorCode& status);  | 
308  |  |     void cleanup();  | 
309  |  |  | 
310  |  |     void loadStrings(const UnicodeString& tzCanonicalID);  | 
311  |  |  | 
312  |  |     const UChar* getGenericLocationName(const UnicodeString& tzCanonicalID);  | 
313  |  |  | 
314  |  |     UnicodeString& formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type,  | 
315  |  |                         UDate date, UnicodeString& name) const;  | 
316  |  |  | 
317  |  |     UnicodeString& getPartialLocationName(const UnicodeString& tzCanonicalID,  | 
318  |  |                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,  | 
319  |  |                         UnicodeString& name) const;  | 
320  |  |  | 
321  |  |     const UChar* getPartialLocationName(const UnicodeString& tzCanonicalID,  | 
322  |  |                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName);  | 
323  |  |  | 
324  |  |     TimeZoneGenericNameMatchInfo* findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;  | 
325  |  |  | 
326  |  |     TimeZoneNames::MatchInfoCollection* findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;  | 
327  |  | };  | 
328  |  |  | 
329  |  |  | 
330  |  | // ---------------------------------------------------  | 
331  |  | // TZGNCore - core implementation of TimeZoneGenericNames  | 
332  |  | //  | 
333  |  | // TimeZoneGenericNames is parallel to TimeZoneNames,  | 
334  |  | // but handles run-time generated time zone names.  | 
335  |  | // This is the main part of this module.  | 
336  |  | // ---------------------------------------------------  | 
337  |  | TZGNCore::TZGNCore(const Locale& locale, UErrorCode& status)  | 
338  | 0  | : fLocale(locale),  | 
339  |  |   fTimeZoneNames(NULL),  | 
340  |  |   fLocationNamesMap(NULL),  | 
341  |  |   fPartialLocationNamesMap(NULL),  | 
342  |  |   fLocaleDisplayNames(NULL),  | 
343  | 0  |   fStringPool(status),  | 
344  | 0  |   fGNamesTrie(TRUE, deleteGNameInfo),  | 
345  | 0  |   fGNamesTrieFullyLoaded(FALSE) { | 
346  | 0  |     initialize(locale, status);  | 
347  | 0  | }  | 
348  |  |  | 
349  | 0  | TZGNCore::~TZGNCore() { | 
350  | 0  |     cleanup();  | 
351  | 0  | }  | 
352  |  |  | 
353  |  | void  | 
354  | 0  | TZGNCore::initialize(const Locale& locale, UErrorCode& status) { | 
355  | 0  |     if (U_FAILURE(status)) { | 
356  | 0  |         return;  | 
357  | 0  |     }  | 
358  |  |  | 
359  |  |     // TimeZoneNames  | 
360  | 0  |     fTimeZoneNames = TimeZoneNames::createInstance(locale, status);  | 
361  | 0  |     if (U_FAILURE(status)) { | 
362  | 0  |         return;  | 
363  | 0  |     }  | 
364  |  |  | 
365  |  |     // Initialize format patterns  | 
366  | 0  |     UnicodeString rpat(TRUE, gDefRegionPattern, -1);  | 
367  | 0  |     UnicodeString fpat(TRUE, gDefFallbackPattern, -1);  | 
368  |  | 
  | 
369  | 0  |     UErrorCode tmpsts = U_ZERO_ERROR;   // OK with fallback warning..  | 
370  | 0  |     UResourceBundle *zoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);  | 
371  | 0  |     zoneStrings = ures_getByKeyWithFallback(zoneStrings, gZoneStrings, zoneStrings, &tmpsts);  | 
372  |  | 
  | 
373  | 0  |     if (U_SUCCESS(tmpsts)) { | 
374  | 0  |         const UChar *regionPattern = ures_getStringByKeyWithFallback(zoneStrings, gRegionFormatTag, NULL, &tmpsts);  | 
375  | 0  |         if (U_SUCCESS(tmpsts) && u_strlen(regionPattern) > 0) { | 
376  | 0  |             rpat.setTo(regionPattern, -1);  | 
377  | 0  |         }  | 
378  | 0  |         tmpsts = U_ZERO_ERROR;  | 
379  | 0  |         const UChar *fallbackPattern = ures_getStringByKeyWithFallback(zoneStrings, gFallbackFormatTag, NULL, &tmpsts);  | 
380  | 0  |         if (U_SUCCESS(tmpsts) && u_strlen(fallbackPattern) > 0) { | 
381  | 0  |             fpat.setTo(fallbackPattern, -1);  | 
382  | 0  |         }  | 
383  | 0  |     }  | 
384  | 0  |     ures_close(zoneStrings);  | 
385  |  | 
  | 
386  | 0  |     fRegionFormat.applyPatternMinMaxArguments(rpat, 1, 1, status);  | 
387  | 0  |     fFallbackFormat.applyPatternMinMaxArguments(fpat, 2, 2, status);  | 
388  | 0  |     if (U_FAILURE(status)) { | 
389  | 0  |         cleanup();  | 
390  | 0  |         return;  | 
391  | 0  |     }  | 
392  |  |  | 
393  |  |     // locale display names  | 
394  | 0  |     fLocaleDisplayNames = LocaleDisplayNames::createInstance(locale);  | 
395  |  |  | 
396  |  |     // hash table for names - no key/value deleters  | 
397  | 0  |     fLocationNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);  | 
398  | 0  |     if (U_FAILURE(status)) { | 
399  | 0  |         cleanup();  | 
400  | 0  |         return;  | 
401  | 0  |     }  | 
402  |  |  | 
403  | 0  |     fPartialLocationNamesMap = uhash_open(hashPartialLocationKey, comparePartialLocationKey, NULL, &status);  | 
404  | 0  |     if (U_FAILURE(status)) { | 
405  | 0  |         cleanup();  | 
406  | 0  |         return;  | 
407  | 0  |     }  | 
408  | 0  |     uhash_setKeyDeleter(fPartialLocationNamesMap, uprv_free);  | 
409  |  |     // no value deleter  | 
410  |  |  | 
411  |  |     // target region  | 
412  | 0  |     const char* region = fLocale.getCountry();  | 
413  | 0  |     int32_t regionLen = static_cast<int32_t>(uprv_strlen(region));  | 
414  | 0  |     if (regionLen == 0) { | 
415  | 0  |         CharString loc;  | 
416  | 0  |         { | 
417  | 0  |             CharStringByteSink sink(&loc);  | 
418  | 0  |             ulocimp_addLikelySubtags(fLocale.getName(), sink, &status);  | 
419  | 0  |         }  | 
420  |  | 
  | 
421  | 0  |         regionLen = uloc_getCountry(loc.data(), fTargetRegion, sizeof(fTargetRegion), &status);  | 
422  | 0  |         if (U_SUCCESS(status)) { | 
423  | 0  |             fTargetRegion[regionLen] = 0;  | 
424  | 0  |         } else { | 
425  | 0  |             cleanup();  | 
426  | 0  |             return;  | 
427  | 0  |         }  | 
428  | 0  |     } else if (regionLen < (int32_t)sizeof(fTargetRegion)) { | 
429  | 0  |         uprv_strcpy(fTargetRegion, region);  | 
430  | 0  |     } else { | 
431  | 0  |         fTargetRegion[0] = 0;  | 
432  | 0  |     }  | 
433  |  |  | 
434  |  |     // preload generic names for the default zone  | 
435  | 0  |     TimeZone *tz = TimeZone::createDefault();  | 
436  | 0  |     const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz);  | 
437  | 0  |     if (tzID != NULL) { | 
438  | 0  |         loadStrings(UnicodeString(TRUE, tzID, -1));  | 
439  | 0  |     }  | 
440  | 0  |     delete tz;  | 
441  | 0  | }  | 
442  |  |  | 
443  |  | void  | 
444  | 0  | TZGNCore::cleanup() { | 
445  | 0  |     if (fLocaleDisplayNames != NULL) { | 
446  | 0  |         delete fLocaleDisplayNames;  | 
447  | 0  |     }  | 
448  | 0  |     if (fTimeZoneNames != NULL) { | 
449  | 0  |         delete fTimeZoneNames;  | 
450  | 0  |     }  | 
451  |  | 
  | 
452  | 0  |     uhash_close(fLocationNamesMap);  | 
453  | 0  |     uhash_close(fPartialLocationNamesMap);  | 
454  | 0  | }  | 
455  |  |  | 
456  |  |  | 
457  |  | UnicodeString&  | 
458  | 0  | TZGNCore::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const { | 
459  | 0  |     name.setToBogus();  | 
460  | 0  |     switch (type) { | 
461  | 0  |     case UTZGNM_LOCATION:  | 
462  | 0  |         { | 
463  | 0  |             const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);  | 
464  | 0  |             if (tzCanonicalID != NULL) { | 
465  | 0  |                 getGenericLocationName(UnicodeString(TRUE, tzCanonicalID, -1), name);  | 
466  | 0  |             }  | 
467  | 0  |         }  | 
468  | 0  |         break;  | 
469  | 0  |     case UTZGNM_LONG:  | 
470  | 0  |     case UTZGNM_SHORT:  | 
471  | 0  |         formatGenericNonLocationName(tz, type, date, name);  | 
472  | 0  |         if (name.isEmpty()) { | 
473  | 0  |             const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);  | 
474  | 0  |             if (tzCanonicalID != NULL) { | 
475  | 0  |                 getGenericLocationName(UnicodeString(TRUE, tzCanonicalID, -1), name);  | 
476  | 0  |             }  | 
477  | 0  |         }  | 
478  | 0  |         break;  | 
479  | 0  |     default:  | 
480  | 0  |         break;  | 
481  | 0  |     }  | 
482  | 0  |     return name;  | 
483  | 0  | }  | 
484  |  |  | 
485  |  | UnicodeString&  | 
486  | 0  | TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const { | 
487  | 0  |     if (tzCanonicalID.isEmpty()) { | 
488  | 0  |         name.setToBogus();  | 
489  | 0  |         return name;  | 
490  | 0  |     }  | 
491  |  |  | 
492  | 0  |     const UChar *locname = NULL;  | 
493  | 0  |     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);  | 
494  | 0  |     umtx_lock(&gLock);  | 
495  | 0  |     { | 
496  | 0  |         locname = nonConstThis->getGenericLocationName(tzCanonicalID);  | 
497  | 0  |     }  | 
498  | 0  |     umtx_unlock(&gLock);  | 
499  |  | 
  | 
500  | 0  |     if (locname == NULL) { | 
501  | 0  |         name.setToBogus();  | 
502  | 0  |     } else { | 
503  | 0  |         name.setTo(locname, u_strlen(locname));  | 
504  | 0  |     }  | 
505  |  | 
  | 
506  | 0  |     return name;  | 
507  | 0  | }  | 
508  |  |  | 
509  |  | /*  | 
510  |  |  * This method updates the cache and must be called with a lock  | 
511  |  |  */  | 
512  |  | const UChar*  | 
513  | 0  | TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID) { | 
514  | 0  |     U_ASSERT(!tzCanonicalID.isEmpty());  | 
515  | 0  |     if (tzCanonicalID.length() > ZID_KEY_MAX) { | 
516  | 0  |         return NULL;  | 
517  | 0  |     }  | 
518  |  |  | 
519  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
520  | 0  |     UChar tzIDKey[ZID_KEY_MAX + 1];  | 
521  | 0  |     int32_t tzIDKeyLen = tzCanonicalID.extract(tzIDKey, ZID_KEY_MAX + 1, status);  | 
522  | 0  |     U_ASSERT(status == U_ZERO_ERROR);   // already checked length above  | 
523  | 0  |     tzIDKey[tzIDKeyLen] = 0;  | 
524  |  | 
  | 
525  | 0  |     const UChar *locname = (const UChar *)uhash_get(fLocationNamesMap, tzIDKey);  | 
526  |  | 
  | 
527  | 0  |     if (locname != NULL) { | 
528  |  |         // gEmpty indicate the name is not available  | 
529  | 0  |         if (locname == gEmpty) { | 
530  | 0  |             return NULL;  | 
531  | 0  |         }  | 
532  | 0  |         return locname;  | 
533  | 0  |     }  | 
534  |  |  | 
535  |  |     // Construct location name  | 
536  | 0  |     UnicodeString name;  | 
537  | 0  |     UnicodeString usCountryCode;  | 
538  | 0  |     UBool isPrimary = FALSE;  | 
539  |  | 
  | 
540  | 0  |     ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode, &isPrimary);  | 
541  |  | 
  | 
542  | 0  |     if (!usCountryCode.isEmpty()) { | 
543  | 0  |         if (isPrimary) { | 
544  |  |             // If this is the primary zone in the country, use the country name.  | 
545  | 0  |             char countryCode[ULOC_COUNTRY_CAPACITY];  | 
546  | 0  |             U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);  | 
547  | 0  |             int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);  | 
548  | 0  |             countryCode[ccLen] = 0;  | 
549  |  | 
  | 
550  | 0  |             UnicodeString country;  | 
551  | 0  |             fLocaleDisplayNames->regionDisplayName(countryCode, country);  | 
552  | 0  |             fRegionFormat.format(country, name, status);  | 
553  | 0  |         } else { | 
554  |  |             // If this is not the primary zone in the country,  | 
555  |  |             // use the exemplar city name.  | 
556  |  |  | 
557  |  |             // getExemplarLocationName should return non-empty string  | 
558  |  |             // if the time zone is associated with a region  | 
559  |  | 
  | 
560  | 0  |             UnicodeString city;  | 
561  | 0  |             fTimeZoneNames->getExemplarLocationName(tzCanonicalID, city);  | 
562  | 0  |             fRegionFormat.format(city, name, status);  | 
563  | 0  |         }  | 
564  | 0  |         if (U_FAILURE(status)) { | 
565  | 0  |             return NULL;  | 
566  | 0  |         }  | 
567  | 0  |     }  | 
568  |  |  | 
569  | 0  |     locname = name.isEmpty() ? NULL : fStringPool.get(name, status);  | 
570  | 0  |     if (U_SUCCESS(status)) { | 
571  |  |         // Cache the result  | 
572  | 0  |         const UChar* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID);  | 
573  | 0  |         U_ASSERT(cacheID != NULL);  | 
574  | 0  |         if (locname == NULL) { | 
575  |  |             // gEmpty to indicate - no location name available  | 
576  | 0  |             uhash_put(fLocationNamesMap, (void *)cacheID, (void *)gEmpty, &status);  | 
577  | 0  |         } else { | 
578  | 0  |             uhash_put(fLocationNamesMap, (void *)cacheID, (void *)locname, &status);  | 
579  | 0  |             if (U_FAILURE(status)) { | 
580  | 0  |                 locname = NULL;  | 
581  | 0  |             } else { | 
582  |  |                 // put the name info into the trie  | 
583  | 0  |                 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));  | 
584  | 0  |                 if (nameinfo != NULL) { | 
585  | 0  |                     nameinfo->type = UTZGNM_LOCATION;  | 
586  | 0  |                     nameinfo->tzID = cacheID;  | 
587  | 0  |                     fGNamesTrie.put(locname, nameinfo, status);  | 
588  | 0  |                 }  | 
589  | 0  |             }  | 
590  | 0  |         }  | 
591  | 0  |     }  | 
592  |  | 
  | 
593  | 0  |     return locname;  | 
594  | 0  | }  | 
595  |  |  | 
596  |  | UnicodeString&  | 
597  | 0  | TZGNCore::formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const { | 
598  | 0  |     U_ASSERT(type == UTZGNM_LONG || type == UTZGNM_SHORT);  | 
599  | 0  |     name.setToBogus();  | 
600  |  | 
  | 
601  | 0  |     const UChar* uID = ZoneMeta::getCanonicalCLDRID(tz);  | 
602  | 0  |     if (uID == NULL) { | 
603  | 0  |         return name;  | 
604  | 0  |     }  | 
605  |  |  | 
606  | 0  |     UnicodeString tzID(TRUE, uID, -1);  | 
607  |  |  | 
608  |  |     // Try to get a name from time zone first  | 
609  | 0  |     UTimeZoneNameType nameType = (type == UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC;  | 
610  | 0  |     fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name);  | 
611  |  | 
  | 
612  | 0  |     if (!name.isEmpty()) { | 
613  | 0  |         return name;  | 
614  | 0  |     }  | 
615  |  |  | 
616  |  |     // Try meta zone  | 
617  | 0  |     UChar mzIDBuf[32];  | 
618  | 0  |     UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));  | 
619  | 0  |     fTimeZoneNames->getMetaZoneID(tzID, date, mzID);  | 
620  | 0  |     if (!mzID.isEmpty()) { | 
621  | 0  |         UErrorCode status = U_ZERO_ERROR;  | 
622  | 0  |         UBool useStandard = FALSE;  | 
623  | 0  |         int32_t raw, sav;  | 
624  | 0  |         UChar tmpNameBuf[ZONE_NAME_U16_MAX];  | 
625  |  | 
  | 
626  | 0  |         tz.getOffset(date, FALSE, raw, sav, status);  | 
627  | 0  |         if (U_FAILURE(status)) { | 
628  | 0  |             return name;  | 
629  | 0  |         }  | 
630  |  |  | 
631  | 0  |         if (sav == 0) { | 
632  | 0  |             useStandard = TRUE;  | 
633  |  | 
  | 
634  | 0  |             TimeZone *tmptz = tz.clone();  | 
635  |  |             // Check if the zone actually uses daylight saving time around the time  | 
636  | 0  |             BasicTimeZone *btz = NULL;  | 
637  | 0  |             if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL  | 
638  | 0  |                 || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL  | 
639  | 0  |                 || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL  | 
640  | 0  |                 || dynamic_cast<VTimeZone *>(tmptz) != NULL) { | 
641  | 0  |                 btz = (BasicTimeZone*)tmptz;  | 
642  | 0  |             }  | 
643  |  | 
  | 
644  | 0  |             if (btz != NULL) { | 
645  | 0  |                 TimeZoneTransition before;  | 
646  | 0  |                 UBool beforTrs = btz->getPreviousTransition(date, TRUE, before);  | 
647  | 0  |                 if (beforTrs  | 
648  | 0  |                         && (date - before.getTime() < kDstCheckRange)  | 
649  | 0  |                         && before.getFrom()->getDSTSavings() != 0) { | 
650  | 0  |                     useStandard = FALSE;  | 
651  | 0  |                 } else { | 
652  | 0  |                     TimeZoneTransition after;  | 
653  | 0  |                     UBool afterTrs = btz->getNextTransition(date, FALSE, after);  | 
654  | 0  |                     if (afterTrs  | 
655  | 0  |                             && (after.getTime() - date < kDstCheckRange)  | 
656  | 0  |                             && after.getTo()->getDSTSavings() != 0) { | 
657  | 0  |                         useStandard = FALSE;  | 
658  | 0  |                     }  | 
659  | 0  |                 }  | 
660  | 0  |             } else { | 
661  |  |                 // If not BasicTimeZone... only if the instance is not an ICU's implementation.  | 
662  |  |                 // We may get a wrong answer in edge case, but it should practically work OK.  | 
663  | 0  |                 tmptz->getOffset(date - kDstCheckRange, FALSE, raw, sav, status);  | 
664  | 0  |                 if (sav != 0) { | 
665  | 0  |                     useStandard = FALSE;  | 
666  | 0  |                 } else { | 
667  | 0  |                     tmptz->getOffset(date + kDstCheckRange, FALSE, raw, sav, status);  | 
668  | 0  |                     if (sav != 0){ | 
669  | 0  |                         useStandard = FALSE;  | 
670  | 0  |                     }  | 
671  | 0  |                 }  | 
672  | 0  |                 if (U_FAILURE(status)) { | 
673  | 0  |                     delete tmptz;  | 
674  | 0  |                     return name;  | 
675  | 0  |                 }  | 
676  | 0  |             }  | 
677  | 0  |             delete tmptz;  | 
678  | 0  |         }  | 
679  | 0  |         if (useStandard) { | 
680  | 0  |             UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC)  | 
681  | 0  |                 ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD;  | 
682  | 0  |             UnicodeString stdName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));  | 
683  | 0  |             fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName);  | 
684  | 0  |             if (!stdName.isEmpty()) { | 
685  | 0  |                 name.setTo(stdName);  | 
686  |  |  | 
687  |  |                 // TODO: revisit this issue later  | 
688  |  |                 // In CLDR, a same display name is used for both generic and standard  | 
689  |  |                 // for some meta zones in some locales.  This looks like a data bugs.  | 
690  |  |                 // For now, we check if the standard name is different from its generic  | 
691  |  |                 // name below.  | 
692  | 0  |                 UChar genNameBuf[ZONE_NAME_U16_MAX];  | 
693  | 0  |                 UnicodeString mzGenericName(genNameBuf, 0, UPRV_LENGTHOF(genNameBuf));  | 
694  | 0  |                 fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName);  | 
695  | 0  |                 if (stdName.caseCompare(mzGenericName, 0) == 0) { | 
696  | 0  |                     name.setToBogus();  | 
697  | 0  |                 }  | 
698  | 0  |             }  | 
699  | 0  |         }  | 
700  | 0  |         if (name.isEmpty()) { | 
701  |  |             // Get a name from meta zone  | 
702  | 0  |             UnicodeString mzName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));  | 
703  | 0  |             fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName);  | 
704  | 0  |             if (!mzName.isEmpty()) { | 
705  |  |                 // Check if we need to use a partial location format.  | 
706  |  |                 // This check is done by comparing offset with the meta zone's  | 
707  |  |                 // golden zone at the given date.  | 
708  | 0  |                 UChar idBuf[32];  | 
709  | 0  |                 UnicodeString goldenID(idBuf, 0, UPRV_LENGTHOF(idBuf));  | 
710  | 0  |                 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, goldenID);  | 
711  | 0  |                 if (!goldenID.isEmpty() && goldenID != tzID) { | 
712  | 0  |                     TimeZone *goldenZone = TimeZone::createTimeZone(goldenID);  | 
713  | 0  |                     int32_t raw1, sav1;  | 
714  |  |  | 
715  |  |                     // Check offset in the golden zone with wall time.  | 
716  |  |                     // With getOffset(date, false, offsets1),  | 
717  |  |                     // you may get incorrect results because of time overlap at DST->STD  | 
718  |  |                     // transition.  | 
719  | 0  |                     goldenZone->getOffset(date + raw + sav, TRUE, raw1, sav1, status);  | 
720  | 0  |                     delete goldenZone;  | 
721  | 0  |                     if (U_SUCCESS(status)) { | 
722  | 0  |                         if (raw != raw1 || sav != sav1) { | 
723  |  |                             // Now we need to use a partial location format  | 
724  | 0  |                             getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name);  | 
725  | 0  |                         } else { | 
726  | 0  |                             name.setTo(mzName);  | 
727  | 0  |                         }  | 
728  | 0  |                     }  | 
729  | 0  |                 } else { | 
730  | 0  |                     name.setTo(mzName);  | 
731  | 0  |                 }  | 
732  | 0  |             }  | 
733  | 0  |         }  | 
734  | 0  |     }  | 
735  | 0  |     return name;  | 
736  | 0  | }  | 
737  |  |  | 
738  |  | UnicodeString&  | 
739  |  | TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,  | 
740  |  |                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,  | 
741  | 0  |                         UnicodeString& name) const { | 
742  | 0  |     name.setToBogus();  | 
743  | 0  |     if (tzCanonicalID.isEmpty() || mzID.isEmpty() || mzDisplayName.isEmpty()) { | 
744  | 0  |         return name;  | 
745  | 0  |     }  | 
746  |  |  | 
747  | 0  |     const UChar *uplname = NULL;  | 
748  | 0  |     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);  | 
749  | 0  |     umtx_lock(&gLock);  | 
750  | 0  |     { | 
751  | 0  |         uplname = nonConstThis->getPartialLocationName(tzCanonicalID, mzID, isLong, mzDisplayName);  | 
752  | 0  |     }  | 
753  | 0  |     umtx_unlock(&gLock);  | 
754  |  | 
  | 
755  | 0  |     if (uplname == NULL) { | 
756  | 0  |         name.setToBogus();  | 
757  | 0  |     } else { | 
758  | 0  |         name.setTo(TRUE, uplname, -1);  | 
759  | 0  |     }  | 
760  | 0  |     return name;  | 
761  | 0  | }  | 
762  |  |  | 
763  |  | /*  | 
764  |  |  * This method updates the cache and must be called with a lock  | 
765  |  |  */  | 
766  |  | const UChar*  | 
767  |  | TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,  | 
768  | 0  |                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) { | 
769  | 0  |     U_ASSERT(!tzCanonicalID.isEmpty());  | 
770  | 0  |     U_ASSERT(!mzID.isEmpty());  | 
771  | 0  |     U_ASSERT(!mzDisplayName.isEmpty());  | 
772  |  | 
  | 
773  | 0  |     PartialLocationKey key;  | 
774  | 0  |     key.tzID = ZoneMeta::findTimeZoneID(tzCanonicalID);  | 
775  | 0  |     key.mzID = ZoneMeta::findMetaZoneID(mzID);  | 
776  | 0  |     key.isLong = isLong;  | 
777  | 0  |     U_ASSERT(key.tzID != NULL && key.mzID != NULL);  | 
778  |  | 
  | 
779  | 0  |     const UChar* uplname = (const UChar*)uhash_get(fPartialLocationNamesMap, (void *)&key);  | 
780  | 0  |     if (uplname != NULL) { | 
781  | 0  |         return uplname;  | 
782  | 0  |     }  | 
783  |  |  | 
784  | 0  |     UnicodeString location;  | 
785  | 0  |     UnicodeString usCountryCode;  | 
786  | 0  |     ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode);  | 
787  | 0  |     if (!usCountryCode.isEmpty()) { | 
788  | 0  |         char countryCode[ULOC_COUNTRY_CAPACITY];  | 
789  | 0  |         U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);  | 
790  | 0  |         int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);  | 
791  | 0  |         countryCode[ccLen] = 0;  | 
792  |  | 
  | 
793  | 0  |         UnicodeString regionalGolden;  | 
794  | 0  |         fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden);  | 
795  | 0  |         if (tzCanonicalID == regionalGolden) { | 
796  |  |             // Use country name  | 
797  | 0  |             fLocaleDisplayNames->regionDisplayName(countryCode, location);  | 
798  | 0  |         } else { | 
799  |  |             // Otherwise, use exemplar city name  | 
800  | 0  |             fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);  | 
801  | 0  |         }  | 
802  | 0  |     } else { | 
803  | 0  |         fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);  | 
804  | 0  |         if (location.isEmpty()) { | 
805  |  |             // This could happen when the time zone is not associated with a country,  | 
806  |  |             // and its ID is not hierarchical, for example, CST6CDT.  | 
807  |  |             // We use the canonical ID itself as the location for this case.  | 
808  | 0  |             location.setTo(tzCanonicalID);  | 
809  | 0  |         }  | 
810  | 0  |     }  | 
811  |  | 
  | 
812  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
813  | 0  |     UnicodeString name;  | 
814  | 0  |     fFallbackFormat.format(location, mzDisplayName, name, status);  | 
815  | 0  |     if (U_FAILURE(status)) { | 
816  | 0  |         return NULL;  | 
817  | 0  |     }  | 
818  |  |  | 
819  | 0  |     uplname = fStringPool.get(name, status);  | 
820  | 0  |     if (U_SUCCESS(status)) { | 
821  |  |         // Add the name to cache  | 
822  | 0  |         PartialLocationKey* cacheKey = (PartialLocationKey *)uprv_malloc(sizeof(PartialLocationKey));  | 
823  | 0  |         if (cacheKey != NULL) { | 
824  | 0  |             cacheKey->tzID = key.tzID;  | 
825  | 0  |             cacheKey->mzID = key.mzID;  | 
826  | 0  |             cacheKey->isLong = key.isLong;  | 
827  | 0  |             uhash_put(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status);  | 
828  | 0  |             if (U_FAILURE(status)) { | 
829  | 0  |                 uprv_free(cacheKey);  | 
830  | 0  |             } else { | 
831  |  |                 // put the name to the local trie as well  | 
832  | 0  |                 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));  | 
833  | 0  |                 if (nameinfo != NULL) { | 
834  | 0  |                     nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT;  | 
835  | 0  |                     nameinfo->tzID = key.tzID;  | 
836  | 0  |                     fGNamesTrie.put(uplname, nameinfo, status);  | 
837  | 0  |                 }  | 
838  | 0  |             }  | 
839  | 0  |         }  | 
840  | 0  |     }  | 
841  | 0  |     return uplname;  | 
842  | 0  | }  | 
843  |  |  | 
844  |  | /*  | 
845  |  |  * This method updates the cache and must be called with a lock,  | 
846  |  |  * except initializer.  | 
847  |  |  */  | 
848  |  | void  | 
849  | 0  | TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) { | 
850  |  |     // load the generic location name  | 
851  | 0  |     getGenericLocationName(tzCanonicalID);  | 
852  |  |  | 
853  |  |     // partial location names  | 
854  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
855  |  | 
  | 
856  | 0  |     const UnicodeString *mzID;  | 
857  | 0  |     UnicodeString goldenID;  | 
858  | 0  |     UnicodeString mzGenName;  | 
859  | 0  |     UTimeZoneNameType genNonLocTypes[] = { | 
860  | 0  |         UTZNM_LONG_GENERIC, UTZNM_SHORT_GENERIC,  | 
861  | 0  |         UTZNM_UNKNOWN /*terminator*/  | 
862  | 0  |     };  | 
863  |  | 
  | 
864  | 0  |     StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status);  | 
865  | 0  |     while ((mzID = mzIDs->snext(status)) != NULL) { | 
866  | 0  |         if (U_FAILURE(status)) { | 
867  | 0  |             break;  | 
868  | 0  |         }  | 
869  |  |         // if this time zone is not the golden zone of the meta zone,  | 
870  |  |         // partial location name (such as "PT (Los Angeles)") might be  | 
871  |  |         // available.  | 
872  | 0  |         fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion, goldenID);  | 
873  | 0  |         if (tzCanonicalID != goldenID) { | 
874  | 0  |             for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) { | 
875  | 0  |                 fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName);  | 
876  | 0  |                 if (!mzGenName.isEmpty()) { | 
877  |  |                     // getPartialLocationName formats a name and put it into the trie  | 
878  | 0  |                     getPartialLocationName(tzCanonicalID, *mzID,  | 
879  | 0  |                         (genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName);  | 
880  | 0  |                 }  | 
881  | 0  |             }  | 
882  | 0  |         }  | 
883  | 0  |     }  | 
884  | 0  |     if (mzIDs != NULL) { | 
885  | 0  |         delete mzIDs;  | 
886  | 0  |     }  | 
887  | 0  | }  | 
888  |  |  | 
889  |  | int32_t  | 
890  |  | TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,  | 
891  | 0  |         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const { | 
892  | 0  |     timeType = UTZFMT_TIME_TYPE_UNKNOWN;  | 
893  | 0  |     tzID.setToBogus();  | 
894  |  | 
  | 
895  | 0  |     if (U_FAILURE(status)) { | 
896  | 0  |         return 0;  | 
897  | 0  |     }  | 
898  |  |  | 
899  |  |     // Find matches in the TimeZoneNames first  | 
900  | 0  |     TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status);  | 
901  | 0  |     if (U_FAILURE(status)) { | 
902  | 0  |         return 0;  | 
903  | 0  |     }  | 
904  |  |  | 
905  | 0  |     int32_t bestMatchLen = 0;  | 
906  | 0  |     UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;  | 
907  | 0  |     UnicodeString bestMatchTzID;  | 
908  |  |     // UBool isLongStandard = FALSE;   // workaround - see the comments below  | 
909  | 0  |     UBool isStandard = FALSE;       // TODO: Temporary hack (on hack) for short standard name/location name conflict (found in zh_Hant), should be removed after CLDR 21m1 integration  | 
910  |  | 
  | 
911  | 0  |     if (tznamesMatches != NULL) { | 
912  | 0  |         UnicodeString mzID;  | 
913  | 0  |         for (int32_t i = 0; i < tznamesMatches->size(); i++) { | 
914  | 0  |             int32_t len = tznamesMatches->getMatchLengthAt(i);  | 
915  | 0  |             if (len > bestMatchLen) { | 
916  | 0  |                 bestMatchLen = len;  | 
917  | 0  |                 if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) { | 
918  |  |                     // name for a meta zone  | 
919  | 0  |                     if (tznamesMatches->getMetaZoneIDAt(i, mzID)) { | 
920  | 0  |                         fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, bestMatchTzID);  | 
921  | 0  |                     }  | 
922  | 0  |                 }  | 
923  | 0  |                 UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i);  | 
924  | 0  |                 if (U_FAILURE(status)) { | 
925  | 0  |                     break;  | 
926  | 0  |                 }  | 
927  | 0  |                 switch (nameType) { | 
928  | 0  |                 case UTZNM_LONG_STANDARD:  | 
929  |  |                     // isLongStandard = TRUE;  | 
930  | 0  |                 case UTZNM_SHORT_STANDARD:  // this one is never used for generic, but just in case  | 
931  | 0  |                     isStandard = TRUE;      // TODO: Remove this later, see the comments above.  | 
932  | 0  |                     bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD;  | 
933  | 0  |                     break;  | 
934  | 0  |                 case UTZNM_LONG_DAYLIGHT:  | 
935  | 0  |                 case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case  | 
936  | 0  |                     bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT;  | 
937  | 0  |                     break;  | 
938  | 0  |                 default:  | 
939  | 0  |                     bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;  | 
940  | 0  |                 }  | 
941  | 0  |             }  | 
942  | 0  |         }  | 
943  | 0  |         delete tznamesMatches;  | 
944  | 0  |         if (U_FAILURE(status)) { | 
945  | 0  |             return 0;  | 
946  | 0  |         }  | 
947  |  |  | 
948  | 0  |         if (bestMatchLen == (text.length() - start)) { | 
949  |  |             // Full match  | 
950  |  |  | 
951  |  |             //tzID.setTo(bestMatchTzID);  | 
952  |  |             //timeType = bestMatchTimeType;  | 
953  |  |             //return bestMatchLen;  | 
954  |  |  | 
955  |  |             // TODO Some time zone uses a same name for the long standard name  | 
956  |  |             // and the location name. When the match is a long standard name,  | 
957  |  |             // then we need to check if the name is same with the location name.  | 
958  |  |             // This is probably a data error or a design bug.  | 
959  |  | /*  | 
960  |  |             if (!isLongStandard) { | 
961  |  |                 tzID.setTo(bestMatchTzID);  | 
962  |  |                 timeType = bestMatchTimeType;  | 
963  |  |                 return bestMatchLen;  | 
964  |  |             }  | 
965  |  | */  | 
966  |  |             // TODO The deprecation of commonlyUsed flag introduced the name  | 
967  |  |             // conflict not only for long standard names, but short standard names too.  | 
968  |  |             // These short names (found in zh_Hant) should be gone once we clean  | 
969  |  |             // up CLDR time zone display name data. Once the short name conflict  | 
970  |  |             // problem (with location name) is resolved, we should change the condition  | 
971  |  |             // below back to the original one above. -Yoshito (2011-09-14)  | 
972  | 0  |             if (!isStandard) { | 
973  | 0  |                 tzID.setTo(bestMatchTzID);  | 
974  | 0  |                 timeType = bestMatchTimeType;  | 
975  | 0  |                 return bestMatchLen;  | 
976  | 0  |             }  | 
977  | 0  |         }  | 
978  | 0  |     }  | 
979  |  |  | 
980  |  |     // Find matches in the local trie  | 
981  | 0  |     TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status);  | 
982  | 0  |     if (U_FAILURE(status)) { | 
983  | 0  |         return 0;  | 
984  | 0  |     }  | 
985  | 0  |     if (localMatches != NULL) { | 
986  | 0  |         for (int32_t i = 0; i < localMatches->size(); i++) { | 
987  | 0  |             int32_t len = localMatches->getMatchLength(i);  | 
988  |  |  | 
989  |  |             // TODO See the above TODO. We use len >= bestMatchLen  | 
990  |  |             // because of the long standard/location name collision  | 
991  |  |             // problem. If it is also a location name, carrying  | 
992  |  |             // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a  | 
993  |  |             // problem in SimpleDateFormat  | 
994  | 0  |             if (len >= bestMatchLen) { | 
995  | 0  |                 bestMatchLen = localMatches->getMatchLength(i);  | 
996  | 0  |                 bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;   // because generic  | 
997  | 0  |                 localMatches->getTimeZoneID(i, bestMatchTzID);  | 
998  | 0  |             }  | 
999  | 0  |         }  | 
1000  | 0  |         delete localMatches;  | 
1001  | 0  |     }  | 
1002  |  | 
  | 
1003  | 0  |     if (bestMatchLen > 0) { | 
1004  | 0  |         timeType = bestMatchTimeType;  | 
1005  | 0  |         tzID.setTo(bestMatchTzID);  | 
1006  | 0  |     }  | 
1007  | 0  |     return bestMatchLen;  | 
1008  | 0  | }  | 
1009  |  |  | 
1010  |  | TimeZoneGenericNameMatchInfo*  | 
1011  | 0  | TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { | 
1012  | 0  |     GNameSearchHandler handler(types);  | 
1013  |  | 
  | 
1014  | 0  |     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);  | 
1015  |  | 
  | 
1016  | 0  |     umtx_lock(&gLock);  | 
1017  | 0  |     { | 
1018  | 0  |         fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);  | 
1019  | 0  |     }  | 
1020  | 0  |     umtx_unlock(&gLock);  | 
1021  |  | 
  | 
1022  | 0  |     if (U_FAILURE(status)) { | 
1023  | 0  |         return NULL;  | 
1024  | 0  |     }  | 
1025  |  |  | 
1026  | 0  |     TimeZoneGenericNameMatchInfo *gmatchInfo = NULL;  | 
1027  |  | 
  | 
1028  | 0  |     int32_t maxLen = 0;  | 
1029  | 0  |     UVector *results = handler.getMatches(maxLen);  | 
1030  | 0  |     if (results != NULL && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) { | 
1031  |  |         // perfect match  | 
1032  | 0  |         gmatchInfo = new TimeZoneGenericNameMatchInfo(results);  | 
1033  | 0  |         if (gmatchInfo == NULL) { | 
1034  | 0  |             status = U_MEMORY_ALLOCATION_ERROR;  | 
1035  | 0  |             delete results;  | 
1036  | 0  |             return NULL;  | 
1037  | 0  |         }  | 
1038  | 0  |         return gmatchInfo;  | 
1039  | 0  |     }  | 
1040  |  |  | 
1041  | 0  |     if (results != NULL) { | 
1042  | 0  |         delete results;  | 
1043  | 0  |     }  | 
1044  |  |  | 
1045  |  |     // All names are not yet loaded into the local trie.  | 
1046  |  |     // Load all available names into the trie. This could be very heavy.  | 
1047  | 0  |     umtx_lock(&gLock);  | 
1048  | 0  |     { | 
1049  | 0  |         if (!fGNamesTrieFullyLoaded) { | 
1050  | 0  |             StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);  | 
1051  | 0  |             if (U_SUCCESS(status)) { | 
1052  | 0  |                 const UnicodeString *tzID;  | 
1053  | 0  |                 while ((tzID = tzIDs->snext(status)) != NULL) { | 
1054  | 0  |                     if (U_FAILURE(status)) { | 
1055  | 0  |                         break;  | 
1056  | 0  |                     }  | 
1057  | 0  |                     nonConstThis->loadStrings(*tzID);  | 
1058  | 0  |                 }  | 
1059  | 0  |             }  | 
1060  | 0  |             if (tzIDs != NULL) { | 
1061  | 0  |                 delete tzIDs;  | 
1062  | 0  |             }  | 
1063  |  | 
  | 
1064  | 0  |             if (U_SUCCESS(status)) { | 
1065  | 0  |                 nonConstThis->fGNamesTrieFullyLoaded = TRUE;  | 
1066  | 0  |             }  | 
1067  | 0  |         }  | 
1068  | 0  |     }  | 
1069  | 0  |     umtx_unlock(&gLock);  | 
1070  |  | 
  | 
1071  | 0  |     if (U_FAILURE(status)) { | 
1072  | 0  |         return NULL;  | 
1073  | 0  |     }  | 
1074  |  |  | 
1075  | 0  |     umtx_lock(&gLock);  | 
1076  | 0  |     { | 
1077  |  |         // now try it again  | 
1078  | 0  |         fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);  | 
1079  | 0  |     }  | 
1080  | 0  |     umtx_unlock(&gLock);  | 
1081  |  | 
  | 
1082  | 0  |     results = handler.getMatches(maxLen);  | 
1083  | 0  |     if (results != NULL && maxLen > 0) { | 
1084  | 0  |         gmatchInfo = new TimeZoneGenericNameMatchInfo(results);  | 
1085  | 0  |         if (gmatchInfo == NULL) { | 
1086  | 0  |             status = U_MEMORY_ALLOCATION_ERROR;  | 
1087  | 0  |             delete results;  | 
1088  | 0  |             return NULL;  | 
1089  | 0  |         }  | 
1090  | 0  |     }  | 
1091  |  |  | 
1092  | 0  |     return gmatchInfo;  | 
1093  | 0  | }  | 
1094  |  |  | 
1095  |  | TimeZoneNames::MatchInfoCollection*  | 
1096  | 0  | TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { | 
1097  |  |     // Check if the target name typs is really in the TimeZoneNames  | 
1098  | 0  |     uint32_t nameTypes = 0;  | 
1099  | 0  |     if (types & UTZGNM_LONG) { | 
1100  | 0  |         nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD);  | 
1101  | 0  |     }  | 
1102  | 0  |     if (types & UTZGNM_SHORT) { | 
1103  | 0  |         nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD);  | 
1104  | 0  |     }  | 
1105  |  | 
  | 
1106  | 0  |     if (types) { | 
1107  |  |         // Find matches in the TimeZoneNames  | 
1108  | 0  |         return fTimeZoneNames->find(text, start, nameTypes, status);  | 
1109  | 0  |     }  | 
1110  |  |  | 
1111  | 0  |     return NULL;  | 
1112  | 0  | }  | 
1113  |  |  | 
1114  |  | typedef struct TZGNCoreRef { | 
1115  |  |     TZGNCore*       obj;  | 
1116  |  |     int32_t         refCount;  | 
1117  |  |     double          lastAccess;  | 
1118  |  | } TZGNCoreRef;  | 
1119  |  |  | 
1120  |  | // TZGNCore object cache handling  | 
1121  |  | static UMutex gTZGNLock;  | 
1122  |  | static UHashtable *gTZGNCoreCache = NULL;  | 
1123  |  | static UBool gTZGNCoreCacheInitialized = FALSE;  | 
1124  |  |  | 
1125  |  | // Access count - incremented every time up to SWEEP_INTERVAL,  | 
1126  |  | // then reset to 0  | 
1127  |  | static int32_t gAccessCount = 0;  | 
1128  |  |  | 
1129  |  | // Interval for calling the cache sweep function - every 100 times  | 
1130  | 0  | #define SWEEP_INTERVAL 100  | 
1131  |  |  | 
1132  |  | // Cache expiration in millisecond. When a cached entry is no  | 
1133  |  | // longer referenced and exceeding this threshold since last  | 
1134  |  | // access time, then the cache entry will be deleted by the sweep  | 
1135  |  | // function. For now, 3 minutes.  | 
1136  | 0  | #define CACHE_EXPIRATION 180000.0  | 
1137  |  |  | 
1138  |  | U_CDECL_BEGIN  | 
1139  |  | /**  | 
1140  |  |  * Cleanup callback func  | 
1141  |  |  */  | 
1142  |  | static UBool U_CALLCONV tzgnCore_cleanup(void)  | 
1143  | 0  | { | 
1144  | 0  |     if (gTZGNCoreCache != NULL) { | 
1145  | 0  |         uhash_close(gTZGNCoreCache);  | 
1146  | 0  |         gTZGNCoreCache = NULL;  | 
1147  | 0  |     }  | 
1148  | 0  |     gTZGNCoreCacheInitialized = FALSE;  | 
1149  | 0  |     return TRUE;  | 
1150  | 0  | }  | 
1151  |  |  | 
1152  |  | /**  | 
1153  |  |  * Deleter for TZGNCoreRef  | 
1154  |  |  */  | 
1155  |  | static void U_CALLCONV  | 
1156  | 0  | deleteTZGNCoreRef(void *obj) { | 
1157  | 0  |     icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj;  | 
1158  | 0  |     delete (icu::TZGNCore*) entry->obj;  | 
1159  | 0  |     uprv_free(entry);  | 
1160  | 0  | }  | 
1161  |  | U_CDECL_END  | 
1162  |  |  | 
1163  |  | /**  | 
1164  |  |  * Function used for removing unreferrenced cache entries exceeding  | 
1165  |  |  * the expiration time. This function must be called with in the mutex  | 
1166  |  |  * block.  | 
1167  |  |  */  | 
1168  | 0  | static void sweepCache() { | 
1169  | 0  |     int32_t pos = UHASH_FIRST;  | 
1170  | 0  |     const UHashElement* elem;  | 
1171  | 0  |     double now = (double)uprv_getUTCtime();  | 
1172  |  | 
  | 
1173  | 0  |     while ((elem = uhash_nextElement(gTZGNCoreCache, &pos)) != NULL) { | 
1174  | 0  |         TZGNCoreRef *entry = (TZGNCoreRef *)elem->value.pointer;  | 
1175  | 0  |         if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) { | 
1176  |  |             // delete this entry  | 
1177  | 0  |             uhash_removeElement(gTZGNCoreCache, elem);  | 
1178  | 0  |         }  | 
1179  | 0  |     }  | 
1180  | 0  | }  | 
1181  |  |  | 
1182  |  | TimeZoneGenericNames::TimeZoneGenericNames()  | 
1183  | 0  | : fRef(0) { | 
1184  | 0  | }  | 
1185  |  |  | 
1186  | 0  | TimeZoneGenericNames::~TimeZoneGenericNames() { | 
1187  | 0  |     umtx_lock(&gTZGNLock);  | 
1188  | 0  |     { | 
1189  | 0  |         U_ASSERT(fRef->refCount > 0);  | 
1190  |  |         // Just decrement the reference count  | 
1191  | 0  |         fRef->refCount--;  | 
1192  | 0  |     }  | 
1193  | 0  |     umtx_unlock(&gTZGNLock);  | 
1194  | 0  | }  | 
1195  |  |  | 
1196  |  | TimeZoneGenericNames*  | 
1197  | 0  | TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) { | 
1198  | 0  |     if (U_FAILURE(status)) { | 
1199  | 0  |         return NULL;  | 
1200  | 0  |     }  | 
1201  | 0  |     TimeZoneGenericNames* instance = new TimeZoneGenericNames();  | 
1202  | 0  |     if (instance == NULL) { | 
1203  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
1204  | 0  |         return NULL;  | 
1205  | 0  |     }  | 
1206  |  |  | 
1207  | 0  |     TZGNCoreRef *cacheEntry = NULL;  | 
1208  | 0  |     { | 
1209  | 0  |         Mutex lock(&gTZGNLock);  | 
1210  |  | 
  | 
1211  | 0  |         if (!gTZGNCoreCacheInitialized) { | 
1212  |  |             // Create empty hashtable  | 
1213  | 0  |             gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);  | 
1214  | 0  |             if (U_SUCCESS(status)) { | 
1215  | 0  |                 uhash_setKeyDeleter(gTZGNCoreCache, uprv_free);  | 
1216  | 0  |                 uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef);  | 
1217  | 0  |                 gTZGNCoreCacheInitialized = TRUE;  | 
1218  | 0  |                 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup);  | 
1219  | 0  |             }  | 
1220  | 0  |         }  | 
1221  | 0  |         if (U_FAILURE(status)) { | 
1222  | 0  |             return NULL;  | 
1223  | 0  |         }  | 
1224  |  |  | 
1225  |  |         // Check the cache, if not available, create new one and cache  | 
1226  | 0  |         const char *key = locale.getName();  | 
1227  | 0  |         cacheEntry = (TZGNCoreRef *)uhash_get(gTZGNCoreCache, key);  | 
1228  | 0  |         if (cacheEntry == NULL) { | 
1229  | 0  |             TZGNCore *tzgnCore = NULL;  | 
1230  | 0  |             char *newKey = NULL;  | 
1231  |  | 
  | 
1232  | 0  |             tzgnCore = new TZGNCore(locale, status);  | 
1233  | 0  |             if (tzgnCore == NULL) { | 
1234  | 0  |                 status = U_MEMORY_ALLOCATION_ERROR;  | 
1235  | 0  |             }  | 
1236  | 0  |             if (U_SUCCESS(status)) { | 
1237  | 0  |                 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1);  | 
1238  | 0  |                 if (newKey == NULL) { | 
1239  | 0  |                     status = U_MEMORY_ALLOCATION_ERROR;  | 
1240  | 0  |                 } else { | 
1241  | 0  |                     uprv_strcpy(newKey, key);  | 
1242  | 0  |                 }  | 
1243  | 0  |             }  | 
1244  | 0  |             if (U_SUCCESS(status)) { | 
1245  | 0  |                 cacheEntry = (TZGNCoreRef *)uprv_malloc(sizeof(TZGNCoreRef));  | 
1246  | 0  |                 if (cacheEntry == NULL) { | 
1247  | 0  |                     status = U_MEMORY_ALLOCATION_ERROR;  | 
1248  | 0  |                 } else { | 
1249  | 0  |                     cacheEntry->obj = tzgnCore;  | 
1250  | 0  |                     cacheEntry->refCount = 1;  | 
1251  | 0  |                     cacheEntry->lastAccess = (double)uprv_getUTCtime();  | 
1252  |  | 
  | 
1253  | 0  |                     uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status);  | 
1254  | 0  |                 }  | 
1255  | 0  |             }  | 
1256  | 0  |             if (U_FAILURE(status)) { | 
1257  | 0  |                 if (tzgnCore != NULL) { | 
1258  | 0  |                     delete tzgnCore;  | 
1259  | 0  |                 }  | 
1260  | 0  |                 if (newKey != NULL) { | 
1261  | 0  |                     uprv_free(newKey);  | 
1262  | 0  |                 }  | 
1263  | 0  |                 if (cacheEntry != NULL) { | 
1264  | 0  |                     uprv_free(cacheEntry);  | 
1265  | 0  |                 }  | 
1266  | 0  |                 cacheEntry = NULL;  | 
1267  | 0  |             }  | 
1268  | 0  |         } else { | 
1269  |  |             // Update the reference count  | 
1270  | 0  |             cacheEntry->refCount++;  | 
1271  | 0  |             cacheEntry->lastAccess = (double)uprv_getUTCtime();  | 
1272  | 0  |         }  | 
1273  | 0  |         gAccessCount++;  | 
1274  | 0  |         if (gAccessCount >= SWEEP_INTERVAL) { | 
1275  |  |             // sweep  | 
1276  | 0  |             sweepCache();  | 
1277  | 0  |             gAccessCount = 0;  | 
1278  | 0  |         }  | 
1279  | 0  |     }  // End of mutex locked block  | 
1280  |  |  | 
1281  | 0  |     if (cacheEntry == NULL) { | 
1282  | 0  |         delete instance;  | 
1283  | 0  |         return NULL;  | 
1284  | 0  |     }  | 
1285  |  |  | 
1286  | 0  |     instance->fRef = cacheEntry;  | 
1287  | 0  |     return instance;  | 
1288  | 0  | }  | 
1289  |  |  | 
1290  |  | bool  | 
1291  | 0  | TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const { | 
1292  |  |     // Just compare if the other object also use the same  | 
1293  |  |     // ref entry  | 
1294  | 0  |     return fRef == other.fRef;  | 
1295  | 0  | }  | 
1296  |  |  | 
1297  |  | TimeZoneGenericNames*  | 
1298  | 0  | TimeZoneGenericNames::clone() const { | 
1299  | 0  |     TimeZoneGenericNames* other = new TimeZoneGenericNames();  | 
1300  | 0  |     if (other) { | 
1301  | 0  |         umtx_lock(&gTZGNLock);  | 
1302  | 0  |         { | 
1303  |  |             // Just increments the reference count  | 
1304  | 0  |             fRef->refCount++;  | 
1305  | 0  |             other->fRef = fRef;  | 
1306  | 0  |         }  | 
1307  | 0  |         umtx_unlock(&gTZGNLock);  | 
1308  | 0  |     }  | 
1309  | 0  |     return other;  | 
1310  | 0  | }  | 
1311  |  |  | 
1312  |  | UnicodeString&  | 
1313  |  | TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,  | 
1314  | 0  |                         UDate date, UnicodeString& name) const { | 
1315  | 0  |     return fRef->obj->getDisplayName(tz, type, date, name);  | 
1316  | 0  | }  | 
1317  |  |  | 
1318  |  | UnicodeString&  | 
1319  | 0  | TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const { | 
1320  | 0  |     return fRef->obj->getGenericLocationName(tzCanonicalID, name);  | 
1321  | 0  | }  | 
1322  |  |  | 
1323  |  | int32_t  | 
1324  |  | TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,  | 
1325  | 0  |         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const { | 
1326  | 0  |     return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status);  | 
1327  | 0  | }  | 
1328  |  |  | 
1329  |  | U_NAMESPACE_END  | 
1330  |  | #endif  |