Coverage Report

Created: 2018-09-25 14:53

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