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