Coverage Report

Created: 2025-06-24 06:54

/src/icu/icu4c/source/i18n/tznames_impl.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
* File TZNAMES_IMPL.CPP
10
*
11
*******************************************************************************
12
*/
13
14
#include "unicode/utypes.h"
15
16
#if !UCONFIG_NO_FORMATTING
17
18
#include "unicode/strenum.h"
19
#include "unicode/stringpiece.h"
20
#include "unicode/ustring.h"
21
#include "unicode/timezone.h"
22
#include "unicode/utf16.h"
23
24
#include "tznames_impl.h"
25
#include "charstr.h"
26
#include "cmemory.h"
27
#include "cstring.h"
28
#include "uassert.h"
29
#include "mutex.h"
30
#include "resource.h"
31
#include "ulocimp.h"
32
#include "uresimp.h"
33
#include "ureslocs.h"
34
#include "zonemeta.h"
35
#include "ucln_in.h"
36
#include "uinvchar.h"
37
#include "uvector.h"
38
#include "olsontz.h"
39
40
U_NAMESPACE_BEGIN
41
42
1.51M
#define ZID_KEY_MAX  128
43
948k
#define MZ_PREFIX_LEN 5
44
45
static const char gZoneStrings[]        = "zoneStrings";
46
static const char gMZPrefix[]           = "meta:";
47
48
static const char EMPTY[]               = "<empty>";   // place holder for empty ZNames
49
static const char DUMMY_LOADER[]        = "<dummy>";   // place holder for dummy ZNamesLoader
50
static const char16_t NO_NAME[]            = { 0 };   // for empty no-fallback time zone names
51
52
// stuff for TZDBTimeZoneNames
53
static const char* TZDBNAMES_KEYS[]               = {"ss", "sd"};
54
static const int32_t TZDBNAMES_KEYS_SIZE = UPRV_LENGTHOF(TZDBNAMES_KEYS);
55
56
static UMutex gDataMutex;
57
58
static UHashtable* gTZDBNamesMap = nullptr;
59
static icu::UInitOnce gTZDBNamesMapInitOnce {};
60
61
static TextTrieMap* gTZDBNamesTrie = nullptr;
62
static icu::UInitOnce gTZDBNamesTrieInitOnce {};
63
64
// The order in which strings are stored may be different than the order in the public enum.
65
enum UTimeZoneNameTypeIndex {
66
    UTZNM_INDEX_UNKNOWN = -1,
67
    UTZNM_INDEX_EXEMPLAR_LOCATION,
68
    UTZNM_INDEX_LONG_GENERIC,
69
    UTZNM_INDEX_LONG_STANDARD,
70
    UTZNM_INDEX_LONG_DAYLIGHT,
71
    UTZNM_INDEX_SHORT_GENERIC,
72
    UTZNM_INDEX_SHORT_STANDARD,
73
    UTZNM_INDEX_SHORT_DAYLIGHT,
74
    UTZNM_INDEX_COUNT
75
};
76
static const char16_t* const EMPTY_NAMES[UTZNM_INDEX_COUNT] = {
77
    nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
78
};
79
80
U_CDECL_BEGIN
81
0
static UBool U_CALLCONV tzdbTimeZoneNames_cleanup() {
82
0
    if (gTZDBNamesMap != nullptr) {
83
0
        uhash_close(gTZDBNamesMap);
84
0
        gTZDBNamesMap = nullptr;
85
0
    }
86
0
    gTZDBNamesMapInitOnce.reset();
87
88
0
    if (gTZDBNamesTrie != nullptr) {
89
0
        delete gTZDBNamesTrie;
90
0
        gTZDBNamesTrie = nullptr;
91
0
    }
92
0
    gTZDBNamesTrieInitOnce.reset();
93
94
0
    return true;
95
0
}
96
U_CDECL_END
97
98
/**
99
 * ZNameInfo stores zone name information in the trie
100
 */
101
struct ZNameInfo {
102
    UTimeZoneNameType   type;
103
    const char16_t*        tzID;
104
    const char16_t*        mzID;
105
};
106
107
/**
108
 * ZMatchInfo stores zone name match information used by find method
109
 */
110
struct ZMatchInfo {
111
    const ZNameInfo*    znameInfo;
112
    int32_t             matchLength;
113
};
114
115
// Helper functions
116
static void mergeTimeZoneKey(const UnicodeString& mzID, char* result, size_t capacity, UErrorCode& status);
117
118
0
#define DEFAULT_CHARACTERNODE_CAPACITY 1
119
120
// ---------------------------------------------------
121
// CharacterNode class implementation
122
// ---------------------------------------------------
123
0
void CharacterNode::clear() {
124
0
    uprv_memset(this, 0, sizeof(*this));
125
0
}
126
127
0
void CharacterNode::deleteValues(UObjectDeleter *valueDeleter) {
128
0
    if (fValues == nullptr) {
129
        // Do nothing.
130
0
    } else if (!fHasValuesVector) {
131
0
        if (valueDeleter) {
132
0
            valueDeleter(fValues);
133
0
        }
134
0
    } else {
135
0
        delete static_cast<UVector*>(fValues);
136
0
    }
137
0
}
138
139
void
140
0
CharacterNode::addValue(void *value, UObjectDeleter *valueDeleter, UErrorCode &status) {
141
0
    if (U_FAILURE(status)) {
142
0
        if (valueDeleter) {
143
0
            valueDeleter(value);
144
0
        }
145
0
        return;
146
0
    }
147
0
    if (fValues == nullptr) {
148
0
        fValues = value;
149
0
    } else {
150
        // At least one value already.
151
0
        if (!fHasValuesVector) {
152
            // There is only one value so far, and not in a vector yet.
153
            // Create a vector and add the old value.
154
0
            LocalPointer<UVector> values(
155
0
                new UVector(valueDeleter, nullptr, DEFAULT_CHARACTERNODE_CAPACITY, status), status);
156
0
            if (U_FAILURE(status)) {
157
0
                if (valueDeleter) {
158
0
                    valueDeleter(value);
159
0
                }
160
0
                return;
161
0
            }
162
0
            if (values->hasDeleter()) {
163
0
                values->adoptElement(fValues, status);
164
0
            } else {
165
0
                values->addElement(fValues, status);
166
0
            }
167
0
            fValues = values.orphan();
168
0
            fHasValuesVector = true;
169
0
        }
170
        // Add the new value.
171
0
        UVector* values = static_cast<UVector*>(fValues);
172
0
        if (values->hasDeleter()) {
173
0
            values->adoptElement(value, status);
174
0
        } else {
175
0
            values->addElement(value, status);
176
0
        }
177
0
    }
178
0
}
179
180
// ---------------------------------------------------
181
// TextTrieMapSearchResultHandler class implementation
182
// ---------------------------------------------------
183
0
TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
184
0
}
185
186
// ---------------------------------------------------
187
// TextTrieMap class implementation
188
// ---------------------------------------------------
189
TextTrieMap::TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleter)
190
1.80k
: fIgnoreCase(ignoreCase), fNodes(nullptr), fNodesCapacity(0), fNodesCount(0), 
191
1.80k
  fLazyContents(nullptr), fIsEmpty(true), fValueDeleter(valueDeleter) {
192
1.80k
}
193
194
0
TextTrieMap::~TextTrieMap() {
195
0
    int32_t index;
196
0
    for (index = 0; index < fNodesCount; ++index) {
197
0
        fNodes[index].deleteValues(fValueDeleter);
198
0
    }
199
0
    uprv_free(fNodes);
200
0
    if (fLazyContents != nullptr) {
201
0
        for (int32_t i=0; i<fLazyContents->size(); i+=2) {
202
0
            if (fValueDeleter) {
203
0
                fValueDeleter(fLazyContents->elementAt(i+1));
204
0
            }
205
0
        } 
206
0
        delete fLazyContents;
207
0
    }
208
0
}
209
210
0
int32_t TextTrieMap::isEmpty() const {
211
    // Use a separate field for fIsEmpty because it will remain unchanged once the
212
    //   Trie is built, while fNodes and fLazyContents change with the lazy init
213
    //   of the nodes structure.  Trying to test the changing fields has
214
    //   thread safety complications.
215
0
    return fIsEmpty;
216
0
}
217
218
219
//  We defer actually building the TextTrieMap node structure until the first time a
220
//     search is performed.  put() simply saves the parameters in case we do
221
//     eventually need to build it.
222
//     
223
void
224
0
TextTrieMap::put(const UnicodeString &key, void *value, ZNStringPool &sp, UErrorCode &status) {
225
0
    const char16_t *s = sp.get(key, status);
226
0
    put(s, value, status);
227
0
}
228
229
// This method is designed for a persistent key, such as string key stored in
230
// resource bundle.
231
void
232
0
TextTrieMap::put(const char16_t *key, void *value, UErrorCode &status) {
233
0
    fIsEmpty = false;
234
0
    if (fLazyContents == nullptr) {
235
0
        LocalPointer<UVector> lpLazyContents(new UVector(status), status);
236
0
        fLazyContents = lpLazyContents.orphan();
237
0
    }
238
0
    if (U_SUCCESS(status)) {
239
0
        U_ASSERT(fLazyContents != nullptr);
240
0
        char16_t *s = const_cast<char16_t *>(key);
241
0
        fLazyContents->addElement(s, status);
242
0
        if (U_SUCCESS(status)) {
243
0
            fLazyContents->addElement(value, status);
244
0
            return;
245
0
        }
246
0
    }
247
0
    if (fValueDeleter) {
248
0
        fValueDeleter(value);
249
0
    }
250
0
}
251
252
void
253
0
TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) {
254
0
    if (fNodes == nullptr) {
255
0
        fNodesCapacity = 512;
256
0
        fNodes = static_cast<CharacterNode*>(uprv_malloc(fNodesCapacity * sizeof(CharacterNode)));
257
0
        if (fNodes == nullptr) {
258
0
            status = U_MEMORY_ALLOCATION_ERROR;
259
0
            return;
260
0
        }
261
0
        fNodes[0].clear();  // Init root node.
262
0
        fNodesCount = 1;
263
0
    }
264
265
0
    UnicodeString foldedKey;
266
0
    const char16_t *keyBuffer;
267
0
    int32_t keyLength;
268
0
    if (fIgnoreCase) {
269
        // Ok to use fastCopyFrom() because we discard the copy when we return.
270
0
        foldedKey.fastCopyFrom(key).foldCase();
271
0
        keyBuffer = foldedKey.getBuffer();
272
0
        keyLength = foldedKey.length();
273
0
    } else {
274
0
        keyBuffer = key.getBuffer();
275
0
        keyLength = key.length();
276
0
    }
277
278
0
    CharacterNode *node = fNodes;
279
0
    int32_t index;
280
0
    for (index = 0; index < keyLength; ++index) {
281
0
        node = addChildNode(node, keyBuffer[index], status);
282
0
    }
283
0
    node->addValue(value, fValueDeleter, status);
284
0
}
285
286
UBool
287
0
TextTrieMap::growNodes() {
288
0
    if (fNodesCapacity == 0xffff) {
289
0
        return false;  // We use 16-bit node indexes.
290
0
    }
291
0
    int32_t newCapacity = fNodesCapacity + 1000;
292
0
    if (newCapacity > 0xffff) {
293
0
        newCapacity = 0xffff;
294
0
    }
295
0
    CharacterNode* newNodes = static_cast<CharacterNode*>(uprv_malloc(newCapacity * sizeof(CharacterNode)));
296
0
    if (newNodes == nullptr) {
297
0
        return false;
298
0
    }
299
0
    uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode));
300
0
    uprv_free(fNodes);
301
0
    fNodes = newNodes;
302
0
    fNodesCapacity = newCapacity;
303
0
    return true;
