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