• 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);
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);
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         uKey.extract(0, uKey.length(), key, sizeof(key), US_INV);
774 
775         loadNames(zoneStrings, key, errorCode);
776     }
777 
loadNamesZNames::ZNamesLoader778     void loadNames(const UResourceBundle* zoneStrings, const char* key, UErrorCode& errorCode) {
779         U_ASSERT(zoneStrings != NULL);
780         U_ASSERT(key != NULL);
781         U_ASSERT(key[0] != '\0');
782 
783         UErrorCode localStatus = U_ZERO_ERROR;
784         clear();
785         ures_getAllItemsWithFallback(zoneStrings, key, *this, localStatus);
786 
787         // Ignore errors, but propagate possible warnings.
788         if (U_SUCCESS(localStatus)) {
789             errorCode = localStatus;
790         }
791     }
792 
setNameIfEmptyZNames::ZNamesLoader793     void setNameIfEmpty(const char* key, const ResourceValue* value, UErrorCode& errorCode) {
794         UTimeZoneNameTypeIndex type = nameTypeFromKey(key);
795         if (type == UTZNM_INDEX_UNKNOWN) { return; }
796         if (names[type] == NULL) {
797             int32_t length;
798             // 'NO_NAME' indicates internally that this field should remain empty.  It will be
799             // replaced by 'NULL' in getNames()
800             names[type] = (value == NULL) ? NO_NAME : value->getString(length, errorCode);
801         }
802     }
803 
putZNames::ZNamesLoader804     virtual void put(const char* key, ResourceValue& value, UBool /*noFallback*/,
805             UErrorCode &errorCode) override {
806         ResourceTable namesTable = value.getTable(errorCode);
807         if (U_FAILURE(errorCode)) { return; }
808         for (int32_t i = 0; namesTable.getKeyAndValue(i, key, value); ++i) {
809             if (value.isNoInheritanceMarker()) {
810                 setNameIfEmpty(key, NULL, errorCode);
811             } else {
812                 setNameIfEmpty(key, &value, errorCode);
813             }
814         }
815     }
816 
nameTypeFromKeyZNames::ZNamesLoader817     static UTimeZoneNameTypeIndex nameTypeFromKey(const char *key) {
818         char c0, c1;
819         if ((c0 = key[0]) == 0 || (c1 = key[1]) == 0 || key[2] != 0) {
820             return UTZNM_INDEX_UNKNOWN;
821         }
822         if (c0 == 'l') {
823             return c1 == 'g' ? UTZNM_INDEX_LONG_GENERIC :
824                     c1 == 's' ? UTZNM_INDEX_LONG_STANDARD :
825                         c1 == 'd' ? UTZNM_INDEX_LONG_DAYLIGHT : UTZNM_INDEX_UNKNOWN;
826         } else if (c0 == 's') {
827             return c1 == 'g' ? UTZNM_INDEX_SHORT_GENERIC :
828                     c1 == 's' ? UTZNM_INDEX_SHORT_STANDARD :
829                         c1 == 'd' ? UTZNM_INDEX_SHORT_DAYLIGHT : UTZNM_INDEX_UNKNOWN;
830         } else if (c0 == 'e' && c1 == 'c') {
831             return UTZNM_INDEX_EXEMPLAR_LOCATION;
832         }
833         return UTZNM_INDEX_UNKNOWN;
834     }
835 
836     /**
837     * Returns an array of names.  It is the caller's responsibility to copy the data into a
838     * permanent location, as the returned array is owned by the loader instance and may be
839     * cleared or leave scope.
840     *
841     * This is different than Java, where the array will no longer be modified and null
842     * may be returned.
843     */
getNamesZNames::ZNamesLoader844     const UChar** getNames() {
845         // Remove 'NO_NAME' references in the array and replace with 'NULL'
846         for (int32_t i = 0; i < UTZNM_INDEX_COUNT; ++i) {
847             if (names[i] == NO_NAME) {
848                 names[i] = NULL;
849             }
850         }
851         return names;
852     }
853 };
854 
~ZNamesLoader()855 ZNames::ZNamesLoader::~ZNamesLoader() {}
856 
857 
858 // ---------------------------------------------------
859 // The meta zone ID enumeration class
860 // ---------------------------------------------------
861 class MetaZoneIDsEnumeration : public StringEnumeration {
862 public:
863     MetaZoneIDsEnumeration();
864     MetaZoneIDsEnumeration(const UVector& mzIDs);
865     MetaZoneIDsEnumeration(LocalPointer<UVector> mzIDs);
866     virtual ~MetaZoneIDsEnumeration();
867     static UClassID U_EXPORT2 getStaticClassID(void);
868     virtual UClassID getDynamicClassID(void) const override;
869     virtual const UnicodeString* snext(UErrorCode& status) override;
870     virtual void reset(UErrorCode& status) override;
871     virtual int32_t count(UErrorCode& status) const override;
872 private:
873     int32_t fLen;
874     int32_t fPos;
875     const UVector* fMetaZoneIDs;
876     LocalPointer<UVector> fLocalVector;
877 };
878 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration)879 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration)
880 
881 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration()
882 : fLen(0), fPos(0), fMetaZoneIDs(NULL), fLocalVector(NULL) {
883 }
884 
MetaZoneIDsEnumeration(const UVector & mzIDs)885 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector& mzIDs)
886 : fPos(0), fMetaZoneIDs(&mzIDs), fLocalVector(NULL) {
887     fLen = fMetaZoneIDs->size();
888 }
889 
MetaZoneIDsEnumeration(LocalPointer<UVector> mzIDs)890 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(LocalPointer<UVector> mzIDs)
891 : fLen(0), fPos(0), fMetaZoneIDs(nullptr), fLocalVector(std::move(mzIDs)) {
892     fMetaZoneIDs = fLocalVector.getAlias();
893     if (fMetaZoneIDs) {
894         fLen = fMetaZoneIDs->size();
895     }
896 }
897 
898 const UnicodeString*
snext(UErrorCode & status)899 MetaZoneIDsEnumeration::snext(UErrorCode& status) {
900     if (U_SUCCESS(status) && fMetaZoneIDs != NULL && fPos < fLen) {
901         unistr.setTo((const UChar*)fMetaZoneIDs->elementAt(fPos++), -1);
902         return &unistr;
903     }
904     return NULL;
905 }
906 
907 void
reset(UErrorCode &)908 MetaZoneIDsEnumeration::reset(UErrorCode& /*status*/) {
909     fPos = 0;
910 }
911 
912 int32_t
count(UErrorCode &) const913 MetaZoneIDsEnumeration::count(UErrorCode& /*status*/) const {
914     return fLen;
915 }
916 
~MetaZoneIDsEnumeration()917 MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
918 }
919 
920 
921 // ---------------------------------------------------
922 // ZNameSearchHandler
923 // ---------------------------------------------------
924 class ZNameSearchHandler : public TextTrieMapSearchResultHandler {
925 public:
926     ZNameSearchHandler(uint32_t types);
927     virtual ~ZNameSearchHandler();
928 
929     UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) override;
930     TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen);
931 
932 private:
933     uint32_t fTypes;
934     int32_t fMaxMatchLen;
935     TimeZoneNames::MatchInfoCollection* fResults;
936 };
937 
ZNameSearchHandler(uint32_t types)938 ZNameSearchHandler::ZNameSearchHandler(uint32_t types)
939 : fTypes(types), fMaxMatchLen(0), fResults(NULL) {
940 }
941 
~ZNameSearchHandler()942 ZNameSearchHandler::~ZNameSearchHandler() {
943     if (fResults != NULL) {
944         delete fResults;
945     }
946 }
947 
948 UBool
handleMatch(int32_t matchLength,const CharacterNode * node,UErrorCode & status)949 ZNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
950     if (U_FAILURE(status)) {
951         return false;
952     }
953     if (node->hasValues()) {
954         int32_t valuesCount = node->countValues();
955         for (int32_t i = 0; i < valuesCount; i++) {
956             ZNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
957             if (nameinfo == NULL) {
958                 continue;
959             }
960             if ((nameinfo->type & fTypes) != 0) {
961                 // matches a requested type
962                 if (fResults == NULL) {
963                     fResults = new TimeZoneNames::MatchInfoCollection();
964                     if (fResults == NULL) {
965                         status = U_MEMORY_ALLOCATION_ERROR;
966                     }
967                 }
968                 if (U_SUCCESS(status)) {
969                     U_ASSERT(fResults != NULL);
970                     if (nameinfo->tzID) {
971                         fResults->addZone(nameinfo->type, matchLength, UnicodeString(nameinfo->tzID, -1), status);
972                     } else {
973                         U_ASSERT(nameinfo->mzID);
974                         fResults->addMetaZone(nameinfo->type, matchLength, UnicodeString(nameinfo->mzID, -1), status);
975                     }
976                     if (U_SUCCESS(status) && matchLength > fMaxMatchLen) {
977                         fMaxMatchLen = matchLength;
978                     }
979                 }
980             }
981         }
982     }
983     return true;
984 }
985 
986 TimeZoneNames::MatchInfoCollection*
getMatches(int32_t & maxMatchLen)987 ZNameSearchHandler::getMatches(int32_t& maxMatchLen) {
988     // give the ownership to the caller
989     TimeZoneNames::MatchInfoCollection* results = fResults;
990     maxMatchLen = fMaxMatchLen;
991 
992     // reset
993     fResults = NULL;
994     fMaxMatchLen = 0;
995     return results;
996 }
997 
998 // ---------------------------------------------------
999 // TimeZoneNamesImpl
1000 //
1001 // TimeZoneNames implementation class. This is the main
1002 // part of this module.
1003 // ---------------------------------------------------
1004 
1005 U_CDECL_BEGIN
1006 /**
1007  * Deleter for ZNames
1008  */
1009 static void U_CALLCONV
deleteZNames(void * obj)1010 deleteZNames(void *obj) {
1011     if (obj != EMPTY) {
1012         delete (ZNames*) obj;
1013     }
1014 }
1015 
1016 /**
1017  * Deleter for ZNameInfo
1018  */
1019 static void U_CALLCONV
deleteZNameInfo(void * obj)1020 deleteZNameInfo(void *obj) {
1021     uprv_free(obj);
1022 }
1023 
1024 U_CDECL_END
1025 
TimeZoneNamesImpl(const Locale & locale,UErrorCode & status)1026 TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status)
1027 : fLocale(locale),
1028   fZoneStrings(NULL),
1029   fTZNamesMap(NULL),
1030   fMZNamesMap(NULL),
1031   fNamesTrieFullyLoaded(false),
1032   fNamesFullyLoaded(false),
1033   fNamesTrie(true, deleteZNameInfo) {
1034     initialize(locale, status);
1035 }
1036 
1037 void
initialize(const Locale & locale,UErrorCode & status)1038 TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) {
1039     if (U_FAILURE(status)) {
1040         return;
1041     }
1042 
1043     // Load zoneStrings bundle
1044     UErrorCode tmpsts = U_ZERO_ERROR;   // OK with fallback warning..
1045     fZoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
1046     fZoneStrings = ures_getByKeyWithFallback(fZoneStrings, gZoneStrings, fZoneStrings, &tmpsts);
1047     if (U_FAILURE(tmpsts)) {
1048         status = tmpsts;
1049         cleanup();
1050         return;
1051     }
1052 
1053     // Initialize hashtables holding time zone/meta zone names
1054     fMZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
1055     fTZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
1056     if (U_FAILURE(status)) {
1057         cleanup();
1058         return;
1059     }
1060 
1061     uhash_setValueDeleter(fMZNamesMap, deleteZNames);
1062     uhash_setValueDeleter(fTZNamesMap, deleteZNames);
1063     // no key deleters for name maps
1064 
1065     // preload zone strings for the default zone
1066     TimeZone *tz = TimeZone::createDefault();
1067     const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
1068     if (tzID != NULL) {
1069         loadStrings(UnicodeString(tzID), status);
1070     }
1071     delete tz;
1072 
1073     return;
1074 }
1075 
1076 /*
1077  * This method updates the cache and must be called with a lock,
1078  * except initializer.
1079  */
1080 void
loadStrings(const UnicodeString & tzCanonicalID,UErrorCode & status)1081 TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID, UErrorCode& status) {
1082     loadTimeZoneNames(tzCanonicalID, status);
1083     LocalPointer<StringEnumeration> mzIDs(getAvailableMetaZoneIDs(tzCanonicalID, status));
1084     if (U_FAILURE(status)) { return; }
1085     U_ASSERT(!mzIDs.isNull());
1086 
1087     const UnicodeString *mzID;
1088     while (((mzID = mzIDs->snext(status)) != NULL) && U_SUCCESS(status)) {
1089         loadMetaZoneNames(*mzID, status);
1090     }
1091 }
1092 
~TimeZoneNamesImpl()1093 TimeZoneNamesImpl::~TimeZoneNamesImpl() {
1094     cleanup();
1095 }
1096 
1097 void
cleanup()1098 TimeZoneNamesImpl::cleanup() {
1099     if (fZoneStrings != NULL) {
1100         ures_close(fZoneStrings);
1101         fZoneStrings = NULL;
1102     }
1103     if (fMZNamesMap != NULL) {
1104         uhash_close(fMZNamesMap);
1105         fMZNamesMap = NULL;
1106     }
1107     if (fTZNamesMap != NULL) {
1108         uhash_close(fTZNamesMap);
1109         fTZNamesMap = NULL;
1110     }
1111 }
1112 
1113 bool
operator ==(const TimeZoneNames & other) const1114 TimeZoneNamesImpl::operator==(const TimeZoneNames& other) const {
1115     if (this == &other) {
1116         return true;
1117     }
1118     // No implementation for now
1119     return false;
1120 }
1121 
1122 TimeZoneNamesImpl*
clone() const1123 TimeZoneNamesImpl::clone() const {
1124     UErrorCode status = U_ZERO_ERROR;
1125     return new TimeZoneNamesImpl(fLocale, status);
1126 }
1127 
1128 StringEnumeration*
getAvailableMetaZoneIDs(UErrorCode & status) const1129 TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const {
1130     return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
1131 }
1132 
1133 // static implementation of getAvailableMetaZoneIDs(UErrorCode&)
1134 StringEnumeration*
_getAvailableMetaZoneIDs(UErrorCode & status)1135 TimeZoneNamesImpl::_getAvailableMetaZoneIDs(UErrorCode& status) {
1136     if (U_FAILURE(status)) {
1137         return NULL;
1138     }
1139     const UVector* mzIDs = ZoneMeta::getAvailableMetazoneIDs();
1140     if (mzIDs == NULL) {
1141         return new MetaZoneIDsEnumeration();
1142     }
1143     return new MetaZoneIDsEnumeration(*mzIDs);
1144 }
1145 
1146 StringEnumeration*
getAvailableMetaZoneIDs(const UnicodeString & tzID,UErrorCode & status) const1147 TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
1148     return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status);
1149 }
1150 
1151 // static implementation of getAvailableMetaZoneIDs(const UnicodeString&, UErrorCode&)
1152 StringEnumeration*
_getAvailableMetaZoneIDs(const UnicodeString & tzID,UErrorCode & status)1153 TimeZoneNamesImpl::_getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) {
1154     if (U_FAILURE(status)) {
1155         return NULL;
1156     }
1157     const UVector* mappings = ZoneMeta::getMetazoneMappings(tzID);
1158     if (mappings == NULL) {
1159         return new MetaZoneIDsEnumeration();
1160     }
1161 
1162     LocalPointer<MetaZoneIDsEnumeration> senum;
1163     LocalPointer<UVector> mzIDs(new UVector(NULL, uhash_compareUChars, status), status);
1164     if (U_SUCCESS(status)) {
1165         U_ASSERT(mzIDs.isValid());
1166         for (int32_t i = 0; U_SUCCESS(status) && i < mappings->size(); i++) {
1167 
1168             OlsonToMetaMappingEntry *map = (OlsonToMetaMappingEntry *)mappings->elementAt(i);
1169             const UChar *mzID = map->mzid;
1170             if (!mzIDs->contains((void *)mzID)) {
1171                 mzIDs->addElement((void *)mzID, status);
1172             }
1173         }
1174         if (U_SUCCESS(status)) {
1175             senum.adoptInsteadAndCheckErrorCode(new MetaZoneIDsEnumeration(std::move(mzIDs)), status);
1176         }
1177     }
1178     return U_SUCCESS(status) ? senum.orphan() : nullptr;
1179 }
1180 
1181 UnicodeString&
getMetaZoneID(const UnicodeString & tzID,UDate date,UnicodeString & mzID) const1182 TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
1183     return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID);
1184 }
1185 
1186 // static implementation of getMetaZoneID
1187 UnicodeString&
_getMetaZoneID(const UnicodeString & tzID,UDate date,UnicodeString & mzID)1188 TimeZoneNamesImpl::_getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) {
1189     ZoneMeta::getMetazoneID(tzID, date, mzID);
1190     return mzID;
1191 }
1192 
1193 UnicodeString&
getReferenceZoneID(const UnicodeString & mzID,const char * region,UnicodeString & tzID) const1194 TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
1195     return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID);
1196 }
1197 
1198 // static implementation of getReferenceZoneID
1199 UnicodeString&
_getReferenceZoneID(const UnicodeString & mzID,const char * region,UnicodeString & tzID)1200 TimeZoneNamesImpl::_getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) {
1201     ZoneMeta::getZoneIdByMetazone(mzID, UnicodeString(region, -1, US_INV), tzID);
1202     return tzID;
1203 }
1204 
1205 UnicodeString&
getMetaZoneDisplayName(const UnicodeString & mzID,UTimeZoneNameType type,UnicodeString & name) const1206 TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID,
1207                                           UTimeZoneNameType type,
1208                                           UnicodeString& name) const {
1209     name.setToBogus();  // cleanup result.
1210     if (mzID.isEmpty()) {
1211         return name;
1212     }
1213 
1214     ZNames *znames = NULL;
1215     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1216 
1217     {
1218         Mutex lock(&gDataMutex);
1219         UErrorCode status = U_ZERO_ERROR;
1220         znames = nonConstThis->loadMetaZoneNames(mzID, status);
1221         if (U_FAILURE(status)) { return name; }
1222     }
1223 
1224     if (znames != NULL) {
1225         const UChar* s = znames->getName(type);
1226         if (s != NULL) {
1227             name.setTo(true, s, -1);
1228         }
1229     }
1230     return name;
1231 }
1232 
1233 UnicodeString&
getTimeZoneDisplayName(const UnicodeString & tzID,UTimeZoneNameType type,UnicodeString & name) const1234 TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
1235     name.setToBogus();  // cleanup result.
1236     if (tzID.isEmpty()) {
1237         return name;
1238     }
1239 
1240     ZNames *tznames = NULL;
1241     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1242 
1243     {
1244         Mutex lock(&gDataMutex);
1245         UErrorCode status = U_ZERO_ERROR;
1246         tznames = nonConstThis->loadTimeZoneNames(tzID, status);
1247         if (U_FAILURE(status)) { return name; }
1248     }
1249 
1250     if (tznames != NULL) {
1251         const UChar *s = tznames->getName(type);
1252         if (s != NULL) {
1253             name.setTo(true, s, -1);
1254         }
1255     }
1256     return name;
1257 }
1258 
1259 UnicodeString&
getExemplarLocationName(const UnicodeString & tzID,UnicodeString & name) const1260 TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
1261     name.setToBogus();  // cleanup result.
1262     const UChar* locName = NULL;
1263     ZNames *tznames = NULL;
1264     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1265 
1266     {
1267         Mutex lock(&gDataMutex);
1268         UErrorCode status = U_ZERO_ERROR;
1269         tznames = nonConstThis->loadTimeZoneNames(tzID, status);
1270         if (U_FAILURE(status)) { return name; }
1271     }
1272 
1273     if (tznames != NULL) {
1274         locName = tznames->getName(UTZNM_EXEMPLAR_LOCATION);
1275     }
1276     if (locName != NULL) {
1277         name.setTo(true, locName, -1);
1278     }
1279 
1280     return name;
1281 }
1282 
1283 
1284 // Merge the MZ_PREFIX and mzId
mergeTimeZoneKey(const UnicodeString & mzID,char * result)1285 static void mergeTimeZoneKey(const UnicodeString& mzID, char* result) {
1286     if (mzID.isEmpty()) {
1287         result[0] = '\0';
1288         return;
1289     }
1290 
1291     char mzIdChar[ZID_KEY_MAX + 1];
1292     int32_t keyLen;
1293     int32_t prefixLen = static_cast<int32_t>(uprv_strlen(gMZPrefix));
1294     keyLen = mzID.extract(0, mzID.length(), mzIdChar, ZID_KEY_MAX + 1, US_INV);
1295     uprv_memcpy((void *)result, (void *)gMZPrefix, prefixLen);
1296     uprv_memcpy((void *)(result + prefixLen), (void *)mzIdChar, keyLen);
1297     result[keyLen + prefixLen] = '\0';
1298 }
1299 
1300 /*
1301  * This method updates the cache and must be called with a lock
1302  */
1303 ZNames*
loadMetaZoneNames(const UnicodeString & mzID,UErrorCode & status)1304 TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) {
1305     if (U_FAILURE(status)) { return NULL; }
1306     U_ASSERT(mzID.length() <= ZID_KEY_MAX - MZ_PREFIX_LEN);
1307 
1308     UChar mzIDKey[ZID_KEY_MAX + 1];
1309     mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status);
1310     U_ASSERT(U_SUCCESS(status));   // already checked length above
1311     mzIDKey[mzID.length()] = 0;
1312 
1313     void* mznames = uhash_get(fMZNamesMap, mzIDKey);
1314     if (mznames == NULL) {
1315         ZNames::ZNamesLoader loader;
1316         loader.loadMetaZone(fZoneStrings, mzID, status);
1317         mznames = ZNames::createMetaZoneAndPutInCache(fMZNamesMap, loader.getNames(), mzID, status);
1318         if (U_FAILURE(status)) { return NULL; }
1319     }
1320 
1321     if (mznames != EMPTY) {
1322         return (ZNames*)mznames;
1323     } else {
1324         return NULL;
1325     }
1326 }
1327 
1328 /*
1329  * This method updates the cache and must be called with a lock
1330  */
1331 ZNames*
loadTimeZoneNames(const UnicodeString & tzID,UErrorCode & status)1332 TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID, UErrorCode& status) {
1333     if (U_FAILURE(status)) { return NULL; }
1334     U_ASSERT(tzID.length() <= ZID_KEY_MAX);
1335 
1336     UChar tzIDKey[ZID_KEY_MAX + 1];
1337     int32_t tzIDKeyLen = tzID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
1338     U_ASSERT(U_SUCCESS(status));   // already checked length above
1339     tzIDKey[tzIDKeyLen] = 0;
1340 
1341     void *tznames = uhash_get(fTZNamesMap, tzIDKey);
1342     if (tznames == NULL) {
1343         ZNames::ZNamesLoader loader;
1344         loader.loadTimeZone(fZoneStrings, tzID, status);
1345         tznames = ZNames::createTimeZoneAndPutInCache(fTZNamesMap, loader.getNames(), tzID, status);
1346         if (U_FAILURE(status)) { return NULL; }
1347     }
1348 
1349     // tznames is never EMPTY
1350     return (ZNames*)tznames;
1351 }
1352 
1353 TimeZoneNames::MatchInfoCollection*
find(const UnicodeString & text,int32_t start,uint32_t types,UErrorCode & status) const1354 TimeZoneNamesImpl::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
1355     ZNameSearchHandler handler(types);
1356     TimeZoneNames::MatchInfoCollection* matches;
1357     TimeZoneNamesImpl* nonConstThis = const_cast<TimeZoneNamesImpl*>(this);
1358 
1359     // Synchronize so that data is not loaded multiple times.
1360     // TODO: Consider more fine-grained synchronization.
1361     {
1362         Mutex lock(&gDataMutex);
1363 
1364         // First try of lookup.
1365         matches = doFind(handler, text, start, status);
1366         if (U_FAILURE(status)) { return NULL; }
1367         if (matches != NULL) {
1368             return matches;
1369         }
1370 
1371         // All names are not yet loaded into the trie.
1372         // We may have loaded names for formatting several time zones,
1373         // and might be parsing one of those.
1374         // Populate the parsing trie from all of the already-loaded names.
1375         nonConstThis->addAllNamesIntoTrie(status);
1376 
1377         // Second try of lookup.
1378         matches = doFind(handler, text, start, status);
1379         if (U_FAILURE(status)) { return NULL; }
1380         if (matches != NULL) {
1381             return matches;
1382         }
1383 
1384         // There are still some names we haven't loaded into the trie yet.
1385         // Load everything now.
1386         nonConstThis->internalLoadAllDisplayNames(status);
1387         nonConstThis->addAllNamesIntoTrie(status);
1388         nonConstThis->fNamesTrieFullyLoaded = true;
1389         if (U_FAILURE(status)) { return NULL; }
1390 
1391         // Third try: we must return this one.
1392         return doFind(handler, text, start, status);
1393     }
1394 }
1395 
1396 TimeZoneNames::MatchInfoCollection*
doFind(ZNameSearchHandler & handler,const UnicodeString & text,int32_t start,UErrorCode & status) const1397 TimeZoneNamesImpl::doFind(ZNameSearchHandler& handler,
1398         const UnicodeString& text, int32_t start, UErrorCode& status) const {
1399 
1400     fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
1401     if (U_FAILURE(status)) { return NULL; }
1402 
1403     int32_t maxLen = 0;
1404     TimeZoneNames::MatchInfoCollection* matches = handler.getMatches(maxLen);
1405     if (matches != NULL && ((maxLen == (text.length() - start)) || fNamesTrieFullyLoaded)) {
1406         // perfect match, or no more names available
1407         return matches;
1408     }
1409     delete matches;
1410     return NULL;
1411 }
1412 
1413 // Caller must synchronize.
addAllNamesIntoTrie(UErrorCode & status)1414 void TimeZoneNamesImpl::addAllNamesIntoTrie(UErrorCode& status) {
1415     if (U_FAILURE(status)) return;
1416     int32_t pos;
1417     const UHashElement* element;
1418 
1419     pos = UHASH_FIRST;
1420     while ((element = uhash_nextElement(fMZNamesMap, &pos)) != NULL) {
1421         if (element->value.pointer == EMPTY) { continue; }
1422         UChar* mzID = (UChar*) element->key.pointer;
1423         ZNames* znames = (ZNames*) element->value.pointer;
1424         znames->addAsMetaZoneIntoTrie(mzID, fNamesTrie, status);
1425         if (U_FAILURE(status)) { return; }
1426     }
1427 
1428     pos = UHASH_FIRST;
1429     while ((element = uhash_nextElement(fTZNamesMap, &pos)) != NULL) {
1430         if (element->value.pointer == EMPTY) { continue; }
1431         UChar* tzID = (UChar*) element->key.pointer;
1432         ZNames* znames = (ZNames*) element->value.pointer;
1433         znames->addAsTimeZoneIntoTrie(tzID, fNamesTrie, status);
1434         if (U_FAILURE(status)) { return; }
1435     }
1436 }
1437 
1438 U_CDECL_BEGIN
1439 static void U_CALLCONV
deleteZNamesLoader(void * obj)1440 deleteZNamesLoader(void* obj) {
1441     if (obj == DUMMY_LOADER) { return; }
1442     const ZNames::ZNamesLoader* loader = (const ZNames::ZNamesLoader*) obj;
1443     delete loader;
1444 }
1445 U_CDECL_END
1446 
1447 struct TimeZoneNamesImpl::ZoneStringsLoader : public ResourceSink {
1448     TimeZoneNamesImpl& tzn;
1449     UHashtable* keyToLoader;
1450 
ZoneStringsLoaderTimeZoneNamesImpl::ZoneStringsLoader1451     ZoneStringsLoader(TimeZoneNamesImpl& _tzn, UErrorCode& status)
1452             : tzn(_tzn) {
1453         keyToLoader = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
1454         if (U_FAILURE(status)) { return; }
1455         uhash_setKeyDeleter(keyToLoader, uprv_free);
1456         uhash_setValueDeleter(keyToLoader, deleteZNamesLoader);
1457     }
1458     virtual ~ZoneStringsLoader();
1459 
createKeyTimeZoneNamesImpl::ZoneStringsLoader1460     void* createKey(const char* key, UErrorCode& status) {
1461         int32_t len = sizeof(char) * (static_cast<int32_t>(uprv_strlen(key)) + 1);
1462         char* newKey = (char*) uprv_malloc(len);
1463         if (newKey == NULL) {
1464             status = U_MEMORY_ALLOCATION_ERROR;
1465             return NULL;
1466         }
1467         uprv_memcpy(newKey, key, len);
1468         newKey[len-1] = '\0';
1469         return (void*) newKey;
1470     }
1471 
isMetaZoneTimeZoneNamesImpl::ZoneStringsLoader1472     UBool isMetaZone(const char* key) {
1473         return (uprv_strlen(key) >= MZ_PREFIX_LEN && uprv_memcmp(key, gMZPrefix, MZ_PREFIX_LEN) == 0);
1474     }
1475 
mzIDFromKeyTimeZoneNamesImpl::ZoneStringsLoader1476     UnicodeString mzIDFromKey(const char* key) {
1477         return UnicodeString(key + MZ_PREFIX_LEN, static_cast<int32_t>(uprv_strlen(key)) - MZ_PREFIX_LEN, US_INV);
1478     }
1479 
tzIDFromKeyTimeZoneNamesImpl::ZoneStringsLoader1480     UnicodeString tzIDFromKey(const char* key) {
1481         UnicodeString tzID(key, -1, US_INV);
1482         // Replace all colons ':' with slashes '/'
1483         for (int i=0; i<tzID.length(); i++) {
1484             if (tzID.charAt(i) == 0x003A) {
1485                 tzID.setCharAt(i, 0x002F);
1486             }
1487         }
1488         return tzID;
1489     }
1490 
loadTimeZoneNamesImpl::ZoneStringsLoader1491     void load(UErrorCode& status) {
1492         ures_getAllItemsWithFallback(tzn.fZoneStrings, "", *this, status);
1493         if (U_FAILURE(status)) { return; }
1494 
1495         int32_t pos = UHASH_FIRST;
1496         const UHashElement* element;
1497         while ((element = uhash_nextElement(keyToLoader, &pos)) != NULL) {
1498             if (element->value.pointer == DUMMY_LOADER) { continue; }
1499             ZNames::ZNamesLoader* loader = (ZNames::ZNamesLoader*) element->value.pointer;
1500             char* key = (char*) element->key.pointer;
1501 
1502             if (isMetaZone(key)) {
1503                 UnicodeString mzID = mzIDFromKey(key);
1504                 ZNames::createMetaZoneAndPutInCache(tzn.fMZNamesMap, loader->getNames(), mzID, status);
1505             } else {
1506                 UnicodeString tzID = tzIDFromKey(key);
1507                 ZNames::createTimeZoneAndPutInCache(tzn.fTZNamesMap, loader->getNames(), tzID, status);
1508             }
1509             if (U_FAILURE(status)) { return; }
1510         }
1511     }
1512 
consumeNamesTableTimeZoneNamesImpl::ZoneStringsLoader1513     void consumeNamesTable(const char *key, ResourceValue &value, UBool noFallback,
1514             UErrorCode &status) {
1515         if (U_FAILURE(status)) { return; }
1516 
1517         void* loader = uhash_get(keyToLoader, key);
1518         if (loader == NULL) {
1519             if (isMetaZone(key)) {
1520                 UnicodeString mzID = mzIDFromKey(key);
1521                 void* cacheVal = uhash_get(tzn.fMZNamesMap, mzID.getTerminatedBuffer());
1522                 if (cacheVal != NULL) {
1523                     // We have already loaded the names for this meta zone.
1524                     loader = (void*) DUMMY_LOADER;
1525                 } else {
1526                     loader = (void*) new ZNames::ZNamesLoader();
1527                     if (loader == NULL) {
1528                         status = U_MEMORY_ALLOCATION_ERROR;
1529                         return;
1530                     }
1531                 }
1532             } else {
1533                 UnicodeString tzID = tzIDFromKey(key);
1534                 void* cacheVal = uhash_get(tzn.fTZNamesMap, tzID.getTerminatedBuffer());
1535                 if (cacheVal != NULL) {
1536                     // We have already loaded the names for this time zone.
1537                     loader = (void*) DUMMY_LOADER;
1538                 } else {
1539                     loader = (void*) new ZNames::ZNamesLoader();
1540                     if (loader == NULL) {
1541                         status = U_MEMORY_ALLOCATION_ERROR;
1542                         return;
1543                     }
1544                 }
1545             }
1546 
1547             void* newKey = createKey(key, status);
1548             if (U_FAILURE(status)) {
1549                 deleteZNamesLoader(loader);
1550                 return;
1551             }
1552 
1553             uhash_put(keyToLoader, newKey, loader, &status);
1554             if (U_FAILURE(status)) { return; }
1555         }
1556 
1557         if (loader != DUMMY_LOADER) {
1558             // Let the ZNamesLoader consume the names table.
1559             ((ZNames::ZNamesLoader*)loader)->put(key, value, noFallback, status);
1560         }
1561     }
1562 
putTimeZoneNamesImpl::ZoneStringsLoader1563     virtual void put(const char *key, ResourceValue &value, UBool noFallback,
1564             UErrorCode &status) override {
1565         ResourceTable timeZonesTable = value.getTable(status);
1566         if (U_FAILURE(status)) { return; }
1567         for (int32_t i = 0; timeZonesTable.getKeyAndValue(i, key, value); ++i) {
1568             U_ASSERT(!value.isNoInheritanceMarker());
1569             if (value.getType() == URES_TABLE) {
1570                 consumeNamesTable(key, value, noFallback, status);
1571             } else {
1572                 // Ignore fields that aren't tables (e.g., fallbackFormat and regionFormatStandard).
1573                 // All time zone fields are tables.
1574             }
1575             if (U_FAILURE(status)) { return; }
1576         }
1577     }
1578 };
1579 
1580 // Virtual destructors must be defined out of line.
~ZoneStringsLoader()1581 TimeZoneNamesImpl::ZoneStringsLoader::~ZoneStringsLoader() {
1582     uhash_close(keyToLoader);
1583 }
1584 
loadAllDisplayNames(UErrorCode & status)1585 void TimeZoneNamesImpl::loadAllDisplayNames(UErrorCode& status) {
1586     if (U_FAILURE(status)) return;
1587 
1588     {
1589         Mutex lock(&gDataMutex);
1590         internalLoadAllDisplayNames(status);
1591     }
1592 }
1593 
getDisplayNames(const UnicodeString & tzID,const UTimeZoneNameType types[],int32_t numTypes,UDate date,UnicodeString dest[],UErrorCode & status) const1594 void TimeZoneNamesImpl::getDisplayNames(const UnicodeString& tzID,
1595         const UTimeZoneNameType types[], int32_t numTypes,
1596         UDate date, UnicodeString dest[], UErrorCode& status) const {
1597     if (U_FAILURE(status)) return;
1598 
1599     if (tzID.isEmpty()) { return; }
1600     void* tznames = NULL;
1601     void* mznames = NULL;
1602     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl*>(this);
1603 
1604     // Load the time zone strings
1605     {
1606         Mutex lock(&gDataMutex);
1607         tznames = (void*) nonConstThis->loadTimeZoneNames(tzID, status);
1608         if (U_FAILURE(status)) { return; }
1609     }
1610     U_ASSERT(tznames != NULL);
1611 
1612     // Load the values into the dest array
1613     for (int i = 0; i < numTypes; i++) {
1614         UTimeZoneNameType type = types[i];
1615         const UChar* name = ((ZNames*)tznames)->getName(type);
1616         if (name == NULL) {
1617             if (mznames == NULL) {
1618                 // Load the meta zone name
1619                 UnicodeString mzID;
1620                 getMetaZoneID(tzID, date, mzID);
1621                 if (mzID.isEmpty()) {
1622                     mznames = (void*) EMPTY;
1623                 } else {
1624                     // Load the meta zone strings
1625                     // Mutex is scoped to the "else" statement
1626                     Mutex lock(&gDataMutex);
1627                     mznames = (void*) nonConstThis->loadMetaZoneNames(mzID, status);
1628                     if (U_FAILURE(status)) { return; }
1629                     // Note: when the metazone doesn't exist, in Java, loadMetaZoneNames returns
1630                     // a dummy object instead of NULL.
1631                     if (mznames == NULL) {
1632                         mznames = (void*) EMPTY;
1633                     }
1634                 }
1635             }
1636             U_ASSERT(mznames != NULL);
1637             if (mznames != EMPTY) {
1638                 name = ((ZNames*)mznames)->getName(type);
1639             }
1640         }
1641         if (name != NULL) {
1642             dest[i].setTo(true, name, -1);
1643         } else {
1644             dest[i].setToBogus();
1645         }
1646     }
1647 }
1648 
1649 // Caller must synchronize.
internalLoadAllDisplayNames(UErrorCode & status)1650 void TimeZoneNamesImpl::internalLoadAllDisplayNames(UErrorCode& status) {
1651     if (!fNamesFullyLoaded) {
1652         fNamesFullyLoaded = true;
1653 
1654         ZoneStringsLoader loader(*this, status);
1655         loader.load(status);
1656         if (U_FAILURE(status)) { return; }
1657 
1658         const UnicodeString *id;
1659 
1660         // load strings for all zones
1661         StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(
1662             UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
1663         if (U_SUCCESS(status)) {
1664             while ((id = tzIDs->snext(status)) != NULL) {
1665                 if (U_FAILURE(status)) {
1666                     break;
1667                 }
1668                 UnicodeString copy(*id);
1669                 void* value = uhash_get(fTZNamesMap, copy.getTerminatedBuffer());
1670                 if (value == NULL) {
1671                     // loadStrings also loads related metazone strings
1672                     loadStrings(*id, status);
1673                 }
1674             }
1675         }
1676         if (tzIDs != NULL) {
1677             delete tzIDs;
1678         }
1679     }
1680 }
1681 
1682 
1683 
1684 static const UChar gEtcPrefix[]         = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
1685 static const int32_t gEtcPrefixLen      = 4;
1686 static const UChar gSystemVPrefix[]     = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
1687 static const int32_t gSystemVPrefixLen  = 8;
1688 static const UChar gRiyadh8[]           = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
1689 static const int32_t gRiyadh8Len       = 7;
1690 
1691 UnicodeString& U_EXPORT2
getDefaultExemplarLocationName(const UnicodeString & tzID,UnicodeString & name)1692 TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) {
1693     if (tzID.isEmpty() || tzID.startsWith(gEtcPrefix, gEtcPrefixLen)
1694         || tzID.startsWith(gSystemVPrefix, gSystemVPrefixLen) || tzID.indexOf(gRiyadh8, gRiyadh8Len, 0) > 0) {
1695         name.setToBogus();
1696         return name;
1697     }
1698 
1699     int32_t sep = tzID.lastIndexOf((UChar)0x2F /* '/' */);
1700     if (sep > 0 && sep + 1 < tzID.length()) {
1701         name.setTo(tzID, sep + 1);
1702         name.findAndReplace(UnicodeString((UChar)0x5f /* _ */),
1703                             UnicodeString((UChar)0x20 /* space */));
1704     } else {
1705         name.setToBogus();
1706     }
1707     return name;
1708 }
1709 
1710 // ---------------------------------------------------
1711 // TZDBTimeZoneNames and its supporting classes
1712 //
1713 // TZDBTimeZoneNames is an implementation class of
1714 // TimeZoneNames holding the IANA tz database abbreviations.
1715 // ---------------------------------------------------
1716 
1717 class TZDBNames : public UMemory {
1718 public:
1719     virtual ~TZDBNames();
1720 
1721     static TZDBNames* createInstance(UResourceBundle* rb, const char* key);
1722     const UChar* getName(UTimeZoneNameType type) const;
1723     const char** getParseRegions(int32_t& numRegions) const;
1724 
1725 protected:
1726     TZDBNames(const UChar** names, char** regions, int32_t numRegions);
1727 
1728 private:
1729     const UChar** fNames;
1730     char** fRegions;
1731     int32_t fNumRegions;
1732 };
1733 
TZDBNames(const UChar ** names,char ** regions,int32_t numRegions)1734 TZDBNames::TZDBNames(const UChar** names, char** regions, int32_t numRegions)
1735     :   fNames(names),
1736         fRegions(regions),
1737         fNumRegions(numRegions) {
1738 }
1739 
~TZDBNames()1740 TZDBNames::~TZDBNames() {
1741     if (fNames != NULL) {
1742         uprv_free(fNames);
1743     }
1744     if (fRegions != NULL) {
1745         char **p = fRegions;
1746         for (int32_t i = 0; i < fNumRegions; p++, i++) {
1747             uprv_free(*p);
1748         }
1749         uprv_free(fRegions);
1750     }
1751 }
1752 
1753 TZDBNames*
createInstance(UResourceBundle * rb,const char * key)1754 TZDBNames::createInstance(UResourceBundle* rb, const char* key) {
1755     if (rb == NULL || key == NULL || *key == 0) {
1756         return NULL;
1757     }
1758 
1759     UErrorCode status = U_ZERO_ERROR;
1760 
1761     const UChar **names = NULL;
1762     char** regions = NULL;
1763     int32_t numRegions = 0;
1764 
1765     int32_t len = 0;
1766 
1767     UResourceBundle* rbTable = NULL;
1768     rbTable = ures_getByKey(rb, key, rbTable, &status);
1769     if (U_FAILURE(status)) {
1770         return NULL;
1771     }
1772 
1773     names = (const UChar **)uprv_malloc(sizeof(const UChar*) * TZDBNAMES_KEYS_SIZE);
1774     UBool isEmpty = true;
1775     if (names != NULL) {
1776         for (int32_t i = 0; i < TZDBNAMES_KEYS_SIZE; i++) {
1777             status = U_ZERO_ERROR;
1778             const UChar *value = ures_getStringByKey(rbTable, TZDBNAMES_KEYS[i], &len, &status);
1779             if (U_FAILURE(status) || len == 0) {
1780                 names[i] = NULL;
1781             } else {
1782                 names[i] = value;
1783                 isEmpty = false;
1784             }
1785         }
1786     }
1787 
1788     if (isEmpty) {
1789         if (names != NULL) {
1790             uprv_free(names);
1791         }
1792         return NULL;
1793     }
1794 
1795     UResourceBundle *regionsRes = ures_getByKey(rbTable, "parseRegions", NULL, &status);
1796     UBool regionError = false;
1797     if (U_SUCCESS(status)) {
1798         numRegions = ures_getSize(regionsRes);
1799         if (numRegions > 0) {
1800             regions = (char**)uprv_malloc(sizeof(char*) * numRegions);
1801             if (regions != NULL) {
1802                 char **pRegion = regions;
1803                 for (int32_t i = 0; i < numRegions; i++, pRegion++) {
1804                     *pRegion = NULL;
1805                 }
1806                 // filling regions
1807                 pRegion = regions;
1808                 for (int32_t i = 0; i < numRegions; i++, pRegion++) {
1809                     status = U_ZERO_ERROR;
1810                     const UChar *uregion = ures_getStringByIndex(regionsRes, i, &len, &status);
1811                     if (U_FAILURE(status)) {
1812                         regionError = true;
1813                         break;
1814                     }
1815                     *pRegion = (char*)uprv_malloc(sizeof(char) * (len + 1));
1816                     if (*pRegion == NULL) {
1817                         regionError = true;
1818                         break;
1819                     }
1820                     u_UCharsToChars(uregion, *pRegion, len);
1821                     (*pRegion)[len] = 0;
1822                 }
1823             }
1824         }
1825     }
1826     ures_close(regionsRes);
1827     ures_close(rbTable);
1828 
1829     if (regionError) {
1830         if (names != NULL) {
1831             uprv_free(names);
1832         }
1833         if (regions != NULL) {
1834             char **p = regions;
1835             for (int32_t i = 0; i < numRegions; p++, i++) {
1836                 uprv_free(*p);
1837             }
1838             uprv_free(regions);
1839         }
1840         return NULL;
1841     }
1842 
1843     return new TZDBNames(names, regions, numRegions);
1844 }
1845 
1846 const UChar*
getName(UTimeZoneNameType type) const1847 TZDBNames::getName(UTimeZoneNameType type) const {
1848     if (fNames == NULL) {
1849         return NULL;
1850     }
1851     const UChar *name = NULL;
1852     switch(type) {
1853     case UTZNM_SHORT_STANDARD:
1854         name = fNames[0];
1855         break;
1856     case UTZNM_SHORT_DAYLIGHT:
1857         name = fNames[1];
1858         break;
1859     default:
1860         name = NULL;
1861     }
1862     return name;
1863 }
1864 
1865 const char**
getParseRegions(int32_t & numRegions) const1866 TZDBNames::getParseRegions(int32_t& numRegions) const {
1867     if (fRegions == NULL) {
1868         numRegions = 0;
1869     } else {
1870         numRegions = fNumRegions;
1871     }
1872     return (const char**)fRegions;
1873 }
1874 
1875 U_CDECL_BEGIN
1876 /**
1877  * TZDBNameInfo stores metazone name information for the IANA abbreviations
1878  * in the trie
1879  */
1880 typedef struct TZDBNameInfo {
1881     const UChar*        mzID;
1882     UTimeZoneNameType   type;
1883     UBool               ambiguousType;
1884     const char**        parseRegions;
1885     int32_t             nRegions;
1886 } TZDBNameInfo;
1887 U_CDECL_END
1888 
1889 
1890 class TZDBNameSearchHandler : public TextTrieMapSearchResultHandler {
1891 public:
1892     TZDBNameSearchHandler(uint32_t types, const char* region);
1893     virtual ~TZDBNameSearchHandler();
1894 
1895     UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) override;
1896     TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen);
1897 
1898 private:
1899     uint32_t fTypes;
1900     int32_t fMaxMatchLen;
1901     TimeZoneNames::MatchInfoCollection* fResults;
1902     const char* fRegion;
1903 };
1904 
TZDBNameSearchHandler(uint32_t types,const char * region)1905 TZDBNameSearchHandler::TZDBNameSearchHandler(uint32_t types, const char* region)
1906 : fTypes(types), fMaxMatchLen(0), fResults(NULL), fRegion(region) {
1907 }
1908 
~TZDBNameSearchHandler()1909 TZDBNameSearchHandler::~TZDBNameSearchHandler() {
1910     if (fResults != NULL) {
1911         delete fResults;
1912     }
1913 }
1914 
1915 UBool
handleMatch(int32_t matchLength,const CharacterNode * node,UErrorCode & status)1916 TZDBNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
1917     if (U_FAILURE(status)) {
1918         return false;
1919     }
1920 
1921     TZDBNameInfo *match = NULL;
1922     TZDBNameInfo *defaultRegionMatch = NULL;
1923 
1924     if (node->hasValues()) {
1925         int32_t valuesCount = node->countValues();
1926         for (int32_t i = 0; i < valuesCount; i++) {
1927             TZDBNameInfo *ninfo = (TZDBNameInfo *)node->getValue(i);
1928             if (ninfo == NULL) {
1929                 continue;
1930             }
1931             if ((ninfo->type & fTypes) != 0) {
1932                 // Some tz database abbreviations are ambiguous. For example,
1933                 // CST means either Central Standard Time or China Standard Time.
1934                 // Unlike CLDR time zone display names, this implementation
1935                 // does not use unique names. And TimeZoneFormat does not expect
1936                 // multiple results returned for the same time zone type.
1937                 // For this reason, this implementation resolve one among same
1938                 // zone type with a same name at this level.
1939                 if (ninfo->parseRegions == NULL) {
1940                     // parseRegions == null means this is the default metazone
1941                     // mapping for the abbreviation.
1942                     if (defaultRegionMatch == NULL) {
1943                         match = defaultRegionMatch = ninfo;
1944                     }
1945                 } else {
1946                     UBool matchRegion = false;
1947                     // non-default metazone mapping for an abbreviation
1948                     // comes with applicable regions. For example, the default
1949                     // metazone mapping for "CST" is America_Central,
1950                     // but if region is one of CN/MO/TW, "CST" is parsed
1951                     // as metazone China (China Standard Time).
1952                     for (int32_t j = 0; j < ninfo->nRegions; j++) {
1953                         const char *region = ninfo->parseRegions[j];
1954                         if (uprv_strcmp(fRegion, region) == 0) {
1955                             match = ninfo;
1956                             matchRegion = true;
1957                             break;
1958                         }
1959                     }
1960                     if (matchRegion) {
1961                         break;
1962                     }
1963                     if (match == NULL) {
1964                         match = ninfo;
1965                     }
1966                 }
1967             }
1968         }
1969 
1970         if (match != NULL) {
1971             UTimeZoneNameType ntype = match->type;
1972             // Note: Workaround for duplicated standard/daylight names
1973             // The tz database contains a few zones sharing a
1974             // same name for both standard time and daylight saving
1975             // time. For example, Australia/Sydney observes DST,
1976             // but "EST" is used for both standard and daylight.
1977             // When both SHORT_STANDARD and SHORT_DAYLIGHT are included
1978             // in the find operation, we cannot tell which one was
1979             // actually matched.
1980             // TimeZoneFormat#parse returns a matched name type (standard
1981             // or daylight) and DateFormat implementation uses the info to
1982             // to adjust actual time. To avoid false type information,
1983             // this implementation replaces the name type with SHORT_GENERIC.
1984             if (match->ambiguousType
1985                     && (ntype == UTZNM_SHORT_STANDARD || ntype == UTZNM_SHORT_DAYLIGHT)
1986                     && (fTypes & UTZNM_SHORT_STANDARD) != 0
1987                     && (fTypes & UTZNM_SHORT_DAYLIGHT) != 0) {
1988                 ntype = UTZNM_SHORT_GENERIC;
1989             }
1990 
1991             if (fResults == NULL) {
1992                 fResults = new TimeZoneNames::MatchInfoCollection();
1993                 if (fResults == NULL) {
1994                     status = U_MEMORY_ALLOCATION_ERROR;
1995                 }
1996             }
1997             if (U_SUCCESS(status)) {
1998                 U_ASSERT(fResults != NULL);
1999                 U_ASSERT(match->mzID != NULL);
2000                 fResults->addMetaZone(ntype, matchLength, UnicodeString(match->mzID, -1), status);
2001                 if (U_SUCCESS(status) && matchLength > fMaxMatchLen) {
2002                     fMaxMatchLen = matchLength;
2003                 }
2004             }
2005         }
2006     }
2007     return true;
2008 }
2009 
2010 TimeZoneNames::MatchInfoCollection*
getMatches(int32_t & maxMatchLen)2011 TZDBNameSearchHandler::getMatches(int32_t& maxMatchLen) {
2012     // give the ownership to the caller
2013     TimeZoneNames::MatchInfoCollection* results = fResults;
2014     maxMatchLen = fMaxMatchLen;
2015 
2016     // reset
2017     fResults = NULL;
2018     fMaxMatchLen = 0;
2019     return results;
2020 }
2021 
2022 U_CDECL_BEGIN
2023 /**
2024  * Deleter for TZDBNames
2025  */
2026 static void U_CALLCONV
deleteTZDBNames(void * obj)2027 deleteTZDBNames(void *obj) {
2028     if (obj != EMPTY) {
2029         delete (TZDBNames *)obj;
2030     }
2031 }
2032 
initTZDBNamesMap(UErrorCode & status)2033 static void U_CALLCONV initTZDBNamesMap(UErrorCode &status) {
2034     gTZDBNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
2035     if (U_FAILURE(status)) {
2036         gTZDBNamesMap = NULL;
2037         return;
2038     }
2039     // no key deleters for tzdb name maps
2040     uhash_setValueDeleter(gTZDBNamesMap, deleteTZDBNames);
2041     ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup);
2042 }
2043 
2044 /**
2045  * Deleter for TZDBNameInfo
2046  */
2047 static void U_CALLCONV
deleteTZDBNameInfo(void * obj)2048 deleteTZDBNameInfo(void *obj) {
2049     if (obj != NULL) {
2050         uprv_free(obj);
2051     }
2052 }
2053 
prepareFind(UErrorCode & status)2054 static void U_CALLCONV prepareFind(UErrorCode &status) {
2055     if (U_FAILURE(status)) {
2056         return;
2057     }
2058     gTZDBNamesTrie = new TextTrieMap(true, deleteTZDBNameInfo);
2059     if (gTZDBNamesTrie == NULL) {
2060         status = U_MEMORY_ALLOCATION_ERROR;
2061         return;
2062     }
2063 
2064     const UnicodeString *mzID;
2065     StringEnumeration *mzIDs = TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
2066     if (U_SUCCESS(status)) {
2067         while ((mzID = mzIDs->snext(status)) != 0 && U_SUCCESS(status)) {
2068             const TZDBNames *names = TZDBTimeZoneNames::getMetaZoneNames(*mzID, status);
2069             if (U_FAILURE(status)) {
2070                 break;
2071             }
2072             if (names == NULL) {
2073                 continue;
2074             }
2075             const UChar *std = names->getName(UTZNM_SHORT_STANDARD);
2076             const UChar *dst = names->getName(UTZNM_SHORT_DAYLIGHT);
2077             if (std == NULL && dst == NULL) {
2078                 continue;
2079             }
2080             int32_t numRegions = 0;
2081             const char **parseRegions = names->getParseRegions(numRegions);
2082 
2083             // The tz database contains a few zones sharing a
2084             // same name for both standard time and daylight saving
2085             // time. For example, Australia/Sydney observes DST,
2086             // but "EST" is used for both standard and daylight.
2087             // we need to store the information for later processing.
2088             UBool ambiguousType = (std != NULL && dst != NULL && u_strcmp(std, dst) == 0);
2089 
2090             const UChar *uMzID = ZoneMeta::findMetaZoneID(*mzID);
2091             if (std != NULL) {
2092                 TZDBNameInfo *stdInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo));
2093                 if (stdInf == NULL) {
2094                     status = U_MEMORY_ALLOCATION_ERROR;
2095                     break;
2096                 }
2097                 stdInf->mzID = uMzID;
2098                 stdInf->type = UTZNM_SHORT_STANDARD;
2099                 stdInf->ambiguousType = ambiguousType;
2100                 stdInf->parseRegions = parseRegions;
2101                 stdInf->nRegions = numRegions;
2102                 gTZDBNamesTrie->put(std, stdInf, status);
2103             }
2104             if (U_SUCCESS(status) && dst != NULL) {
2105                 TZDBNameInfo *dstInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo));
2106                 if (dstInf == NULL) {
2107                     status = U_MEMORY_ALLOCATION_ERROR;
2108                     break;
2109                 }
2110                 dstInf->mzID = uMzID;
2111                 dstInf->type = UTZNM_SHORT_DAYLIGHT;
2112                 dstInf->ambiguousType = ambiguousType;
2113                 dstInf->parseRegions = parseRegions;
2114                 dstInf->nRegions = numRegions;
2115                 gTZDBNamesTrie->put(dst, dstInf, status);
2116             }
2117         }
2118     }
2119     delete mzIDs;
2120 
2121     if (U_FAILURE(status)) {
2122         delete gTZDBNamesTrie;
2123         gTZDBNamesTrie = NULL;
2124         return;
2125     }
2126 
2127     ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup);
2128 }
2129 
2130 U_CDECL_END
2131 
TZDBTimeZoneNames(const Locale & locale)2132 TZDBTimeZoneNames::TZDBTimeZoneNames(const Locale& locale)
2133 : fLocale(locale) {
2134     UBool useWorld = true;
2135     const char* region = fLocale.getCountry();
2136     int32_t regionLen = static_cast<int32_t>(uprv_strlen(region));
2137     if (regionLen == 0) {
2138         UErrorCode status = U_ZERO_ERROR;
2139         CharString loc;
2140         {
2141             CharStringByteSink sink(&loc);
2142             ulocimp_addLikelySubtags(fLocale.getName(), sink, &status);
2143         }
2144         regionLen = uloc_getCountry(loc.data(), fRegion, sizeof(fRegion), &status);
2145         if (U_SUCCESS(status) && regionLen < (int32_t)sizeof(fRegion)) {
2146             useWorld = false;
2147         }
2148     } else if (regionLen < (int32_t)sizeof(fRegion)) {
2149         uprv_strcpy(fRegion, region);
2150         useWorld = false;
2151     }
2152     if (useWorld) {
2153         uprv_strcpy(fRegion, "001");
2154     }
2155 }
2156 
~TZDBTimeZoneNames()2157 TZDBTimeZoneNames::~TZDBTimeZoneNames() {
2158 }
2159 
2160 bool
operator ==(const TimeZoneNames & other) const2161 TZDBTimeZoneNames::operator==(const TimeZoneNames& other) const {
2162     if (this == &other) {
2163         return true;
2164     }
2165     // No implementation for now
2166     return false;
2167 }
2168 
2169 TZDBTimeZoneNames*
clone() const2170 TZDBTimeZoneNames::clone() const {
2171     return new TZDBTimeZoneNames(fLocale);
2172 }
2173 
2174 StringEnumeration*
getAvailableMetaZoneIDs(UErrorCode & status) const2175 TZDBTimeZoneNames::getAvailableMetaZoneIDs(UErrorCode& status) const {
2176     return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
2177 }
2178 
2179 StringEnumeration*
getAvailableMetaZoneIDs(const UnicodeString & tzID,UErrorCode & status) const2180 TZDBTimeZoneNames::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
2181     return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status);
2182 }
2183 
2184 UnicodeString&
getMetaZoneID(const UnicodeString & tzID,UDate date,UnicodeString & mzID) const2185 TZDBTimeZoneNames::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
2186     return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID);
2187 }
2188 
2189 UnicodeString&
getReferenceZoneID(const UnicodeString & mzID,const char * region,UnicodeString & tzID) const2190 TZDBTimeZoneNames::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
2191     return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID);
2192 }
2193 
2194 UnicodeString&
getMetaZoneDisplayName(const UnicodeString & mzID,UTimeZoneNameType type,UnicodeString & name) const2195 TZDBTimeZoneNames::getMetaZoneDisplayName(const UnicodeString& mzID,
2196                                           UTimeZoneNameType type,
2197                                           UnicodeString& name) const {
2198     name.setToBogus();
2199     if (mzID.isEmpty()) {
2200         return name;
2201     }
2202 
2203     UErrorCode status = U_ZERO_ERROR;
2204     const TZDBNames *tzdbNames = TZDBTimeZoneNames::getMetaZoneNames(mzID, status);
2205     if (U_SUCCESS(status)) {
2206         if (tzdbNames != NULL) {
2207             const UChar *s = tzdbNames->getName(type);
2208             if (s != NULL) {
2209                 name.setTo(true, s, -1);
2210             }
2211         }
2212     }
2213 
2214     return name;
2215 }
2216 
2217 UnicodeString&
getTimeZoneDisplayName(const UnicodeString &,UTimeZoneNameType,UnicodeString & name) const2218 TZDBTimeZoneNames::getTimeZoneDisplayName(const UnicodeString& /* tzID */, UTimeZoneNameType /* type */, UnicodeString& name) const {
2219     // No abbreviations associated a zone directly for now.
2220     name.setToBogus();
2221     return name;
2222 }
2223 
2224 TZDBTimeZoneNames::MatchInfoCollection*
find(const UnicodeString & text,int32_t start,uint32_t types,UErrorCode & status) const2225 TZDBTimeZoneNames::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
2226     umtx_initOnce(gTZDBNamesTrieInitOnce, &prepareFind, status);
2227     if (U_FAILURE(status)) {
2228         return NULL;
2229     }
2230 
2231     TZDBNameSearchHandler handler(types, fRegion);
2232     gTZDBNamesTrie->search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
2233     if (U_FAILURE(status)) {
2234         return NULL;
2235     }
2236     int32_t maxLen = 0;
2237     return handler.getMatches(maxLen);
2238 }
2239 
2240 const TZDBNames*
getMetaZoneNames(const UnicodeString & mzID,UErrorCode & status)2241 TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) {
2242     umtx_initOnce(gTZDBNamesMapInitOnce, &initTZDBNamesMap, status);
2243     if (U_FAILURE(status)) {
2244         return NULL;
2245     }
2246 
2247     TZDBNames* tzdbNames = NULL;
2248 
2249     UChar mzIDKey[ZID_KEY_MAX + 1];
2250     mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status);
2251     U_ASSERT(status == U_ZERO_ERROR);   // already checked length above
2252     mzIDKey[mzID.length()] = 0;
2253 
2254     static UMutex gTZDBNamesMapLock;
2255     umtx_lock(&gTZDBNamesMapLock);
2256     {
2257         void *cacheVal = uhash_get(gTZDBNamesMap, mzIDKey);
2258         if (cacheVal == NULL) {
2259             UResourceBundle *zoneStringsRes = ures_openDirect(U_ICUDATA_ZONE, "tzdbNames", &status);
2260             zoneStringsRes = ures_getByKey(zoneStringsRes, gZoneStrings, zoneStringsRes, &status);
2261             if (U_SUCCESS(status)) {
2262                 char key[ZID_KEY_MAX + 1];
2263                 mergeTimeZoneKey(mzID, key);
2264                 tzdbNames = TZDBNames::createInstance(zoneStringsRes, key);
2265 
2266                 if (tzdbNames == NULL) {
2267                     cacheVal = (void *)EMPTY;
2268                 } else {
2269                     cacheVal = tzdbNames;
2270                 }
2271                 // Use the persistent ID as the resource key, so we can
2272                 // avoid duplications.
2273                 // TODO: Is there a more efficient way, like intern() in Java?
2274                 void* newKey = (void*) ZoneMeta::findMetaZoneID(mzID);
2275                 if (newKey != NULL) {
2276                     uhash_put(gTZDBNamesMap, newKey, cacheVal, &status);
2277                     if (U_FAILURE(status)) {
2278                         if (tzdbNames != NULL) {
2279                             delete tzdbNames;
2280                             tzdbNames = NULL;
2281                         }
2282                     }
2283                 } else {
2284                     // Should never happen with a valid input
2285                     if (tzdbNames != NULL) {
2286                         // It's not possible that we get a valid tzdbNames with unknown ID.
2287                         // But just in case..
2288                         delete tzdbNames;
2289                         tzdbNames = NULL;
2290                     }
2291                 }
2292             }
2293             ures_close(zoneStringsRes);
2294         } else if (cacheVal != EMPTY) {
2295             tzdbNames = (TZDBNames *)cacheVal;
2296         }
2297     }
2298     umtx_unlock(&gTZDBNamesMapLock);
2299 
2300     return tzdbNames;
2301 }
2302 
2303 U_NAMESPACE_END
2304 
2305 
2306 #endif /* #if !UCONFIG_NO_FORMATTING */
2307 
2308 //eof
2309