304
0
}
305
306
CharacterNode*
307
0
TextTrieMap::addChildNode(CharacterNode *parent, char16_t c, UErrorCode &status) {
308
0
    if (U_FAILURE(status)) {
309
0
        return nullptr;
310
0
    }
311
    // Linear search of the sorted list of children.
312
0
    uint16_t prevIndex = 0;
313
0
    uint16_t nodeIndex = parent->fFirstChild;
314
0
    while (nodeIndex > 0) {
315
0
        CharacterNode *current = fNodes + nodeIndex;
316
0
        char16_t childCharacter = current->fCharacter;
317
0
        if (childCharacter == c) {
318
0
            return current;
319
0
        } else if (childCharacter > c) {
320
0
            break;
321
0
        }
322
0
        prevIndex = nodeIndex;
323
0
        nodeIndex = current->fNextSibling;
324
0
    }
325
326
    // Ensure capacity. Grow fNodes[] if needed.
327
0
    if (fNodesCount == fNodesCapacity) {
328
0
        int32_t parentIndex = static_cast<int32_t>(parent - fNodes);
329
0
        if (!growNodes()) {
330
0
            status = U_MEMORY_ALLOCATION_ERROR;
331
0
            return nullptr;
332
0
        }
333
0
        parent = fNodes + parentIndex;
334
0
    }
335
336
    // Insert a new child node with c in sorted order.
337
0
    CharacterNode *node = fNodes + fNodesCount;
338
0
    node->clear();
339
0
    node->fCharacter = c;
340
0
    node->fNextSibling = nodeIndex;
341
0
    if (prevIndex == 0) {
342
0
        parent->fFirstChild = static_cast<uint16_t>(fNodesCount);
343
0
    } else {
344
0
        fNodes[prevIndex].fNextSibling = static_cast<uint16_t>(fNodesCount);
345
0
    }
346
0
    ++fNodesCount;
347
0
    return node;
348
0
}
349
350
CharacterNode*
351
0
TextTrieMap::getChildNode(CharacterNode *parent, char16_t c) const {
352
    // Linear search of the sorted list of children.
353
0
    uint16_t nodeIndex = parent->fFirstChild;
354
0
    while (nodeIndex > 0) {
355
0
        CharacterNode *current = fNodes + nodeIndex;
356
0
        char16_t childCharacter = current->fCharacter;
357
0
        if (childCharacter == c) {
358
0
            return current;
359
0
        } else if (childCharacter > c) {
360
0
            break;
361
0
        }
362
0
        nodeIndex = current->fNextSibling;
363
0
    }
364
0
    return nullptr;
365
0
}
366
367
368
// buildTrie() - The Trie node structure is needed.  Create it from the data that was
369
//               saved at the time the ZoneStringFormatter was created.  The Trie is only
370
//               needed for parsing operations, which are less common than formatting,
371
//               and the Trie is big, which is why its creation is deferred until first use.
372
0
void TextTrieMap::buildTrie(UErrorCode &status) {
373
0
    if (fLazyContents != nullptr) {
374
0
        for (int32_t i=0; i<fLazyContents->size(); i+=2) {
375
0
            const char16_t* key = static_cast<char16_t*>(fLazyContents->elementAt(i));
376
0
            void  *val = fLazyContents->elementAt(i+1);
377
0
            UnicodeString keyString(true, key, -1);  // Aliasing UnicodeString constructor.
378
0
            putImpl(keyString, val, status);
379
0
        }
380
0
        delete fLazyContents;
381
0
        fLazyContents = nullptr; 
382
0
    }
383
0
}
384
385
void
386
TextTrieMap::search(const UnicodeString &text, int32_t start,
387
0
                  TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
388
0
    {
389
        // TODO: if locking the mutex for each check proves to be a performance problem,
390
        //       add a flag of type atomic_int32_t to class TextTrieMap, and use only
391
        //       the ICU atomic safe functions for assigning and testing.
392
        //       Don't test the pointer fLazyContents.
393
        //       Don't do unless it's really required.
394
395
        // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
396
0
        static UMutex TextTrieMutex;
397
398
0
        Mutex lock(&TextTrieMutex);
399
0
        if (fLazyContents != nullptr) {
400
0
            TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this);
401
0
            nonConstThis->buildTrie(status);
402
0
        }
403
0
    }
404
0
    if (fNodes == nullptr) {
405
0
        return;
406
0
    }
407
0
    search(fNodes, text, start, start, handler, status);
408
0
}
409
410
void
411
TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start,
412
0
                  int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
413
0
    if (U_FAILURE(status)) {
414
0
        return;
415
0
    }
416
0
    if (node->hasValues()) {
417
0
        if (!handler->handleMatch(index - start, node, status)) {
418
0
            return;
419
0
        }
420
0
        if (U_FAILURE(status)) {
421
0
            return;
422
0
        }
423
0
    }
424
0
    if (fIgnoreCase) {
425
        // for folding we need to get a complete code point.
426
        // size of character may grow after fold operation;
427
        // then we need to get result as UTF16 code units.
428
0
        UChar32 c32 = text.char32At(index);
429
0
        index += U16_LENGTH(c32);
430
0
        UnicodeString tmp(c32);
431
0
        tmp.foldCase();
432
0
        int32_t tmpidx = 0;
433
0
        while (tmpidx < tmp.length()) {
434
0
            char16_t c = tmp.charAt(tmpidx++);
435
0
            node = getChildNode(node, c);
436
0
            if (node == nullptr) {
437
0
                break;
438
0
            }
439
0
        }
440
0
    } else {
441
        // here we just get the next UTF16 code unit
442
0
        char16_t c = text.charAt(index++);
443
0
        node = getChildNode(node, c);
444
0
    }
445
0
    if (node != nullptr) {
446
0
        search(node, text, start, index, handler, status);
447
0
    }
448
0
}
449
450
// ---------------------------------------------------
451
// ZNStringPool class implementation
452
// ---------------------------------------------------
453
static const int32_t POOL_CHUNK_SIZE = 2000;
454
struct ZNStringPoolChunk: public UMemory {
455
    ZNStringPoolChunk    *fNext;                       // Ptr to next pool chunk
456
    int32_t               fLimit;                       // Index to start of unused area at end of fStrings
457
    char16_t              fStrings[POOL_CHUNK_SIZE];    //  Strings array
458
    ZNStringPoolChunk();
459
};
460
461
274
ZNStringPoolChunk::ZNStringPoolChunk() {
462
274
    fNext = nullptr;
463
274
    fLimit = 0;
464
274
}
465
466
274
ZNStringPool::ZNStringPool(UErrorCode &status) {
467
274
    fChunks = nullptr;
468
274
    fHash   = nullptr;
469
274
    if (U_FAILURE(status)) {
470
0
        return;
471
0
    }
472
274
    fChunks = new ZNStringPoolChunk;
473
274
    if (fChunks == nullptr) {
474
0
        status = U_MEMORY_ALLOCATION_ERROR;
475
0
        return;
476
0
    }
477
478
274
    fHash   = uhash_open(uhash_hashUChars      /* keyHash */, 
479
274
                         uhash_compareUChars   /* keyComp */, 
480
274
                         uhash_compareUChars   /* valueComp */, 
481
274
                         &status);
482
274
    if (U_FAILURE(status)) {
483
0
        return;
484
0
    }
485
274
}
486
487
0
ZNStringPool::~ZNStringPool() {
488
0
    if (fHash != nullptr) {
489
0
        uhash_close(fHash);
490
0
        fHash = nullptr;
491
0
    }
492
493
0
    while (fChunks != nullptr) {
494
0
        ZNStringPoolChunk *nextChunk = fChunks->fNext;
495
0
        delete fChunks;
496
0
        fChunks = nextChunk;
497
0
    }
498
0
}
499
500
static const char16_t EmptyString = 0;
501
502
0
const char16_t *ZNStringPool::get(const char16_t *s, UErrorCode &status) {
503
0
    const char16_t *pooledString;
504
0
    if (U_FAILURE(status)) {
505
0
        return &EmptyString;
506
0
    }
507
508
0
    pooledString = static_cast<char16_t *>(uhash_get(fHash, s));
509
0
    if (pooledString != nullptr) {
510
0
        return pooledString;
511
0
    }
512
513
0
    int32_t length = u_strlen(s);
514
0
    int32_t remainingLength = POOL_CHUNK_SIZE - fChunks->fLimit;
515
0
    if (remainingLength <= length) {
516
0
        U_ASSERT(length < POOL_CHUNK_SIZE);
517
0
        if (length >= POOL_CHUNK_SIZE) {
518
0
            status = U_INTERNAL_PROGRAM_ERROR;
519
0
            return &EmptyString;
520
0
        }
521
0
        ZNStringPoolChunk *oldChunk = fChunks;
522
0
        fChunks = new ZNStringPoolChunk;
523
0
        if (fChunks == nullptr) {
524
0
            status = U_MEMORY_ALLOCATION_ERROR;
525
0
            return &EmptyString;
526
0
        }
527
0
        fChunks->fNext = oldChunk;
528
0
    }
529
    
530
0
    char16_t *destString = &fChunks->fStrings[fChunks->fLimit];
531
0
    u_strcpy(destString, s);
532
0
    fChunks->fLimit += (length + 1);
533
0
    uhash_put(fHash, destString, destString, &status);
534
0
    return destString;
535
0
}        
536
537
538
//
539
//  ZNStringPool::adopt()    Put a string into the hash, but do not copy the string data
540
//                           into the pool's storage.  Used for strings from resource bundles,
541
//                           which will persist for the life of the zone string formatter, and
542
//                           therefore can be used directly without copying.
543
0
const char16_t *ZNStringPool::adopt(const char16_t * s, UErrorCode &status) {
544
0
    const char16_t *pooledString;
545
0
    if (U_FAILURE(status)) {
546
0
        return &EmptyString;
547
0
    }
548
0
    if (s != nullptr) {
549
0
        pooledString = static_cast<char16_t *>(uhash_get(fHash, s));
550
0
        if (pooledString == nullptr) {
551
0
            char16_t *ncs = const_cast<char16_t *>(s);
552
0
            uhash_put(fHash, ncs, ncs, &status);
553
0
        }
554
0
    }
555
0
    return s;
556
0
}
557
558
    
559
0
const char16_t *ZNStringPool::get(const UnicodeString &s, UErrorCode &status) {
560
0
    UnicodeString &nonConstStr = const_cast<UnicodeString &>(s);
561
0
    return this->get(nonConstStr.getTerminatedBuffer(), status);
562
0
}
563
564
/*
565
 * freeze().   Close the hash table that maps to the pooled strings.
566
 *             After freezing, the pool can not be searched or added to,
567
 *             but all existing references to pooled strings remain valid.
568
 *
569
 *             The main purpose is to recover the storage used for the hash.
570
 */
571
0
void ZNStringPool::freeze() {
572
0
    uhash_close(fHash);
573
0
    fHash = nullptr;
574
0
}
575
576
577
/**
578
 * This class stores name data for a meta zone or time zone.
579
 */
