Coverage Report

Created: 2025-06-24 06:43

/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