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