580
class ZNames : public UMemory {
581
private:
582
    friend class TimeZoneNamesImpl;
583
584
1.84M
    static UTimeZoneNameTypeIndex getTZNameTypeIndex(UTimeZoneNameType type) {
585
1.84M
        switch(type) {
586
1.02k
        case UTZNM_EXEMPLAR_LOCATION: return UTZNM_INDEX_EXEMPLAR_LOCATION;
587
31
        case UTZNM_LONG_GENERIC: return UTZNM_INDEX_LONG_GENERIC;
588
461k
        case UTZNM_LONG_STANDARD: return UTZNM_INDEX_LONG_STANDARD;
589
459k
        case UTZNM_LONG_DAYLIGHT: return UTZNM_INDEX_LONG_DAYLIGHT;
590
1.36k
        case UTZNM_SHORT_GENERIC: return UTZNM_INDEX_SHORT_GENERIC;
591
462k
        case UTZNM_SHORT_STANDARD: return UTZNM_INDEX_SHORT_STANDARD;
592
459k
        case UTZNM_SHORT_DAYLIGHT: return UTZNM_INDEX_SHORT_DAYLIGHT;
593
1.52k
        default: return UTZNM_INDEX_UNKNOWN;
594
1.84M
        }
595
1.84M
    }
596
0
    static UTimeZoneNameType getTZNameType(UTimeZoneNameTypeIndex index) {
597
0
        switch(index) {
598
0
        case UTZNM_INDEX_EXEMPLAR_LOCATION: return UTZNM_EXEMPLAR_LOCATION;
599
0
        case UTZNM_INDEX_LONG_GENERIC: return UTZNM_LONG_GENERIC;
600
0
        case UTZNM_INDEX_LONG_STANDARD: return UTZNM_LONG_STANDARD;
601
0
        case UTZNM_INDEX_LONG_DAYLIGHT: return UTZNM_LONG_DAYLIGHT;
602
0
        case UTZNM_INDEX_SHORT_GENERIC: return UTZNM_SHORT_GENERIC;
603
0
        case UTZNM_INDEX_SHORT_STANDARD: return UTZNM_SHORT_STANDARD;
604
0
        case UTZNM_INDEX_SHORT_DAYLIGHT: return UTZNM_SHORT_DAYLIGHT;
605
0
        default: return UTZNM_UNKNOWN;
606
0
        }
607
0
    }
608
609
    const char16_t* fNames[UTZNM_INDEX_COUNT];
610
    UBool fDidAddIntoTrie;
611
612
    // Whether we own the location string, if computed rather than loaded from a bundle.
613
    // A meta zone names instance never has an exemplar location string.
614
    UBool fOwnsLocationName;
615
616
    ZNames(const char16_t* names[], const char16_t* locationName)
617
228k
            : fDidAddIntoTrie(false) {
618
228k
        uprv_memcpy(fNames, names, sizeof(fNames));
619
228k
        if (locationName != nullptr) {
620
95.8k
            fOwnsLocationName = true;
621
95.8k
            fNames[UTZNM_INDEX_EXEMPLAR_LOCATION] = locationName;
622
132k
        } else {
623
132k
            fOwnsLocationName = false;
624
132k
        }
625
228k
    }
626
627
public:
628
1.60k
    ~ZNames() {
629
1.60k
        if (fOwnsLocationName) {
630
490
            const char16_t* locationName = fNames[UTZNM_INDEX_EXEMPLAR_LOCATION];
631
490
            U_ASSERT(locationName != nullptr);
632
490
            uprv_free((void*) locationName);
633
490
        }
634
1.60k
    }
635
636
private:
637
    static void* createMetaZoneAndPutInCache(UHashtable* cache, const char16_t* names[],
638
53.3k
            const UnicodeString& mzID, UErrorCode& status) {
639
53.3k
        if (U_FAILURE(status)) { return nullptr; }
640
53.3k
        U_ASSERT(names != nullptr);
641
642
        // Use the persistent ID as the resource key, so we can
643
        // avoid duplications.
644
        // TODO: Is there a more efficient way, like intern() in Java?
645
53.3k
        void* key = (void*) ZoneMeta::findMetaZoneID(mzID);
646
53.3k
        void* value;
647
53.3k
        if (uprv_memcmp(names, EMPTY_NAMES, sizeof(EMPTY_NAMES)) == 0) {
648
19.5k
            value = (void*) EMPTY;
649
33.7k
        } else {
650
33.7k
            value = (void*) (new ZNames(names, nullptr));
651
33.7k
            if (value == nullptr) {
652
0
                status = U_MEMORY_ALLOCATION_ERROR;
653
0
                return nullptr;
654
0
            }
655
33.7k
        }
656
53.3k
        uhash_put(cache, key, value, &status);
657
53.3k
        return value;
658
53.3k
    }
659
660
    static void* createTimeZoneAndPutInCache(UHashtable* cache, const char16_t* names[],
661
195k
            const UnicodeString& tzID, UErrorCode& status) {
662
195k
        if (U_FAILURE(status)) { return nullptr; }
663
195k
        U_ASSERT(names != nullptr);
664
665
        // If necessary, compute the location name from the time zone name.
666
195k
        char16_t* locationName = nullptr;
667
195k
        if (names[UTZNM_INDEX_EXEMPLAR_LOCATION] == nullptr) {
668
133k
            UnicodeString locationNameUniStr;
669
133k
            TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, locationNameUniStr);
670
671
            // Copy the computed location name to the heap
672
133k
            if (locationNameUniStr.length() > 0) {
673
95.8k
                const char16_t* buff = locationNameUniStr.getTerminatedBuffer();
674
95.8k
                int32_t len = sizeof(char16_t) * (locationNameUniStr.length() + 1);
675
95.8k
                locationName = static_cast<char16_t*>(uprv_malloc(len));
676
95.8k
                if (locationName == nullptr) {
677
0
                    status = U_MEMORY_ALLOCATION_ERROR;
678
0
                    return nullptr;
679
0
                }
680
95.8k
                uprv_memcpy(locationName, buff, len);
681
95.8k
            }
682
133k
        }
683
684
        // Use the persistent ID as the resource key, so we can
685
        // avoid duplications.
686
        // TODO: Is there a more efficient way, like intern() in Java?
687
195k
        void* key = (void*) ZoneMeta::findTimeZoneID(tzID);
688
195k
        void* value = (void*) (new ZNames(names, locationName));
689
195k
        if (value == nullptr) {
690
0
            status = U_MEMORY_ALLOCATION_ERROR;
691
0
            return nullptr;
692
0
        }
693
195k
        uhash_put(cache, key, value, &status);
694
195k
        return value;
695
195k
    }
696
697
1.84M
    const char16_t* getName(UTimeZoneNameType type) const {
698
1.84M
        UTimeZoneNameTypeIndex index = getTZNameTypeIndex(type);
699
1.84M
        return index >= 0 ? fNames[index] : nullptr;
700
1.84M
    }
701
702
0
    void addAsMetaZoneIntoTrie(const char16_t* mzID, TextTrieMap& trie, UErrorCode& status) {
703
0
        addNamesIntoTrie(mzID, nullptr, trie, status);
704
0
    }
705
0
    void addAsTimeZoneIntoTrie(const char16_t* tzID, TextTrieMap& trie, UErrorCode& status) {
706
0
        addNamesIntoTrie(nullptr, tzID, trie, status);
707
0
    }
708
709
    void addNamesIntoTrie(const char16_t* mzID, const char16_t* tzID, TextTrieMap& trie,
710
0
            UErrorCode& status) {
711
0
        if (U_FAILURE(status)) { return; }
712
0
        if (fDidAddIntoTrie) { return; }
713
0
        fDidAddIntoTrie = true;
714
715
0
        for (int32_t i = 0; i < UTZNM_INDEX_COUNT; i++) {
716
0
            const char16_t* name = fNames[i];
717
0
            if (name != nullptr) {
718
0
                LocalMemory<ZNameInfo> nameinfo(static_cast<ZNameInfo*>(uprv_malloc(sizeof(ZNameInfo))));
719
0
                if (nameinfo.isNull()) {
720
0
                    status = U_MEMORY_ALLOCATION_ERROR;
721
0
                    return;
722
0
                }
723
0
                nameinfo->mzID = mzID;
724
0
                nameinfo->tzID = tzID;
725
0
                nameinfo->type = getTZNameType(static_cast<UTimeZoneNameTypeIndex>(i));
726
0
                trie.put(name, nameinfo.orphan(), status); // trie.put() takes ownership of the key
727
0
                if (U_FAILURE(status)) {
728
0
                    return;
729
0
                }
730
0
            }
731
0
        }
732
0
    }
733
734
public:
735
    struct ZNamesLoader;
736
};
737
738
struct ZNames::ZNamesLoader : public ResourceSink {
739
    const char16_t *names[UTZNM_INDEX_COUNT];
740
741
248k
    ZNamesLoader() {
742
248k
        clear();
743
248k
    }
744
    virtual ~ZNamesLoader();
745
746
    /** Reset for loading another set of names. */
747
401k
    void clear() {
748
401k
        uprv_memcpy(names, EMPTY_NAMES, sizeof(names));
749
401k
    }
750
751
19.7k
    void loadMetaZone(const UResourceBundle* zoneStrings, const UnicodeString& mzID, UErrorCode& errorCode) {
752
19.7k
        if (U_FAILURE(errorCode)) { return; }
753
754
19.7k
        char key[ZID_KEY_MAX + 1];
755
19.7k
        mergeTimeZoneKey(mzID, key, sizeof(key), errorCode);
756
757
19.7k
        loadNames(zoneStrings, key, errorCode);
758
19.7k
    }
759
760
133k
    void loadTimeZone(const UResourceBundle* zoneStrings, const UnicodeString& tzID, UErrorCode& errorCode) {
761
        // Replace "/" with ":".
762
133k
        UnicodeString uKey(tzID);
763
1.86M
        for (int32_t i = 0; i < uKey.length(); i++) {
764
1.72M
            if (uKey.charAt(i) == static_cast<char16_t>(0x2F)) {
765
118k
                uKey.setCharAt(i, static_cast<char16_t>(0x3A));
766
118k
            }
767
1.72M
        }
768
769
133k
        char key[ZID_KEY_MAX + 1];
770
133k
        if (uKey.length() > ZID_KEY_MAX) {
771
0
            errorCode = U_INTERNAL_PROGRAM_ERROR;
772
0
            return;
773
0
        }
774
133k
        uKey.extract(0, uKey.length(), key, sizeof(key), US_INV);
775
776
133k
        loadNames(zoneStrings, key, errorCode);
777
133k
    }
778
779
153k
    void loadNames(const UResourceBundle* zoneStrings, const char* key, UErrorCode& errorCode) {
780
153k
        U_ASSERT(zoneStrings != nullptr);
781
153k
        U_ASSERT(key != nullptr);
782
153k
        U_ASSERT(key[0] != '\0');
783
784
153k
        UErrorCode localStatus = U_ZERO_ERROR;
785
153k
        clear();
786
153k
        ures_getAllItemsWithFallback(zoneStrings, key, *this, localStatus);
787
788
        // Ignore errors, but propagate possible warnings.
789
153k
        if (U_SUCCESS(localStatus)) {
790
2.52k
            errorCode = localStatus;
791
2.52k
        }
792
153k
    }
793
794
419k
    void setNameIfEmpty(const char* key, const ResourceValue* value, UErrorCode& errorCode) {
795
419k
        UTimeZoneNameTypeIndex type = nameTypeFromKey(key);
796
419k
        if (type == UTZNM_INDEX_UNKNOWN) { return; }
797
152k
        if (names[type] == nullptr) {
798
136k
            int32_t length;
799
            // 'NO_NAME' indicates internally that this field should remain empty.  It will be
800
            // replaced by 'nullptr' in getNames()
801
136k
            names[type] = (value == nullptr) ? NO_NAME : value->getString(length, errorCode);
802
136k
        }
803
152k
    }
804
805
    virtual void put(const char* key, ResourceValue& value, UBool /*noFallback*/,
806
111k
            UErrorCode &errorCode) override {
807
111k
        ResourceTable namesTable = value.getTable(errorCode);
808
111k
        if (U_FAILURE(errorCode)) { return; }
809
531k
        for (int32_t i = 0; namesTable.getKeyAndValue(i, key, value); ++i) {
810
419k
            if (value.isNoInheritanceMarker()) {
811
697
                setNameIfEmpty(key, nullptr, errorCode);
812
418k
            } else {
813
418k
                setNameIfEmpty(key, &value, errorCode);
814
418k
            }
815
419k
        }
816
111k
    }
817
818
419k
    static UTimeZoneNameTypeIndex nameTypeFromKey(const char *key) {
819
419k
        char c0, c1;
820
419k
        if ((c0 = key[0]) == 0 || (c1 = key[1]) == 0 || key[2] != 0) {
821
267k
            return UTZNM_INDEX_UNKNOWN;
822
267k
        }
823
152k
        if (c0 == 'l') {
824
76.1k
            return c1 == 'g' ? UTZNM_INDEX_LONG_GENERIC :
825
76.1k
                    c1 == 's' ? UTZNM_INDEX_LONG_STANDARD :
826
56.7k
                        c1 == 'd' ? UTZNM_INDEX_LONG_DAYLIGHT : UTZNM_INDEX_UNKNOWN;
827
76.1k
        } else if (c0 == 's') {
828
4.74k
            return c1 == 'g' ? UTZNM_INDEX_SHORT_GENERIC :
829
4.74k
                    c1 == 's' ? UTZNM_INDEX_SHORT_STANDARD :
830
3.76k
                        c1 == 'd' ? UTZNM_INDEX_SHORT_DAYLIGHT : UTZNM_INDEX_UNKNOWN;
831
71.2k
        } else if (c0 == 'e' && c1 == 'c') {
832
71.2k
            return UTZNM_INDEX_EXEMPLAR_LOCATION;
833
71.2k
        }
834
0
        return UTZNM_INDEX_UNKNOWN;
835
152k
    }
836
837
    /**
838
    * Returns an array of names.  It is the caller's responsibility to copy the data into a
839
    * permanent location, as the returned array is owned by the loader instance and may be
840
    * cleared or leave scope.
841
    *
842
    * This is different than Java, where the array will no longer be modified and null
843
    * may be returned.
844
    */
845
248k
    const char16_t** getNames() {
846
        // Remove 'NO_NAME' references in the array and replace with 'nullptr'
847
1.98M
        for (int32_t i = 0; i < UTZNM_INDEX_COUNT; ++i) {
848
1.73M
            if (names[i] == NO_NAME) {
849
688
                names[i] = nullptr;
850
688
            }
851
1.73M
        }
852
248k
        return names;
853
248k
    }
854
};
855
856
248k
ZNames::ZNamesLoader::~ZNamesLoader() {}
857
858
859
// ---------------------------------------------------
860
// The meta zone ID enumeration class
861
// ---------------------------------------------------
862
class MetaZoneIDsEnumeration : public StringEnumeration {
863
public:
864
    MetaZoneIDsEnumeration();
865
    MetaZoneIDsEnumeration(const UVector& mzIDs);
866
    MetaZoneIDsEnumeration(LocalPointer<UVector> mzIDs);
867
    virtual ~MetaZoneIDsEnumeration();
868
    static UClassID U_EXPORT2 getStaticClassID();
869
    virtual UClassID getDynamicClassID() const override;
870
    virtual const UnicodeString* snext(UErrorCode& status) override;
871
    virtual void reset(UErrorCode& status) override;
872
    virtual int32_t count(UErrorCode& status) const override;
873
private:
874
    int32_t fLen;
875
    int32_t fPos;
876
    const UVector* fMetaZoneIDs;
877
    LocalPointer<UVector> fLocalVector;
878
};
879
880
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration)
881
882
MetaZoneIDsEnumeration::MetaZoneIDsEnumeration() 
883
16.4k
: fLen(0), fPos(0), fMetaZoneIDs(nullptr), fLocalVector(nullptr) {
884
16.4k
}
885
886
MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector& mzIDs) 
887
2.70k
: fPos(0), fMetaZoneIDs(&mzIDs), fLocalVector(nullptr) {
888
2.70k
    fLen = fMetaZoneIDs->size();
889
2.70k
}
890
891
MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(LocalPointer<UVector> mzIDs)
892
64.4k
: fLen(0), fPos(0), fMetaZoneIDs(nullptr), fLocalVector(std::move(mzIDs)) {
893
64.4k
    fMetaZoneIDs = fLocalVector.getAlias();
894
64.4k
    if (fMetaZoneIDs) {
895
64.4k
        fLen = fMetaZoneIDs->size();
896
64.4k
    }
897
64.4k
}
898
899
const UnicodeString*
900
164k
MetaZoneIDsEnumeration::snext(UErrorCode& status) {
901
164k
    if (U_SUCCESS(status) && fMetaZoneIDs != nullptr && fPos < fLen) {
902
85.8k
        unistr.setTo(static_cast<const char16_t*>(fMetaZoneIDs->elementAt(fPos++)), -1);
903
85.8k
        return &unistr;
904
85.8k
    }
905
78.2k
    return nullptr;
906
164k
}
907
908
void
909
0
MetaZoneIDsEnumeration::reset(UErrorCode& /*status*/) {
910
0
    fPos = 0;
911
0
}
912
913
int32_t
914
0
MetaZoneIDsEnumeration::count(UErrorCode& /*status*/) const {
915
0
    return fLen;
916
0
}
917
918
83.6k
MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
919
83.6k
}
920
921
922
// ---------------------------------------------------
923
// ZNameSearchHandler
924
// ---------------------------------------------------
925
class ZNameSearchHandler : public TextTrieMapSearchResultHandler {
926
public:
927
    ZNameSearchHandler(uint32_t types);
928
    virtual ~ZNameSearchHandler();
929
930
    UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) override;
