Coverage Report

Created: 2018-09-25 14:53

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