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