931
    TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen);
932
933
private:
934
    uint32_t fTypes;
935
    int32_t fMaxMatchLen;
936
    TimeZoneNames::MatchInfoCollection* fResults;
937
};
938
939
ZNameSearchHandler::ZNameSearchHandler(uint32_t types) 
940
0
: fTypes(types), fMaxMatchLen(0), fResults(nullptr) {
941
0
}
942
943
0
ZNameSearchHandler::~ZNameSearchHandler() {
944
0
    delete fResults;
945
0
}
946
947
UBool
948
0
ZNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
949
0
    if (U_FAILURE(status)) {
950
0
        return false;
951
0
    }
952
0
    if (node->hasValues()) {
953
0
        int32_t valuesCount = node->countValues();
954
0
        for (int32_t i = 0; i < valuesCount; i++) {
955
0
            ZNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
956
0
            if (nameinfo == nullptr) {
957
0
                continue;
958
0
            }
959
0
            if ((nameinfo->type & fTypes) != 0) {
960
                // matches a requested type
961
0
                if (fResults == nullptr) {
962
0
                    fResults = new TimeZoneNames::MatchInfoCollection();
963
0
                    if (fResults == nullptr) {
964
0
                        status = U_MEMORY_ALLOCATION_ERROR;
965
0
                    }
966
0
                }
967
0
                if (U_SUCCESS(status)) {
968
0
                    U_ASSERT(fResults != nullptr);
969
0
                    if (nameinfo->tzID) {
970
0
                        fResults->addZone(nameinfo->type, matchLength, UnicodeString(nameinfo->tzID, -1), status);
971
0
                    } else {
972
0
                        U_ASSERT(nameinfo->mzID);
973
0
                        fResults->addMetaZone(nameinfo->type, matchLength, UnicodeString(nameinfo->mzID, -1), status);
974
0
                    }
975
0
                    if (U_SUCCESS(status) && matchLength > fMaxMatchLen) {
976
0
                        fMaxMatchLen = matchLength;
977
0
                    }
978
0
                }
979
0
            }
980
0
        }
981
0
    }
982
0
    return true;
983
0
}
984
985
TimeZoneNames::MatchInfoCollection*
986
0
ZNameSearchHandler::getMatches(int32_t& maxMatchLen) {
987
    // give the ownership to the caller
988
0
    TimeZoneNames::MatchInfoCollection* results = fResults;
989
0
    maxMatchLen = fMaxMatchLen;
990
991
    // reset
992
0
    fResults = nullptr;
993
0
    fMaxMatchLen = 0;
994
0
    return results;
995
0
}
996
997
// ---------------------------------------------------
998
// TimeZoneNamesImpl
999
//
1000
// TimeZoneNames implementation class. This is the main
1001
// part of this module.
1002
// ---------------------------------------------------
1003
1004
U_CDECL_BEGIN
1005
/**
1006
 * Deleter for ZNames
1007
 */
1008
static void U_CALLCONV
1009
1.60k
deleteZNames(void *obj) {
1010
1.60k
    if (obj != EMPTY) {
1011
1.60k
        delete (ZNames*) obj;
1012
1.60k
    }
1013
1.60k
}
1014
1015
/**
1016
 * Deleter for ZNameInfo
1017
 */
1018
static void U_CALLCONV
1019
0
deleteZNameInfo(void *obj) {
1020
0
    uprv_free(obj);
1021
0
}
1022
1023
U_CDECL_END
1024
1025
TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status)
1026
1.52k
: fLocale(locale),
1027
1.52k
  fZoneStrings(nullptr),
1028
1.52k
  fTZNamesMap(nullptr),
1029
1.52k
  fMZNamesMap(nullptr),
1030
1.52k
  fNamesTrieFullyLoaded(false),
1031
1.52k
  fNamesFullyLoaded(false),
1032
1.52k
  fNamesTrie(true, deleteZNameInfo) {
1033
1.52k
    initialize(locale, status);
1034
1.52k
}
1035
1036
void
1037
1.52k
TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) {
1038
1.52k
    if (U_FAILURE(status)) {
1039
0
        return;
1040
0
    }
1041
1042
    // Load zoneStrings bundle
1043
1.52k
    UErrorCode tmpsts = U_ZERO_ERROR;   // OK with fallback warning..
1044
1.52k
    fZoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
1045
1.52k
    fZoneStrings = ures_getByKeyWithFallback(fZoneStrings, gZoneStrings, fZoneStrings, &tmpsts);
1046
1.52k
    if (U_FAILURE(tmpsts)) {
1047
0
        status = tmpsts;
1048
0
        cleanup();
1049
0
        return;
1050
0
    }
1051
1052
    // Initialize hashtables holding time zone/meta zone names
1053
1.52k
    fMZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status);
1054
1.52k
    fTZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status);
1055
1.52k
    if (U_FAILURE(status)) {
1056
0
        cleanup();
1057
0
        return;
1058
0
    }
1059
1060
1.52k
    uhash_setValueDeleter(fMZNamesMap, deleteZNames);
1061
1.52k
    uhash_setValueDeleter(fTZNamesMap, deleteZNames);
1062
    // no key deleters for name maps
1063
1064
    // preload zone strings for the default zone
1065
1.52k
    TimeZone *tz = TimeZone::createDefault();
1066
1.52k
    const char16_t *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
1067
1.52k
    if (tzID != nullptr) {
1068
1.52k
        loadStrings(UnicodeString(tzID), status);
1069
1.52k
    }
1070
1.52k
    delete tz;
1071
1.52k
}
1072
1073
/*
1074
 * This method updates the cache and must be called with a lock,
1075
 * except initializer.
1076
 */
1077
void
1078
77.9k
TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID, UErrorCode& status) {
1079
77.9k
    loadTimeZoneNames(tzCanonicalID, status);
1080
77.9k
    LocalPointer<StringEnumeration> mzIDs(getAvailableMetaZoneIDs(tzCanonicalID, status));
1081
77.9k
    if (U_FAILURE(status)) { return; }
1082
77.9k
    U_ASSERT(!mzIDs.isNull());
1083
1084
77.9k
    const UnicodeString *mzID;
1085
163k
    while (((mzID = mzIDs->snext(status)) != nullptr) && U_SUCCESS(status)) {
1086
85.8k
        loadMetaZoneNames(*mzID, status);
1087
85.8k
    }
1088
77.9k
}
1089
1090
0
TimeZoneNamesImpl::~TimeZoneNamesImpl() {
1091
0
    cleanup();
1092
0
}
1093
1094
void
1095
0
TimeZoneNamesImpl::cleanup() {
1096
0
    if (fZoneStrings != nullptr) {
1097
0
        ures_close(fZoneStrings);
1098
0
        fZoneStrings = nullptr;
1099
0
    }
1100
0
    if (fMZNamesMap != nullptr) {
1101
0
        uhash_close(fMZNamesMap);
1102
0
        fMZNamesMap = nullptr;
1103
0
    }
1104
0
    if (fTZNamesMap != nullptr) {
1105
0
        uhash_close(fTZNamesMap);
1106
0
        fTZNamesMap = nullptr;
1107
0
    }
1108
0
}
1109
1110
bool
1111
0
TimeZoneNamesImpl::operator==(const TimeZoneNames& other) const {
1112
0
    if (this == &other) {
1113
0
        return true;
1114
0
    }
1115
    // No implementation for now
1116
0
    return false;
1117
0
}
1118
1119
TimeZoneNamesImpl*
1120
0
TimeZoneNamesImpl::clone() const {
1121
0
    UErrorCode status = U_ZERO_ERROR;
1122
0
    return new TimeZoneNamesImpl(fLocale, status);
1123
0
}
1124
1125
StringEnumeration*
1126
1.35k
TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const {
1127
1.35k
    return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
1128
1.35k
}
1129
1130
// static implementation of getAvailableMetaZoneIDs(UErrorCode&)
1131
StringEnumeration*
1132
2.70k
TimeZoneNamesImpl::_getAvailableMetaZoneIDs(UErrorCode& status) {
1133
2.70k
    if (U_FAILURE(status)) {
1134
0
        return nullptr;
1135
0
    }
1136
2.70k
    const UVector* mzIDs = ZoneMeta::getAvailableMetazoneIDs();
1137
2.70k
    if (mzIDs == nullptr) {
1138
0
        return new MetaZoneIDsEnumeration();
1139
0
    }
1140
2.70k
    return new MetaZoneIDsEnumeration(*mzIDs);
1141
2.70k
}
1142
1143
StringEnumeration*
1144
79.5k
TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
1145
79.5k
    return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status);
1146
79.5k
}
1147
1148
// static implementation of getAvailableMetaZoneIDs(const UnicodeString&, UErrorCode&)
1149
StringEnumeration*
1150
80.9k
TimeZoneNamesImpl::_getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) {
1151
80.9k
    if (U_FAILURE(status)) {
1152
0
        return nullptr;
1153
0
    }
1154
80.9k
    const UVector* mappings = ZoneMeta::getMetazoneMappings(tzID);
1155
80.9k
    if (mappings == nullptr) {
1156
16.4k
        return new MetaZoneIDsEnumeration();
1157
16.4k
    }
1158
1159
64.4k
    LocalPointer<MetaZoneIDsEnumeration> senum;
1160
64.4k
    LocalPointer<UVector> mzIDs(new UVector(nullptr, uhash_compareUChars, status), status);
1161
64.4k
    if (U_SUCCESS(status)) {
1162
64.4k
        U_ASSERT(mzIDs.isValid());
1163
167k
        for (int32_t i = 0; U_SUCCESS(status) && i < mappings->size(); i++) {
1164
1165
103k
            OlsonToMetaMappingEntry* map = static_cast<OlsonToMetaMappingEntry*>(mappings->elementAt(i));
1166
103k
            const char16_t *mzID = map->mzid;
1167
103k
            if (!mzIDs->contains((void *)mzID)) {
1168
86.4k
                mzIDs->addElement((void *)mzID, status);
1169
86.4k
            }
1170
103k
        }
1171
64.4k
        if (U_SUCCESS(status)) {
1172
64.4k
            senum.adoptInsteadAndCheckErrorCode(new MetaZoneIDsEnumeration(std::move(mzIDs)), status);
1173
64.4k
        }
1174
64.4k
    }
1175
64.4k
    return U_SUCCESS(status) ? senum.orphan() : nullptr;
1176
80.9k
}
1177
1178
UnicodeString&
1179
277k
TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
1180
277k
    return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID);
1181
277k
}
1182
1183
// static implementation of getMetaZoneID
1184
UnicodeString&
1185
279k
TimeZoneNamesImpl::_getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) {
1186
279k
    ZoneMeta::getMetazoneID(tzID, date, mzID);
1187
279k
    return mzID;
1188
279k
}
1189
1190
UnicodeString&
1191
0
TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
1192
0
    return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID);
