Coverage Report

Created: 2025-06-24 06:43

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