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