1193
0
}
1194
1195
// static implementation of getReferenceZoneID
1196
UnicodeString&
1197
0
TimeZoneNamesImpl::_getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) {
1198
0
    ZoneMeta::getZoneIdByMetazone(mzID, UnicodeString(region, -1, US_INV), tzID);
1199
0
    return tzID;
1200
0
}
1201
1202
UnicodeString&
1203
TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID,
1204
                                          UTimeZoneNameType type,
1205
3.23k
                                          UnicodeString& name) const {
1206
3.23k
    name.setToBogus();  // cleanup result.
1207
3.23k
    if (mzID.isEmpty()) {
1208
1.88k
        return name;
1209
1.88k
    }
1210
1211
1.34k
    ZNames *znames = nullptr;
1212
1.34k
    TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1213
1214
1.34k
    {
1215
1.34k
        Mutex lock(&gDataMutex);
1216
1.34k
        UErrorCode status = U_ZERO_ERROR;
1217
1.34k
        znames = nonConstThis->loadMetaZoneNames(mzID, status);
1218
1.34k
        if (U_FAILURE(status)) { return name; }
1219
1.34k
    }
1220
1221
923
    if (znames != nullptr) {
1222
140
        const char16_t* s = znames->getName(type);
1223
140
        if (s != nullptr) {
1224
14
            name.setTo(true, s, -1);
1225
14
        }
1226
140
    }
1227
923
    return name;
1228
1.34k
}
1229
1230
UnicodeString&
1231
8.03k
TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
1232
8.03k
    name.setToBogus();  // cleanup result.
1233
8.03k
    if (tzID.isEmpty()) {
1234
318
        return name;
1235
318
    }
1236
1237
7.71k
    ZNames *tznames = nullptr;
1238
7.71k
    TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1239
1240
7.71k
    {
1241
7.71k
        Mutex lock(&gDataMutex);
1242
7.71k
        UErrorCode status = U_ZERO_ERROR;
1243
7.71k
        tznames = nonConstThis->loadTimeZoneNames(tzID, status);
1244
7.71k
        if (U_FAILURE(status)) { return name; }
1245
7.71k
    }
1246
1247
6.89k
    if (tznames != nullptr) {
1248
6.89k
        const char16_t *s = tznames->getName(type);
1249
6.89k
        if (s != nullptr) {
1250
3.46k
            name.setTo(true, s, -1);
1251
3.46k
        }
1252
6.89k
    }
1253
6.89k
    return name;
1254
7.71k
}
1255
1256
UnicodeString&
1257
1.37k
TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
1258
1.37k
    name.setToBogus();  // cleanup result.
1259
1.37k
    const char16_t* locName = nullptr;
1260
1.37k
    ZNames *tznames = nullptr;
1261
1.37k
    TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1262
1263
1.37k
    {
1264
1.37k
        Mutex lock(&gDataMutex);
1265
1.37k
        UErrorCode status = U_ZERO_ERROR;
1266
1.37k
        tznames = nonConstThis->loadTimeZoneNames(tzID, status);
1267
1.37k
        if (U_FAILURE(status)) { return name; }
1268
1.37k
    }
1269
1270
967
    if (tznames != nullptr) {
1271
967
        locName = tznames->getName(UTZNM_EXEMPLAR_LOCATION);
1272
967
    }
1273
967
    if (locName != nullptr) {
1274
270
        name.setTo(true, locName, -1);
1275
270
    }
1276
1277
967
    return name;
1278
1.37k
}
1279
1280
1281
// Merge the MZ_PREFIX and mzId
1282
static void mergeTimeZoneKey(const UnicodeString& mzID, char* result, size_t capacity,
1283
20.2k
                             UErrorCode& status) {
1284
20.2k
    if (U_FAILURE(status)) {
1285
0
        return;
1286
0
    }
1287
20.2k
    if (mzID.isEmpty()) {
1288
0
        result[0] = '\0';
1289
0
        return;
1290
0
    }
1291
1292
20.2k
    if (MZ_PREFIX_LEN + 1 > capacity) {
1293
0
        result[0] = '\0';
1294
0
        status = U_INTERNAL_PROGRAM_ERROR;
1295
0
        return;
1296
0
    }
1297
20.2k
    uprv_memcpy((void *)result, (void *)gMZPrefix, MZ_PREFIX_LEN);
1298
20.2k
    if (static_cast<size_t>(MZ_PREFIX_LEN +  mzID.length() + 1) > capacity) {
1299
5
        result[0] = '\0';
1300
5
        status = U_INTERNAL_PROGRAM_ERROR;
1301
5
        return;
1302
5
    }
1303
20.1k
    int32_t keyLen = mzID.extract(0, mzID.length(), result + MZ_PREFIX_LEN,
1304
20.1k
                                  static_cast<int32_t>(capacity - MZ_PREFIX_LEN), US_INV);
1305
20.1k
    result[keyLen + MZ_PREFIX_LEN] = '\0';
1306
20.1k
}
1307
1308
/*
1309
 * This method updates the cache and must be called with a lock
1310
 */
1311
ZNames*
1312
332k
TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) {
1313
332k
    if (U_FAILURE(status)) { return nullptr; }
1314
332k
    if (mzID.length() > ZID_KEY_MAX - MZ_PREFIX_LEN) {
1315
422
        status = U_INTERNAL_PROGRAM_ERROR;
1316
422
        return nullptr;
1317
422
    }
1318
1319
331k
    char16_t mzIDKey[ZID_KEY_MAX + 1];
1320
331k
    mzID.extract(mzIDKey, ZID_KEY_MAX, status);
1321
331k
    if (U_FAILURE(status)) {
1322
0
        return nullptr;
1323
0
    }
1324
331k
    mzIDKey[mzID.length()] = 0;
1325
1326
331k
    void* mznames = uhash_get(fMZNamesMap, mzIDKey);
1327
331k
    if (mznames == nullptr) {
1328
19.7k
        ZNames::ZNamesLoader loader;
1329
19.7k
        loader.loadMetaZone(fZoneStrings, mzID, status);
1330
19.7k
        mznames = ZNames::createMetaZoneAndPutInCache(fMZNamesMap, loader.getNames(), mzID, status);
1331
19.7k
        if (U_FAILURE(status)) { return nullptr; }
1332
19.7k
    }
1333
1334
331k
    if (mznames != EMPTY) {
1335
235k
        return static_cast<ZNames*>(mznames);
1336
235k
    } else {
1337
95.9k
        return nullptr;
1338
95.9k
    }
1339
331k
}
1340
1341
/*
1342
 * This method updates the cache and must be called with a lock
1343
 */
1344
ZNames*
1345
359k
TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID, UErrorCode& status) {
1346
359k
    if (U_FAILURE(status)) { return nullptr; }
1347
359k
    if (tzID.length() > ZID_KEY_MAX) {
1348
1.22k
        status = U_INTERNAL_PROGRAM_ERROR;
1349
1.22k
        return nullptr;
1350
1.22k
    }
1351
1352
358k
    char16_t tzIDKey[ZID_KEY_MAX + 1];
1353
358k
    int32_t tzIDKeyLen = tzID.extract(tzIDKey, ZID_KEY_MAX, status);
1354
358k
    U_ASSERT(U_SUCCESS(status));   // already checked length above
1355
358k
    tzIDKey[tzIDKeyLen] = 0;
1356
1357
358k
    void *tznames = uhash_get(fTZNamesMap, tzIDKey);
1358
358k
    if (tznames == nullptr) {
1359
133k
        ZNames::ZNamesLoader loader;
1360
133k
        loader.loadTimeZone(fZoneStrings, tzID, status);
1361
133k
        tznames = ZNames::createTimeZoneAndPutInCache(fTZNamesMap, loader.getNames(), tzID, status);
1362
133k
        if (U_FAILURE(status)) { return nullptr; }
1363
133k
    }
1364
1365
    // tznames is never EMPTY
1366
358k
    return static_cast<ZNames*>(tznames);
1367
358k
}
1368
1369
TimeZoneNames::MatchInfoCollection*
1370
0
TimeZoneNamesImpl::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
1371
0
    ZNameSearchHandler handler(types);
1372
0
    TimeZoneNames::MatchInfoCollection* matches;
1373
0
    TimeZoneNamesImpl* nonConstThis = const_cast<TimeZoneNamesImpl*>(this);
1374
1375
    // Synchronize so that data is not loaded multiple times.
1376
    // TODO: Consider more fine-grained synchronization.
1377
0
    {
1378
0
        Mutex lock(&gDataMutex);
1379
1380
        // First try of lookup.
1381
0
        matches = doFind(handler, text, start, status);
1382
0
        if (U_FAILURE(status)) { return nullptr; }
1383
0
        if (matches != nullptr) {
1384
0
            return matches;
1385
0
        }
1386
1387
        // All names are not yet loaded into the trie.
1388
        // We may have loaded names for formatting several time zones,
1389
        // and might be parsing one of those.
1390
        // Populate the parsing trie from all of the already-loaded names.
1391
0
        nonConstThis->addAllNamesIntoTrie(status);
1392
1393
        // Second try of lookup.
1394
0
        matches = doFind(handler, text, start, status);
1395
0
        if (U_FAILURE(status)) { return nullptr; }
1396
0
        if (matches != nullptr) {
1397
0
            return matches;
1398
0
        }
1399
1400
        // There are still some names we haven't loaded into the trie yet.
1401
        // Load everything now.
1402
0
        nonConstThis->internalLoadAllDisplayNames(status);
1403
0
        nonConstThis->addAllNamesIntoTrie(status);
1404
0
        nonConstThis->fNamesTrieFullyLoaded = true;
1405
0
        if (U_FAILURE(status)) { return nullptr; }
1406
1407
        // Third try: we must return this one.
1408
0
        return doFind(handler, text, start, status);
1409
0
    }
1410
0
}
1411
1412
TimeZoneNames::MatchInfoCollection*
1413
TimeZoneNamesImpl::doFind(ZNameSearchHandler& handler,
1414
0
        const UnicodeString& text, int32_t start, UErrorCode& status) const {
1415
1416
0
    fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
1417
0
    if (U_FAILURE(status)) { return nullptr; }
1418
1419
0
    int32_t maxLen = 0;
1420
0
    TimeZoneNames::MatchInfoCollection* matches = handler.getMatches(maxLen);
1421
0
    if (matches != nullptr && ((maxLen == (text.length() - start)) || fNamesTrieFullyLoaded)) {
1422
        // perfect match, or no more names available
1423
0
        return matches;
1424
0
    }
1425
0
    delete matches;
1426
0
    return nullptr;
1427
0
}
1428
1429
// Caller must synchronize.
1430
0
void TimeZoneNamesImpl::addAllNamesIntoTrie(UErrorCode& status) {
1431
0
    if (U_FAILURE(status)) return;
1432
0
    int32_t pos;
1433
0
    const UHashElement* element;
1434
1435
0
    pos = UHASH_FIRST;
1436
0
    while ((element = uhash_nextElement(fMZNamesMap, &pos)) != nullptr) {
1437
0
        if (element->value.pointer == EMPTY) { continue; }
1438
0
        char16_t* mzID = static_cast<char16_t*>(element->key.pointer);
1439
0
        ZNames* znames = static_cast<ZNames*>(element->value.pointer);
1440
0
        znames->addAsMetaZoneIntoTrie(mzID, fNamesTrie, status);
1441
0
        if (U_FAILURE(status)) { return; }
1442
0
    }
1443
1444
0
    pos = UHASH_FIRST;
1445
0
    while ((element = uhash_nextElement(fTZNamesMap, &pos)) != nullptr) {
1446
0
        if (element->value.pointer == EMPTY) { continue; }
1447
0
        char16_t* tzID = static_cast<char16_t*>(element->key.pointer);
1448
0
        ZNames* znames = static_cast<ZNames*>(element->value.pointer);
1449
0
        znames->addAsTimeZoneIntoTrie(tzID, fNamesTrie, status);
1450
0
        if (U_FAILURE(status)) { return; }
1451
0
    }
1452
0
}
1453
1454
U_CDECL_BEGIN
1455
static void U_CALLCONV
1456
95.1k
deleteZNamesLoader(void* obj) {
1457
95.1k
    if (obj == DUMMY_LOADER) { return; }
1458
94.8k
    const ZNames::ZNamesLoader* loader = (const ZNames::ZNamesLoader*) obj;
1459
94.8k
    delete loader;
1460
94.8k
}
1461
U_CDECL_END
1462
1463
struct TimeZoneNamesImpl::ZoneStringsLoader : public ResourceSink {
1464
    TimeZoneNamesImpl& tzn;
1465
    UHashtable* keyToLoader;
1466
1467
    ZoneStringsLoader(TimeZoneNamesImpl& _tzn, UErrorCode& status)
1468
300
            : tzn(_tzn) {
1469
300
        keyToLoader = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status);
1470
300
        if (U_FAILURE(status)) { return; }
1471
300
        uhash_setKeyDeleter(keyToLoader, uprv_free);
1472
300
        uhash_setValueDeleter(keyToLoader, deleteZNamesLoader);
1473
300
    }
