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