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