1474
    virtual ~ZoneStringsLoader();
1475
1476
95.1k
    void* createKey(const char* key, UErrorCode& status) {
1477
95.1k
        int32_t len = sizeof(char) * (static_cast<int32_t>(uprv_strlen(key)) + 1);
1478
95.1k
        char* newKey = static_cast<char*>(uprv_malloc(len));
1479
95.1k
        if (newKey == nullptr) {
1480
0
            status = U_MEMORY_ALLOCATION_ERROR;
1481
0
            return nullptr;
1482
0
        }
1483
95.1k
        uprv_memcpy(newKey, key, len);
1484
95.1k
        newKey[len-1] = '\0';
1485
95.1k
        return (void*) newKey;
1486
95.1k
    }
1487
1488
190k
    UBool isMetaZone(const char* key) {
1489
190k
        return (uprv_strlen(key) >= MZ_PREFIX_LEN && uprv_memcmp(key, gMZPrefix, MZ_PREFIX_LEN) == 0);
1490
190k
    }
1491
1492
67.3k
    UnicodeString mzIDFromKey(const char* key) {
1493
67.3k
        return UnicodeString(key + MZ_PREFIX_LEN, static_cast<int32_t>(uprv_strlen(key)) - MZ_PREFIX_LEN, US_INV);
1494
67.3k
    }
1495
1496
122k
    UnicodeString tzIDFromKey(const char* key) {
1497
122k
        UnicodeString tzID(key, -1, US_INV);
1498
        // Replace all colons ':' with slashes '/'
1499
2.03M
        for (int i=0; i<tzID.length(); i++) {
1500
1.91M
            if (tzID.charAt(i) == 0x003A) {
1501
131k
                tzID.setCharAt(i, 0x002F);
1502
131k
            }
1503
1.91M
        }
1504
122k
        return tzID;
1505
122k
    }
1506
1507
300
    void load(UErrorCode& status) {
1508
300
        ures_getAllItemsWithFallback(tzn.fZoneStrings, "", *this, status);
1509
300
        if (U_FAILURE(status)) { return; }
1510
1511
300
        int32_t pos = UHASH_FIRST;
1512
300
        const UHashElement* element;
1513
95.4k
        while ((element = uhash_nextElement(keyToLoader, &pos)) != nullptr) {
1514
95.1k
            if (element->value.pointer == DUMMY_LOADER) { continue; }
1515
94.8k
            ZNames::ZNamesLoader* loader = static_cast<ZNames::ZNamesLoader*>(element->value.pointer);
1516
94.8k
            char* key = static_cast<char*>(element->key.pointer);
1517
1518
94.8k
            if (isMetaZone(key)) {
1519
33.6k
                UnicodeString mzID = mzIDFromKey(key);
1520
33.6k
                ZNames::createMetaZoneAndPutInCache(tzn.fMZNamesMap, loader->getNames(), mzID, status);
1521
61.2k
            } else {
1522
61.2k
                UnicodeString tzID = tzIDFromKey(key);
1523
61.2k
                ZNames::createTimeZoneAndPutInCache(tzn.fTZNamesMap, loader->getNames(), tzID, status);
1524
61.2k
            }
1525
94.8k
            if (U_FAILURE(status)) { return; }
1526
94.8k
        }
1527
300
    }
1528
1529
    void consumeNamesTable(const char *key, ResourceValue &value, UBool noFallback,
1530
107k
            UErrorCode &status) {
1531
107k
        if (U_FAILURE(status)) { return; }
1532
1533
107k
        void* loader = uhash_get(keyToLoader, key);
1534
107k
        if (loader == nullptr) {
1535
95.1k
            if (isMetaZone(key)) {
1536
33.6k
                UnicodeString mzID = mzIDFromKey(key);
1537
33.6k
                void* cacheVal = uhash_get(tzn.fMZNamesMap, mzID.getTerminatedBuffer());
1538
33.6k
                if (cacheVal != nullptr) {
1539
                    // We have already loaded the names for this meta zone.
1540
0
                    loader = (void*) DUMMY_LOADER;
1541
33.6k
                } else {
1542
33.6k
                    loader = (void*) new ZNames::ZNamesLoader();
1543
33.6k
                    if (loader == nullptr) {
1544
0
                        status = U_MEMORY_ALLOCATION_ERROR;
1545
0
                        return;
1546
0
                    }
1547
33.6k
                }
1548
61.5k
            } else {
1549
61.5k
                UnicodeString tzID = tzIDFromKey(key);
1550
61.5k
                void* cacheVal = uhash_get(tzn.fTZNamesMap, tzID.getTerminatedBuffer());
1551
61.5k
                if (cacheVal != nullptr) {
1552
                    // We have already loaded the names for this time zone.
1553
300
                    loader = (void*) DUMMY_LOADER;
1554
61.2k
                } else {
1555
61.2k
                    loader = (void*) new ZNames::ZNamesLoader();
1556
61.2k
                    if (loader == nullptr) {
1557
0
                        status = U_MEMORY_ALLOCATION_ERROR;
1558
0
                        return;
1559
0
                    }
1560
61.2k
                }
1561
61.5k
            }
1562
1563
95.1k
            void* newKey = createKey(key, status);
1564
95.1k
            if (U_FAILURE(status)) {
1565
0
                deleteZNamesLoader(loader);
1566
0
                return;
1567
0
            }
1568
1569
95.1k
            uhash_put(keyToLoader, newKey, loader, &status);
1570
95.1k
            if (U_FAILURE(status)) { return; }
1571
95.1k
        }
1572
1573
107k
        if (loader != DUMMY_LOADER) {
1574
            // Let the ZNamesLoader consume the names table.
1575
107k
            static_cast<ZNames::ZNamesLoader*>(loader)->put(key, value, noFallback, status);
1576
107k
        }
1577
107k
    }
1578
1579
    virtual void put(const char *key, ResourceValue &value, UBool noFallback,
1580
637
            UErrorCode &status) override {
1581
637
        ResourceTable timeZonesTable = value.getTable(status);
1582
637
        if (U_FAILURE(status)) { return; }
1583
111k
        for (int32_t i = 0; timeZonesTable.getKeyAndValue(i, key, value); ++i) {
1584
111k
            U_ASSERT(!value.isNoInheritanceMarker());
1585
111k
            if (value.getType() == URES_TABLE) {
1586
107k
                consumeNamesTable(key, value, noFallback, status);
1587
107k
            } else {
1588
                // Ignore fields that aren't tables (e.g., fallbackFormat and regionFormatStandard).
1589
                // All time zone fields are tables.
1590
3.34k
            }
1591
111k
            if (U_FAILURE(status)) { return; }
1592
111k
        }
1593
637
    }
1594
};
1595
1596
// Virtual destructors must be defined out of line.
1597
300
TimeZoneNamesImpl::ZoneStringsLoader::~ZoneStringsLoader() {
1598
300
    uhash_close(keyToLoader);
1599
300
}
1600
1601
427
void TimeZoneNamesImpl::loadAllDisplayNames(UErrorCode& status) {
1602
427
    if (U_FAILURE(status)) return;
1603
1604
427
    {
1605
427
        Mutex lock(&gDataMutex);
1606
427
        internalLoadAllDisplayNames(status);
1607
427
    }
1608
427
}
1609
1610
void TimeZoneNamesImpl::getDisplayNames(const UnicodeString& tzID,
1611
        const UTimeZoneNameType types[], int32_t numTypes,
1612
272k
        UDate date, UnicodeString dest[], UErrorCode& status) const {
1613
272k
    if (U_FAILURE(status)) return;
1614
1615
272k
    if (tzID.isEmpty()) { return; }
1616
272k
    void* tznames = nullptr;
1617
272k
    void* mznames = nullptr;
1618
272k
    TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl*>(this);
1619
1620
    // Load the time zone strings
1621
272k
    {
1622
272k
        Mutex lock(&gDataMutex);
1623
272k
        tznames = (void*) nonConstThis->loadTimeZoneNames(tzID, status);
1624
272k
        if (U_FAILURE(status)) { return; }
1625
272k
    }
1626
272k
    U_ASSERT(tznames != nullptr);
1627
1628
    // Load the values into the dest array
1629
1.36M
    for (int i = 0; i < numTypes; i++) {
1630
1.08M
        UTimeZoneNameType type = types[i];
1631
1.08M
        const char16_t* name = static_cast<ZNames*>(tznames)->getName(type);
1632
1.08M
        if (name == nullptr) {
1633
1.08M
            if (mznames == nullptr) {
1634
                // Load the meta zone name
1635
272k
                UnicodeString mzID;
1636
272k
                getMetaZoneID(tzID, date, mzID);
1637
272k
                if (mzID.isEmpty()) {
1638
27.3k
                    mznames = (void*) EMPTY;
1639
245k
                } else {
1640
                    // Load the meta zone strings
1641
                    // Mutex is scoped to the "else" statement
1642
245k
                    Mutex lock(&gDataMutex);
1643
245k
                    mznames = (void*) nonConstThis->loadMetaZoneNames(mzID, status);
1644
245k
                    if (U_FAILURE(status)) { return; }
1645
                    // Note: when the metazone doesn't exist, in Java, loadMetaZoneNames returns
1646
                    // a dummy object instead of nullptr.
1647
245k
                    if (mznames == nullptr) {
1648
57.7k
                        mznames = (void*) EMPTY;
1649
57.7k
                    }
1650
245k
                }
1651
272k
            }
1652
1.08M
            U_ASSERT(mznames != nullptr);
1653
1.08M
            if (mznames != EMPTY) {
1654
748k
                name = static_cast<ZNames*>(mznames)->getName(type);
1655
748k
            }
1656
1.08M
        }
1657
1.08M
        if (name != nullptr) {
1658
355k
            dest[i].setTo(true, name, -1);
1659
733k
        } else {
1660
733k
            dest[i].setToBogus();
1661
733k
        }
1662
1.08M
    }
1663
272k
}
1664
1665
// Caller must synchronize.
1666
427
void TimeZoneNamesImpl::internalLoadAllDisplayNames(UErrorCode& status) {
1667
427
    if (!fNamesFullyLoaded) {
1668
300
        fNamesFullyLoaded = true;
1669
1670
300
        ZoneStringsLoader loader(*this, status);
1671
300
        loader.load(status);
1672
300
        if (U_FAILURE(status)) { return; }
1673
1674
300
        const UnicodeString *id;
1675
1676
        // load strings for all zones
1677
300
        StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(
1678
300
            UCAL_ZONE_TYPE_CANONICAL, nullptr, nullptr, status);
1679
300
        if (U_SUCCESS(status)) {
1680
138k
            while ((id = tzIDs->snext(status)) != nullptr) {
1681
137k
                if (U_FAILURE(status)) {
1682
0
                    break;
1683
0
                }
1684
137k
                UnicodeString copy(*id);
1685
137k
                void* value = uhash_get(fTZNamesMap, copy.getTerminatedBuffer());
1686
137k
                if (value == nullptr) {
1687
                    // loadStrings also loads related metazone strings
1688
76.4k
                    loadStrings(*id, status);
1689
76.4k
                }
1690
137k
            }
1691
300
        }
1692
300
        delete tzIDs;
1693
300
    }
1694
427
}
1695
1696
1697
1698
static const char16_t gEtcPrefix[]         = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
1699
static const int32_t gEtcPrefixLen      = 4;
1700
static const char16_t gSystemVPrefix[]     = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
1701
static const int32_t gSystemVPrefixLen  = 8;
1702
static const char16_t gRiyadh8[]           = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
1703
static const int32_t gRiyadh8Len       = 7;
1704
1705
UnicodeString& U_EXPORT2
1706
135k
TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) {
1707
135k
    if (tzID.isEmpty() || tzID.startsWith(gEtcPrefix, gEtcPrefixLen)
1708
135k
        || tzID.startsWith(gSystemVPrefix, gSystemVPrefixLen) || tzID.indexOf(gRiyadh8, gRiyadh8Len, 0) > 0) {
1709
16.0k
        name.setToBogus();
1710
16.0k
        return name;
1711
16.0k
    }
1712
1713
119k
    int32_t sep = tzID.lastIndexOf(static_cast<char16_t>(0x2F) /* '/' */);
1714
119k
    if (sep > 0 && sep + 1 < tzID.length()) {
1715
96.4k
        name.setTo(tzID, sep + 1);
1716
96.4k
        name.findAndReplace(UnicodeString(static_cast<char16_t>(0x5f) /* _ */),
1717
96.4k
                            UnicodeString(static_cast<char16_t>(0x20) /* space */));
1718
96.4k
    } else {
1719
22.8k
        name.setToBogus();
1720
22.8k
    }
1721
119k
    return name;
1722
135k
}
1723
1724
// ---------------------------------------------------
1725
// TZDBTimeZoneNames and its supporting classes
1726
//
1727
// TZDBTimeZoneNames is an implementation class of
1728
// TimeZoneNames holding the IANA tz database abbreviations.
1729
// ---------------------------------------------------
1730
1731
class TZDBNames : public UMemory {
1732
public:
1733
    virtual ~TZDBNames();
1734
1735
    static TZDBNames* createInstance(UResourceBundle* rb, const char* key);
1736
    const char16_t* getName(UTimeZoneNameType type) const;
1737
    const char** getParseRegions(int32_t& numRegions) const;
1738
1739
protected:
1740
    TZDBNames(const char16_t** names, char** regions, int32_t numRegions);
1741
1742
private:
1743
    const char16_t** fNames;
1744
    char** fRegions;
1745
    int32_t fNumRegions;
1746
};
1747
1748
TZDBNames::TZDBNames(const char16_t** names, char** regions, int32_t numRegions)
1749
56
    :   fNames(names),
