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