1750
56
        fRegions(regions),
1751
56
        fNumRegions(numRegions) {
1752
56
}
1753
1754
3
TZDBNames::~TZDBNames() {
1755
3
    if (fNames != nullptr) {
1756
3
        uprv_free(fNames);
1757
3
    }
1758
3
    if (fRegions != nullptr) {
1759
2
        char **p = fRegions;
1760
5
        for (int32_t i = 0; i < fNumRegions; p++, i++) {
1761
3
            uprv_free(*p);
1762
3
        }
1763
2
        uprv_free(fRegions);
1764
2
    }
1765
3
}
1766
1767
TZDBNames*
1768
483
TZDBNames::createInstance(UResourceBundle* rb, const char* key) {
1769
483
    if (rb == nullptr || key == nullptr || *key == 0) {
1770
0
        return nullptr;
1771
0
    }
1772
1773
483
    UErrorCode status = U_ZERO_ERROR;
1774
1775
483
    const char16_t **names = nullptr;
1776
483
    char** regions = nullptr;
1777
483
    int32_t numRegions = 0;
1778
1779
483
    int32_t len = 0;
1780
1781
483
    UResourceBundle* rbTable = nullptr;
1782
483
    rbTable = ures_getByKey(rb, key, rbTable, &status);
1783
483
    if (U_FAILURE(status)) {
1784
427
        return nullptr;
1785
427
    }
1786
1787
56
    names = static_cast<const char16_t**>(uprv_malloc(sizeof(const char16_t*) * TZDBNAMES_KEYS_SIZE));
1788
56
    UBool isEmpty = true;
1789
56
    if (names != nullptr) {
1790
168
        for (int32_t i = 0; i < TZDBNAMES_KEYS_SIZE; i++) {
1791
112
            status = U_ZERO_ERROR;
1792
112
            const char16_t *value = ures_getStringByKey(rbTable, TZDBNAMES_KEYS[i], &len, &status);
1793
112
            if (U_FAILURE(status) || len == 0) {
1794
14
                names[i] = nullptr;
1795
98
            } else {
1796
98
                names[i] = value;
1797
98
                isEmpty = false;
1798
98
            }
1799
112
        }
1800
56
    }
1801
1802
56
    if (isEmpty) {
1803
0
        if (names != nullptr) {
1804
0
            uprv_free(names);
1805
0
        }
1806
0
        return nullptr;
1807
0
    }
1808
1809
56
    UResourceBundle *regionsRes = ures_getByKey(rbTable, "parseRegions", nullptr, &status);
1810
56
    UBool regionError = false;
1811
56
    if (U_SUCCESS(status)) {
1812
11
        numRegions = ures_getSize(regionsRes);
1813
11
        if (numRegions > 0) {
1814
11
            regions = static_cast<char**>(uprv_malloc(sizeof(char*) * numRegions));
1815
11
            if (regions != nullptr) {
1816
11
                char **pRegion = regions;
1817
30
                for (int32_t i = 0; i < numRegions; i++, pRegion++) {
1818
19
                    *pRegion = nullptr;
1819
19
                }
1820
                // filling regions
1821
11
                pRegion = regions;
1822
30
                for (int32_t i = 0; i < numRegions; i++, pRegion++) {
1823
19
                    status = U_ZERO_ERROR;
1824
19
                    const char16_t *uregion = ures_getStringByIndex(regionsRes, i, &len, &status);
1825
19
                    if (U_FAILURE(status)) {
1826
0
                        regionError = true;
1827
0
                        break;
1828
0
                    }
1829
19
                    *pRegion = static_cast<char*>(uprv_malloc(sizeof(char) * (len + 1)));
1830
19
                    if (*pRegion == nullptr) {
1831
0
                        regionError = true;
1832
0
                        break;
1833
0
                    }
1834
19
                    u_UCharsToChars(uregion, *pRegion, len);
1835
19
                    (*pRegion)[len] = 0;
1836
19
                }
1837
11
            }
1838
11
        }
1839
11
    }
1840
56
    ures_close(regionsRes);
1841
56
    ures_close(rbTable);
1842
1843
56
    if (regionError) {
1844
0
        if (names != nullptr) {
1845
0
            uprv_free(names);
1846
0
        }
1847
0
        if (regions != nullptr) {
1848
0
            char **p = regions;
1849
0
            for (int32_t i = 0; i < numRegions; p++, i++) {
1850
0
                uprv_free(*p);
1851
0
            }
1852
0
            uprv_free(regions);
1853
0
        }
1854
0
        return nullptr;
1855
0
    }
1856
1857
56
    return new TZDBNames(names, regions, numRegions);
1858
56
}
1859
1860
const char16_t*
1861
181
TZDBNames::getName(UTimeZoneNameType type) const {
1862
181
    if (fNames == nullptr) {
1863
0
        return nullptr;
1864
0
    }
1865
181
    const char16_t *name = nullptr;
1866
181
    switch(type) {
1867
8
    case UTZNM_SHORT_STANDARD:
1868
8
        name = fNames[0];
1869
8
        break;
1870
4
    case UTZNM_SHORT_DAYLIGHT:
1871
4
        name = fNames[1];
1872
4
        break;
1873
169
    default:
1874
169
        name = nullptr;
1875
181
    }
1876
181
    return name;
1877
181
}
1878
1879
const char**
1880
0
TZDBNames::getParseRegions(int32_t& numRegions) const {
1881
0
    if (fRegions == nullptr) {
1882
0
        numRegions = 0;
1883
0
    } else {
1884
0
        numRegions = fNumRegions;
1885
0
    }
1886
0
    return (const char**)fRegions;
1887
0
}
1888
1889
U_CDECL_BEGIN
1890
/**
1891
 * TZDBNameInfo stores metazone name information for the IANA abbreviations
1892
 * in the trie
1893
 */
1894
typedef struct TZDBNameInfo {
1895
    const char16_t*        mzID;
1896
    UTimeZoneNameType   type;
1897
    UBool               ambiguousType;
1898
    const char**        parseRegions;
1899
    int32_t             nRegions;
1900
} TZDBNameInfo;
1901
U_CDECL_END
1902
1903
1904
class TZDBNameSearchHandler : public TextTrieMapSearchResultHandler {
1905
public:
1906
    TZDBNameSearchHandler(uint32_t types, StringPiece region);
1907
    virtual ~TZDBNameSearchHandler();
1908
1909
    UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) override;
1910
    TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen);
1911
1912
private:
1913
    uint32_t fTypes;
1914
    int32_t fMaxMatchLen;
1915
    TimeZoneNames::MatchInfoCollection* fResults;
1916
    StringPiece fRegion;
1917
};
1918
1919
TZDBNameSearchHandler::TZDBNameSearchHandler(uint32_t types, StringPiece region)
1920
0
: fTypes(types), fMaxMatchLen(0), fResults(nullptr), fRegion(region) {
1921
0
}
1922
1923
0
TZDBNameSearchHandler::~TZDBNameSearchHandler() {
1924
0
    delete fResults;
1925
0
}
1926
1927
UBool
1928
0
TZDBNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
1929
0
    if (U_FAILURE(status)) {
1930
0
        return false;
1931
0
    }
1932
1933
0
    TZDBNameInfo *match = nullptr;
1934
0
    TZDBNameInfo *defaultRegionMatch = nullptr;
1935
1936
0
    if (node->hasValues()) {
1937
0
        int32_t valuesCount = node->countValues();
1938
0
        for (int32_t i = 0; i < valuesCount; i++) {
1939
0
            TZDBNameInfo *ninfo = (TZDBNameInfo *)node->getValue(i);
1940
0
            if (ninfo == nullptr) {
1941
0
                continue;
1942
0
            }
1943
0
            if ((ninfo->type & fTypes) != 0) {
1944
                // Some tz database abbreviations are ambiguous. For example,
1945
                // CST means either Central Standard Time or China Standard Time.
1946
                // Unlike CLDR time zone display names, this implementation
1947
                // does not use unique names. And TimeZoneFormat does not expect
1948
                // multiple results returned for the same time zone type.
1949
                // For this reason, this implementation resolve one among same
1950
                // zone type with a same name at this level.
1951
0
                if (ninfo->parseRegions == nullptr) {
1952
                    // parseRegions == null means this is the default metazone
1953
                    // mapping for the abbreviation.
1954
0
                    if (defaultRegionMatch == nullptr) {
1955
0
                        match = defaultRegionMatch = ninfo;
1956
0
                    }
1957
0
                } else {
1958
0
                    UBool matchRegion = false;
1959
                    // non-default metazone mapping for an abbreviation
1960
                    // comes with applicable regions. For example, the default
1961
                    // metazone mapping for "CST" is America_Central,
1962
                    // but if region is one of CN/MO/TW, "CST" is parsed
1963
                    // as metazone China (China Standard Time).
1964
0
                    for (int32_t j = 0; j < ninfo->nRegions; j++) {
1965
0
                        const char *region = ninfo->parseRegions[j];
1966
0
                        if (fRegion == region) {
1967
0
                            match = ninfo;
1968
0
                            matchRegion = true;
1969
0
                            break;
1970
0
                        }
1971
0
                    }
1972
0
                    if (matchRegion) {
1973
0
                        break;
1974
0
                    }
1975
0
                    if (match == nullptr) {
1976
0
                        match = ninfo;
1977
0
                    }
1978
0
                }
1979
0
            }
1980
0
        }
1981
1982
0
        if (match != nullptr) {
1983
0
            UTimeZoneNameType ntype = match->type;
1984
            // Note: Workaround for duplicated standard/daylight names
1985
            // The tz database contains a few zones sharing a
1986
            // same name for both standard time and daylight saving
1987
            // time. For example, Australia/Sydney observes DST,
1988
            // but "EST" is used for both standard and daylight.
1989
            // When both SHORT_STANDARD and SHORT_DAYLIGHT are included
1990
            // in the find operation, we cannot tell which one was
1991
            // actually matched.
1992
            // TimeZoneFormat#parse returns a matched name type (standard
1993
            // or daylight) and DateFormat implementation uses the info to
1994
            // to adjust actual time. To avoid false type information,
1995
            // this implementation replaces the name type with SHORT_GENERIC.
1996
0
            if (match->ambiguousType
1997
0
                    && (ntype == UTZNM_SHORT_STANDARD || ntype == UTZNM_SHORT_DAYLIGHT)
1998
0
                    && (fTypes & UTZNM_SHORT_STANDARD) != 0
1999
0
                    && (fTypes & UTZNM_SHORT_DAYLIGHT) != 0) {
2000
0
                ntype = UTZNM_SHORT_GENERIC;
2001
0
            }
2002
2003
0
            if (fResults == nullptr) {
2004
0
                fResults = new TimeZoneNames::MatchInfoCollection();
2005
0
                if (fResults == nullptr) {
2006
0
                    status = U_MEMORY_ALLOCATION_ERROR;
2007
0
                }
2008
0
            }
2009
0
            if (U_SUCCESS(status)) {
2010
0
                U_ASSERT(fResults != nullptr);
2011
0
                U_ASSERT(match->mzID != nullptr);
2012
0
                fResults->addMetaZone(ntype, matchLength, UnicodeString(match->mzID, -1), status);
2013
0
                if (U_SUCCESS(status) && matchLength > fMaxMatchLen) {
2014
0
                    fMaxMatchLen = matchLength;
2015
0
                }
2016
0
            }
2017
0
        }
2018
0
    }
2019
0
    return true;
2020
0
}
2021
2022
TimeZoneNames::MatchInfoCollection*
2023
0
TZDBNameSearchHandler::getMatches(int32_t& maxMatchLen) {
2024
    // give the ownership to the caller
2025
0
    TimeZoneNames::MatchInfoCollection* results = fResults;
2026
0
    maxMatchLen = fMaxMatchLen;
2027
2028
    // reset
2029
0
    fResults = nullptr;
2030
0
    fMaxMatchLen = 0;
2031
0
    return results;
2032
0
}
2033
2034
U_CDECL_BEGIN
2035
/**
2036
 * Deleter for TZDBNames
2037
 */
2038
static void U_CALLCONV
2039
0
deleteTZDBNames(void *obj) {
2040
0
    if (obj != EMPTY) {
2041
0
        delete (TZDBNames *)obj;
2042
0
    }
2043
0
}
2044
2045
1
static void U_CALLCONV initTZDBNamesMap(UErrorCode &status) {
2046
1
    gTZDBNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status);
2047
1
    if (U_FAILURE(status)) {
2048
0
        gTZDBNamesMap = nullptr;
2049
0
        return;
2050
0
    }
2051
    // no key deleters for tzdb name maps
2052
1
    uhash_setValueDeleter(gTZDBNamesMap, deleteTZDBNames);
2053
1
    ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup);
2054
1
}
2055
2056
/**
2057
 * Deleter for TZDBNameInfo
2058
 */
2059
static void U_CALLCONV
2060
0
deleteTZDBNameInfo(void *obj) {
2061
0
    if (obj != nullptr) {
2062
0
        uprv_free(obj);
2063
0
    }
2064
0
}
2065
2066
0
static void U_CALLCONV prepareFind(UErrorCode &status) {
2067
0
    if (U_FAILURE(status)) {
2068
0
        return;
2069
0
    }
2070
0
    gTZDBNamesTrie = new TextTrieMap(true, deleteTZDBNameInfo);
2071
0
    if (gTZDBNamesTrie == nullptr) {
2072
0
        status = U_MEMORY_ALLOCATION_ERROR;
2073
0
        return;
2074
0
    }
2075
2076
0
    const UnicodeString *mzID;
2077
0
    StringEnumeration *mzIDs = TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
2078
0
    if (U_SUCCESS(status)) {
2079
0
        while ((mzID = mzIDs->snext(status)) != nullptr && U_SUCCESS(status)) {
2080
0
            const TZDBNames *names = TZDBTimeZoneNames::getMetaZoneNames(*mzID, status);
2081
0
            if (U_FAILURE(status)) {
2082
0
                break;
2083
0
            }
2084
0
            if (names == nullptr) {
2085
0
                continue;
2086
0
            }
2087
0
            const char16_t *std = names->getName(UTZNM_SHORT_STANDARD);
2088
0
            const char16_t *dst = names->getName(UTZNM_SHORT_DAYLIGHT);
2089
0
            if (std == nullptr && dst == nullptr) {
2090
0
                continue;
2091
0
            }
2092
0
            int32_t numRegions = 0;
2093
0
            const char **parseRegions = names->getParseRegions(numRegions);
2094
2095
            // The tz database contains a few zones sharing a
2096
            // same name for both standard time and daylight saving
2097
            // time. For example, Australia/Sydney observes DST,
2098
            // but "EST" is used for both standard and daylight.
2099
            // we need to store the information for later processing.
2100
0
            UBool ambiguousType = (std != nullptr && dst != nullptr && u_strcmp(std, dst) == 0);
2101
2102
0
            const char16_t *uMzID = ZoneMeta::findMetaZoneID(*mzID);
2103
0
            if (std != nullptr) {
2104
0
                TZDBNameInfo *stdInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo));
2105
0
                if (stdInf == nullptr) {
2106
0
                    status = U_MEMORY_ALLOCATION_ERROR;
2107
0
                    break;
2108
0
                }
2109
0
                stdInf->mzID = uMzID;
2110
0
                stdInf->type = UTZNM_SHORT_STANDARD;
2111
0
                stdInf->ambiguousType = ambiguousType;
2112
0
                stdInf->parseRegions = parseRegions;
2113
0
                stdInf->nRegions = numRegions;
2114
0
                gTZDBNamesTrie->put(std, stdInf, status);
2115
0
            }
2116
0
            if (U_SUCCESS(status) && dst != nullptr) {
2117
0
                TZDBNameInfo *dstInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo));
2118
0
                if (dstInf == nullptr) {
2119
0
                    status = U_MEMORY_ALLOCATION_ERROR;
2120
0
                    break;
2121
0
                }
2122
0
                dstInf->mzID = uMzID;
2123
0
                dstInf->type = UTZNM_SHORT_DAYLIGHT;
2124
0
                dstInf->ambiguousType = ambiguousType;
2125
0
                dstInf->parseRegions = parseRegions;
2126
0
                dstInf->nRegions = numRegions;
2127
0
                gTZDBNamesTrie->put(dst, dstInf, status);
2128
0
            }
2129
0
        }
2130
0
    }
2131
0
    delete mzIDs;
2132
2133
0
    if (U_FAILURE(status)) {
2134
0
        delete gTZDBNamesTrie;
2135
0
        gTZDBNamesTrie = nullptr;
2136
0
        return;
2137
0
    }
2138
2139
0
    ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup);
2140
0
}
2141
2142
U_CDECL_END
2143
2144
TZDBTimeZoneNames::TZDBTimeZoneNames(const Locale& locale)
2145
1.35k
: fLocale(locale), fRegion() {
2146
1.35k
    UBool useWorld = true;
2147
1.35k
    const char* region = fLocale.getCountry();
2148
1.35k
    int32_t regionLen = static_cast<int32_t>(uprv_strlen(region));
2149
1.35k
    if (regionLen == 0) {
2150
436
        UErrorCode status = U_ZERO_ERROR;
2151
436
        CharString loc = ulocimp_addLikelySubtags(fLocale.getName(), status);
2152
436
        ulocimp_getSubtags(loc.toStringPiece(), nullptr, nullptr, &fRegion, nullptr, nullptr, status);
2153
436
        if (U_SUCCESS(status)) {
2154
436
            useWorld = false;
2155
436
        }
2156
915
    } else {
2157
915
        UErrorCode status = U_ZERO_ERROR;
2158
915
        fRegion.append(region, regionLen, status);
2159
915
        U_ASSERT(U_SUCCESS(status));
2160
915
        useWorld = false;
2161
915
    }
2162
1.35k
    if (useWorld) {
2163
0
        UErrorCode status = U_ZERO_ERROR;
2164
0
        fRegion.append("001", status);
2165
0
        U_ASSERT(U_SUCCESS(status));
2166
0
    }
2167
1.35k
}
2168
2169
1.35k
TZDBTimeZoneNames::~TZDBTimeZoneNames() {
2170
1.35k
}
2171
2172
bool
2173
0
TZDBTimeZoneNames::operator==(const TimeZoneNames& other) const {
2174
0
    if (this == &other) {
2175
0
        return true;
2176
0
    }
2177
    // No implementation for now
2178
0
    return false;
2179
0
}
2180
2181
TZDBTimeZoneNames*
2182
0
TZDBTimeZoneNames::clone() const {
2183
0
    return new TZDBTimeZoneNames(fLocale);
2184
0
}
2185
2186
StringEnumeration*
2187
1.35k
TZDBTimeZoneNames::getAvailableMetaZoneIDs(UErrorCode& status) const {
2188
1.35k
    return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
2189
1.35k
}
2190
2191
StringEnumeration*
2192
1.35k
TZDBTimeZoneNames::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
2193
1.35k
    return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status);
2194
1.35k
}
2195
2196
UnicodeString&
2197
2.70k
TZDBTimeZoneNames::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
2198
2.70k
    return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID);
2199
2.70k
}
2200
2201
UnicodeString&
2202
0
TZDBTimeZoneNames::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
2203
0
    return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID);
2204
0
}
2205
2206
UnicodeString&
2207
TZDBTimeZoneNames::getMetaZoneDisplayName(const UnicodeString& mzID,
2208
                                          UTimeZoneNameType type,
2209
2.70k
                                          UnicodeString& name) const {
2210
2.70k
    name.setToBogus();
2211
2.70k
    if (mzID.isEmpty()) {
2212
1.34k
        return name;
2213
1.34k
    }
2214
2215
1.35k
    UErrorCode status = U_ZERO_ERROR;
2216
1.35k
    const TZDBNames *tzdbNames = TZDBTimeZoneNames::getMetaZoneNames(mzID, status);
2217
1.35k
    if (U_SUCCESS(status)) {
2218
614
        if (tzdbNames != nullptr) {
2219
181
            const char16_t *s = tzdbNames->getName(type);
2220
181
            if (s != nullptr) {
2221
8
                name.setTo(true, s, -1);
2222
8
            }
2223
181
        }
2224
614
    }
2225
2226
1.35k
    return name;
2227
2.70k
}
2228
2229
UnicodeString&
2230
2.70k
TZDBTimeZoneNames::getTimeZoneDisplayName(const UnicodeString& /* tzID */, UTimeZoneNameType /* type */, UnicodeString& name) const {
2231
    // No abbreviations associated a zone directly for now.
2232
2.70k
    name.setToBogus();
2233
2.70k
    return name;
2234
2.70k
}
2235
2236
TZDBTimeZoneNames::MatchInfoCollection*
2237
0
TZDBTimeZoneNames::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
2238
0
    umtx_initOnce(gTZDBNamesTrieInitOnce, &prepareFind, status);
2239
0
    if (U_FAILURE(status)) {
2240
0
        return nullptr;
2241
0
    }
2242
2243
0
    TZDBNameSearchHandler handler(types, fRegion.toStringPiece());
2244
0
    gTZDBNamesTrie->search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
2245
0
    if (U_FAILURE(status)) {
2246
0
        return nullptr;
2247
0
    }
2248
0
    int32_t maxLen = 0;
2249
0
    return handler.getMatches(maxLen);
2250
0
}
2251
2252
const TZDBNames*
2253
1.35k
TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) {
2254
1.35k
    umtx_initOnce(gTZDBNamesMapInitOnce, &initTZDBNamesMap, status);
2255
1.35k
    if (U_FAILURE(status)) {
2256
0
        return nullptr;
2257
0
    }
2258
2259
1.35k
    TZDBNames* tzdbNames = nullptr;
2260
2261
1.35k
    char16_t mzIDKey[ZID_KEY_MAX + 1];
2262
1.35k
    mzID.extract(mzIDKey, ZID_KEY_MAX, status);
2263
1.35k
    if (U_FAILURE(status)) {
2264
408
        return nullptr;
2265
408
    }
2266
946
    mzIDKey[mzID.length()] = 0;
2267
946
    if (!uprv_isInvariantUString(mzIDKey, mzID.length())) {
2268
327
        status = U_ILLEGAL_ARGUMENT_ERROR;
2269
327
        return nullptr;
2270
327
    }
2271
2272
619
    static UMutex gTZDBNamesMapLock;
2273
619
    umtx_lock(&gTZDBNamesMapLock);
2274
619
    {
2275
619
        void *cacheVal = uhash_get(gTZDBNamesMap, mzIDKey);
2276
619
        if (cacheVal == nullptr) {
2277
488
            UResourceBundle *zoneStringsRes = ures_openDirect(U_ICUDATA_ZONE, "tzdbNames", &status);
2278
488
            zoneStringsRes = ures_getByKey(zoneStringsRes, gZoneStrings, zoneStringsRes, &status);
2279
488
            char key[ZID_KEY_MAX + 1];
2280
488
            mergeTimeZoneKey(mzID, key, sizeof(key), status);
2281
488
            if (U_SUCCESS(status)) {
2282
483
                tzdbNames = TZDBNames::createInstance(zoneStringsRes, key);
2283
2284
483
                if (tzdbNames == nullptr) {
2285
427
                    cacheVal = (void *)EMPTY;
2286
427
                } else {
2287
56
                    cacheVal = tzdbNames;
2288
56
                }
2289
                // Use the persistent ID as the resource key, so we can
2290
                // avoid duplications.
2291
                // TODO: Is there a more efficient way, like intern() in Java?
2292
483
                void* newKey = (void*) ZoneMeta::findMetaZoneID(mzID);
2293
483
                if (newKey != nullptr) {
2294
55
                    uhash_put(gTZDBNamesMap, newKey, cacheVal, &status);
2295
55
                    if (U_FAILURE(status)) {
2296
0
                        if (tzdbNames != nullptr) {
2297
0
                            delete tzdbNames;
2298
0
                            tzdbNames = nullptr;
2299
0
                        }
2300
0
                    }
2301
428
                } else {
2302
                    // Should never happen with a valid input
2303
428
                    if (tzdbNames != nullptr) {
2304
                        // It's not possible that we get a valid tzdbNames with unknown ID.
2305
                        // But just in case..
2306
3
                        delete tzdbNames;
2307
3
                        tzdbNames = nullptr;
2308
3
                    }
2309
428
                }
2310
483
            }
2311
488
            ures_close(zoneStringsRes);
2312
488
        } else if (cacheVal != EMPTY) {
2313
128
            tzdbNames = static_cast<TZDBNames*>(cacheVal);
2314
128
        }
2315
619
    }
2316
619
    umtx_unlock(&gTZDBNamesMapLock);
2317
2318
619
    return tzdbNames;
2319
946
}
2320
2321
U_NAMESPACE_END
2322
2323
2324
#endif /* #if !UCONFIG_NO_FORMATTING */
2325
2326
//eof