• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 * Copyright (C) 2011, 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 "uresimp.h"
24 #include "ureslocs.h"
25 #include "zonemeta.h"
26 #include "ucln_in.h"
27 #include "uvector.h"
28 #include "olsontz.h"
29 
30 
31 U_NAMESPACE_BEGIN
32 
33 #define ZID_KEY_MAX  128
34 #define MZ_PREFIX_LEN 5
35 
36 static const char gZoneStrings[]        = "zoneStrings";
37 static const char gMZPrefix[]           = "meta:";
38 
39 static const char* KEYS[]               = {"lg", "ls", "ld", "sg", "ss", "sd"};
40 static const int32_t KEYS_SIZE = (sizeof KEYS / sizeof KEYS[0]);
41 
42 static const char gCuTag[]              = "cu";
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_SHORT_STANDARD_COMMONLY_USED, UTZNM_SHORT_DAYLIGHT_COMMONLY_USED,
51     UTZNM_UNKNOWN // unknown as the last one
52 };
53 
54 #define DEFAULT_CHARACTERNODE_CAPACITY 1
55 
56 // ---------------------------------------------------
57 // CaracterNode class implementation
58 // ---------------------------------------------------
clear()59 void CharacterNode::clear() {
60     uprv_memset(this, 0, sizeof(*this));
61 }
62 
deleteValues(UObjectDeleter * valueDeleter)63 void CharacterNode::deleteValues(UObjectDeleter *valueDeleter) {
64     if (fValues == NULL) {
65         // Do nothing.
66     } else if (!fHasValuesVector) {
67         if (valueDeleter) {
68             valueDeleter(fValues);
69         }
70     } else {
71         delete (UVector *)fValues;
72     }
73 }
74 
75 void
addValue(void * value,UObjectDeleter * valueDeleter,UErrorCode & status)76 CharacterNode::addValue(void *value, UObjectDeleter *valueDeleter, UErrorCode &status) {
77     if (U_FAILURE(status)) {
78         if (valueDeleter) {
79             valueDeleter(value);
80         }
81         return;
82     }
83     if (fValues == NULL) {
84         fValues = value;
85     } else {
86         // At least one value already.
87         if (!fHasValuesVector) {
88             // There is only one value so far, and not in a vector yet.
89             // Create a vector and add the old value.
90             UVector *values = new UVector(valueDeleter, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status);
91             if (U_FAILURE(status)) {
92                 if (valueDeleter) {
93                     valueDeleter(value);
94                 }
95                 return;
96             }
97             values->addElement(fValues, status);
98             fValues = values;
99             fHasValuesVector = TRUE;
100         }
101         // Add the new value.
102         ((UVector *)fValues)->addElement(value, status);
103     }
104 }
105 
106 // ---------------------------------------------------
107 // TextTrieMapSearchResultHandler class implementation
108 // ---------------------------------------------------
~TextTrieMapSearchResultHandler()109 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
110 }
111 
112 // ---------------------------------------------------
113 // TextTrieMap class implementation
114 // ---------------------------------------------------
TextTrieMap(UBool ignoreCase,UObjectDeleter * valueDeleter)115 TextTrieMap::TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleter)
116 : fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0),
117   fLazyContents(NULL), fIsEmpty(TRUE), fValueDeleter(valueDeleter) {
118 }
119 
~TextTrieMap()120 TextTrieMap::~TextTrieMap() {
121     int32_t index;
122     for (index = 0; index < fNodesCount; ++index) {
123         fNodes[index].deleteValues(fValueDeleter);
124     }
125     uprv_free(fNodes);
126     if (fLazyContents != NULL) {
127         for (int32_t i=0; i<fLazyContents->size(); i+=2) {
128             if (fValueDeleter) {
129                 fValueDeleter(fLazyContents->elementAt(i+1));
130             }
131         }
132         delete fLazyContents;
133     }
134 }
135 
isEmpty() const136 int32_t TextTrieMap::isEmpty() const {
137     // Use a separate field for fIsEmpty because it will remain unchanged once the
138     //   Trie is built, while fNodes and fLazyContents change with the lazy init
139     //   of the nodes structure.  Trying to test the changing fields has
140     //   thread safety complications.
141     return fIsEmpty;
142 }
143 
144 
145 //  We defer actually building the TextTrieMap node structure until the first time a
146 //     search is performed.  put() simply saves the parameters in case we do
147 //     eventually need to build it.
148 //
149 void
put(const UnicodeString & key,void * value,ZNStringPool & sp,UErrorCode & status)150 TextTrieMap::put(const UnicodeString &key, void *value, ZNStringPool &sp, UErrorCode &status) {
151     const UChar *s = sp.get(key, status);
152     put(s, value, status);
153 }
154 
155 // This method is for designed for a persistent key, such as string key stored in
156 // resource bundle.
157 void
put(const UChar * key,void * value,UErrorCode & status)158 TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) {
159     fIsEmpty = FALSE;
160     if (fLazyContents == NULL) {
161         fLazyContents = new UVector(status);
162         if (fLazyContents == NULL) {
163             status = U_MEMORY_ALLOCATION_ERROR;
164         }
165     }
166     if (U_FAILURE(status)) {
167         return;
168     }
169     UChar *s = const_cast<UChar *>(key);
170     fLazyContents->addElement(s, status);
171     fLazyContents->addElement(value, status);
172 }
173 
174 void
putImpl(const UnicodeString & key,void * value,UErrorCode & status)175 TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) {
176     if (fNodes == NULL) {
177         fNodesCapacity = 512;
178         fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode));
179         fNodes[0].clear();  // Init root node.
180         fNodesCount = 1;
181     }
182 
183     UnicodeString foldedKey;
184     const UChar *keyBuffer;
185     int32_t keyLength;
186     if (fIgnoreCase) {
187         // Ok to use fastCopyFrom() because we discard the copy when we return.
188         foldedKey.fastCopyFrom(key).foldCase();
189         keyBuffer = foldedKey.getBuffer();
190         keyLength = foldedKey.length();
191     } else {
192         keyBuffer = key.getBuffer();
193         keyLength = key.length();
194     }
195 
196     CharacterNode *node = fNodes;
197     int32_t index;
198     for (index = 0; index < keyLength; ++index) {
199         node = addChildNode(node, keyBuffer[index], status);
200     }
201     node->addValue(value, fValueDeleter, status);
202 }
203 
204 UBool
growNodes()205 TextTrieMap::growNodes() {
206     if (fNodesCapacity == 0xffff) {
207         return FALSE;  // We use 16-bit node indexes.
208     }
209     int32_t newCapacity = fNodesCapacity + 1000;
210     if (newCapacity > 0xffff) {
211         newCapacity = 0xffff;
212     }
213     CharacterNode *newNodes = (CharacterNode *)uprv_malloc(newCapacity * sizeof(CharacterNode));
214     if (newNodes == NULL) {
215         return FALSE;
216     }
217     uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode));
218     uprv_free(fNodes);
219     fNodes = newNodes;
220     fNodesCapacity = newCapacity;
221     return TRUE;
222 }
223 
224 CharacterNode*
addChildNode(CharacterNode * parent,UChar c,UErrorCode & status)225 TextTrieMap::addChildNode(CharacterNode *parent, UChar c, UErrorCode &status) {
226     if (U_FAILURE(status)) {
227         return NULL;
228     }
229     // Linear search of the sorted list of children.
230     uint16_t prevIndex = 0;
231     uint16_t nodeIndex = parent->fFirstChild;
232     while (nodeIndex > 0) {
233         CharacterNode *current = fNodes + nodeIndex;
234         UChar childCharacter = current->fCharacter;
235         if (childCharacter == c) {
236             return current;
237         } else if (childCharacter > c) {
238             break;
239         }
240         prevIndex = nodeIndex;
241         nodeIndex = current->fNextSibling;
242     }
243 
244     // Ensure capacity. Grow fNodes[] if needed.
245     if (fNodesCount == fNodesCapacity) {
246         int32_t parentIndex = (int32_t)(parent - fNodes);
247         if (!growNodes()) {
248             status = U_MEMORY_ALLOCATION_ERROR;
249             return NULL;
250         }
251         parent = fNodes + parentIndex;
252     }
253 
254     // Insert a new child node with c in sorted order.
255     CharacterNode *node = fNodes + fNodesCount;
256     node->clear();
257     node->fCharacter = c;
258     node->fNextSibling = nodeIndex;
259     if (prevIndex == 0) {
260         parent->fFirstChild = (uint16_t)fNodesCount;
261     } else {
262         fNodes[prevIndex].fNextSibling = (uint16_t)fNodesCount;
263     }
264     ++fNodesCount;
265     return node;
266 }
267 
268 CharacterNode*
getChildNode(CharacterNode * parent,UChar c) const269 TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const {
270     // Linear search of the sorted list of children.
271     uint16_t nodeIndex = parent->fFirstChild;
272     while (nodeIndex > 0) {
273         CharacterNode *current = fNodes + nodeIndex;
274         UChar childCharacter = current->fCharacter;
275         if (childCharacter == c) {
276             return current;
277         } else if (childCharacter > c) {
278             break;
279         }
280         nodeIndex = current->fNextSibling;
281     }
282     return NULL;
283 }
284 
285 // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
286 static UMTX TextTrieMutex;
287 
288 // buildTrie() - The Trie node structure is needed.  Create it from the data that was
289 //               saved at the time the ZoneStringFormatter was created.  The Trie is only
290 //               needed for parsing operations, which are less common than formatting,
291 //               and the Trie is big, which is why its creation is deferred until first use.
buildTrie(UErrorCode & status)292 void TextTrieMap::buildTrie(UErrorCode &status) {
293     umtx_lock(&TextTrieMutex);
294     if (fLazyContents != NULL) {
295         for (int32_t i=0; i<fLazyContents->size(); i+=2) {
296             const UChar *key = (UChar *)fLazyContents->elementAt(i);
297             void  *val = fLazyContents->elementAt(i+1);
298             UnicodeString keyString(TRUE, key, -1);  // Aliasing UnicodeString constructor.
299             putImpl(keyString, val, status);
300         }
301         delete fLazyContents;
302         fLazyContents = NULL;
303     }
304     umtx_unlock(&TextTrieMutex);
305 }
306 
307 void
search(const UnicodeString & text,int32_t start,TextTrieMapSearchResultHandler * handler,UErrorCode & status) const308 TextTrieMap::search(const UnicodeString &text, int32_t start,
309                   TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
310     UBool trieNeedsInitialization = FALSE;
311     UMTX_CHECK(&TextTrieMutex, fLazyContents != NULL, trieNeedsInitialization);
312     if (trieNeedsInitialization) {
313         TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this);
314         nonConstThis->buildTrie(status);
315     }
316     if (fNodes == NULL) {
317         return;
318     }
319     search(fNodes, text, start, start, handler, status);
320 }
321 
322 void
search(CharacterNode * node,const UnicodeString & text,int32_t start,int32_t index,TextTrieMapSearchResultHandler * handler,UErrorCode & status) const323 TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start,
324                   int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
325     if (U_FAILURE(status)) {
326         return;
327     }
328     if (node->hasValues()) {
329         if (!handler->handleMatch(index - start, node, status)) {
330             return;
331         }
332         if (U_FAILURE(status)) {
333             return;
334         }
335     }
336     UChar32 c = text.char32At(index);
337     if (fIgnoreCase) {
338         // size of character may grow after fold operation
339         UnicodeString tmp(c);
340         tmp.foldCase();
341         int32_t tmpidx = 0;
342         while (tmpidx < tmp.length()) {
343             c = tmp.char32At(tmpidx);
344             node = getChildNode(node, c);
345             if (node == NULL) {
346                 break;
347             }
348             tmpidx = tmp.moveIndex32(tmpidx, 1);
349         }
350     } else {
351         node = getChildNode(node, c);
352     }
353     if (node != NULL) {
354         search(node, text, start, index+1, handler, status);
355     }
356 }
357 
358 // ---------------------------------------------------
359 // ZNStringPool class implementation
360 // ---------------------------------------------------
361 static const int32_t POOL_CHUNK_SIZE = 2000;
362 struct ZNStringPoolChunk: public UMemory {
363     ZNStringPoolChunk    *fNext;                       // Ptr to next pool chunk
364     int32_t               fLimit;                       // Index to start of unused area at end of fStrings
365     UChar                 fStrings[POOL_CHUNK_SIZE];    //  Strings array
366     ZNStringPoolChunk();
367 };
368 
ZNStringPoolChunk()369 ZNStringPoolChunk::ZNStringPoolChunk() {
370     fNext = NULL;
371     fLimit = 0;
372 }
373 
ZNStringPool(UErrorCode & status)374 ZNStringPool::ZNStringPool(UErrorCode &status) {
375     fChunks = NULL;
376     fHash   = NULL;
377     if (U_FAILURE(status)) {
378         return;
379     }
380     fChunks = new ZNStringPoolChunk;
381     if (fChunks == NULL) {
382         status = U_MEMORY_ALLOCATION_ERROR;
383         return;
384     }
385 
386     fHash   = uhash_open(uhash_hashUChars      /* keyHash */,
387                          uhash_compareUChars   /* keyComp */,
388                          uhash_compareUChars   /* valueComp */,
389                          &status);
390     if (U_FAILURE(status)) {
391         return;
392     }
393 }
394 
~ZNStringPool()395 ZNStringPool::~ZNStringPool() {
396     if (fHash != NULL) {
397         uhash_close(fHash);
398         fHash = NULL;
399     }
400 
401     while (fChunks != NULL) {
402         ZNStringPoolChunk *nextChunk = fChunks->fNext;
403         delete fChunks;
404         fChunks = nextChunk;
405     }
406 }
407 
408 static const UChar EmptyString = 0;
409 
get(const UChar * s,UErrorCode & status)410 const UChar *ZNStringPool::get(const UChar *s, UErrorCode &status) {
411     const UChar *pooledString;
412     if (U_FAILURE(status)) {
413         return &EmptyString;
414     }
415 
416     pooledString = static_cast<UChar *>(uhash_get(fHash, s));
417     if (pooledString != NULL) {
418         return pooledString;
419     }
420 
421     int32_t length = u_strlen(s);
422     int32_t remainingLength = POOL_CHUNK_SIZE - fChunks->fLimit;
423     if (remainingLength <= length) {
424         U_ASSERT(length < POOL_CHUNK_SIZE);
425         if (length >= POOL_CHUNK_SIZE) {
426             status = U_INTERNAL_PROGRAM_ERROR;
427             return &EmptyString;
428         }
429         ZNStringPoolChunk *oldChunk = fChunks;
430         fChunks = new ZNStringPoolChunk;
431         if (fChunks == NULL) {
432             status = U_MEMORY_ALLOCATION_ERROR;
433             return &EmptyString;
434         }
435         fChunks->fNext = oldChunk;
436     }
437 
438     UChar *destString = &fChunks->fStrings[fChunks->fLimit];
439     u_strcpy(destString, s);
440     fChunks->fLimit += (length + 1);
441     uhash_put(fHash, destString, destString, &status);
442     return destString;
443 }
444 
445 
446 //
447 //  ZNStringPool::adopt()    Put a string into the hash, but do not copy the string data
448 //                           into the pool's storage.  Used for strings from resource bundles,
449 //                           which will perisist for the life of the zone string formatter, and
450 //                           therefore can be used directly without copying.
adopt(const UChar * s,UErrorCode & status)451 const UChar *ZNStringPool::adopt(const UChar * s, UErrorCode &status) {
452     const UChar *pooledString;
453     if (U_FAILURE(status)) {
454         return &EmptyString;
455     }
456     if (s != NULL) {
457         pooledString = static_cast<UChar *>(uhash_get(fHash, s));
458         if (pooledString == NULL) {
459             UChar *ncs = const_cast<UChar *>(s);
460             uhash_put(fHash, ncs, ncs, &status);
461         }
462     }
463     return s;
464 }
465 
466 
get(const UnicodeString & s,UErrorCode & status)467 const UChar *ZNStringPool::get(const UnicodeString &s, UErrorCode &status) {
468     UnicodeString &nonConstStr = const_cast<UnicodeString &>(s);
469     return this->get(nonConstStr.getTerminatedBuffer(), status);
470 }
471 
472 /*
473  * freeze().   Close the hash table that maps to the pooled strings.
474  *             After freezing, the pool can not be searched or added to,
475  *             but all existing references to pooled strings remain valid.
476  *
477  *             The main purpose is to recover the storage used for the hash.
478  */
freeze()479 void ZNStringPool::freeze() {
480     uhash_close(fHash);
481     fHash = NULL;
482 }
483 
484 
485 // ---------------------------------------------------
486 // ZNames - names common for time zone and meta zone
487 // ---------------------------------------------------
488 class ZNames : public UMemory {
489 public:
490     virtual ~ZNames();
491 
492     static ZNames* createInstance(UResourceBundle* rb, const char* key);
493     const UChar* getName(UTimeZoneNameType type);
494 
495 protected:
496     ZNames(const UChar** names, UBool shortCommonlyUsed);
497     static const UChar** loadData(UResourceBundle* rb, const char* key, UBool& shortCommonlyUsed);
498 
499 private:
500     const UChar** fNames;
501     UBool fShortCommonlyUsed;
502 };
503 
ZNames(const UChar ** names,UBool shortCommonlyUsed)504 ZNames::ZNames(const UChar** names, UBool shortCommonlyUsed)
505 : fNames(names), fShortCommonlyUsed(shortCommonlyUsed) {
506 }
507 
~ZNames()508 ZNames::~ZNames() {
509     if (fNames != NULL) {
510         uprv_free(fNames);
511     }
512 }
513 
514 ZNames*
createInstance(UResourceBundle * rb,const char * key)515 ZNames::createInstance(UResourceBundle* rb, const char* key) {
516     UBool shortCommonlyUsed = FALSE;
517     const UChar** names = loadData(rb, key, shortCommonlyUsed);
518     if (names == NULL) {
519         // No names data available
520         return NULL;
521     }
522     return new ZNames(names, shortCommonlyUsed);
523 }
524 
525 const UChar*
getName(UTimeZoneNameType type)526 ZNames::getName(UTimeZoneNameType type) {
527     if (fNames == NULL) {
528         return NULL;
529     }
530     const UChar *name = NULL;
531     switch(type) {
532     case UTZNM_LONG_GENERIC:
533         name = fNames[0];
534         break;
535     case UTZNM_LONG_STANDARD:
536         name = fNames[1];
537         break;
538     case UTZNM_LONG_DAYLIGHT:
539         name = fNames[2];
540         break;
541     case UTZNM_SHORT_GENERIC:
542         if (fShortCommonlyUsed) {
543             name = fNames[3];
544         }
545         break;
546     case UTZNM_SHORT_STANDARD:
547         name = fNames[4];
548         break;
549     case UTZNM_SHORT_DAYLIGHT:
550         name = fNames[5];
551         break;
552     case UTZNM_SHORT_STANDARD_COMMONLY_USED:
553         if (fShortCommonlyUsed) {
554             name = fNames[4];
555         }
556         break;
557     case UTZNM_SHORT_DAYLIGHT_COMMONLY_USED:
558         if (fShortCommonlyUsed) {
559             name = fNames[5];
560         }
561         break;
562     default:
563         name = NULL;
564     }
565     return name;
566 }
567 
568 const UChar**
loadData(UResourceBundle * rb,const char * key,UBool & shortCommonlyUsed)569 ZNames::loadData(UResourceBundle* rb, const char* key, UBool& shortCommonlyUsed) {
570     if (rb == NULL || key == NULL || *key == 0) {
571         return NULL;
572     }
573 
574     UErrorCode status = U_ZERO_ERROR;
575     const UChar **names = NULL;
576 
577     UResourceBundle* rbTable = NULL;
578     rbTable = ures_getByKeyWithFallback(rb, key, rbTable, &status);
579     if (U_SUCCESS(status)) {
580         names = (const UChar **)uprv_malloc(sizeof(const UChar*) * KEYS_SIZE);
581         if (names != NULL) {
582             UBool isEmpty = TRUE;
583             for (int32_t i = 0; i < KEYS_SIZE; i++) {
584                 status = U_ZERO_ERROR;
585                 int32_t len = 0;
586                 const UChar *value = ures_getStringByKeyWithFallback(rbTable, KEYS[i], &len, &status);
587                 if (U_FAILURE(status) || len == 0) {
588                     names[i] = NULL;
589                 } else {
590                     names[i] = value;
591                     isEmpty = FALSE;
592                 }
593             }
594             if (isEmpty) {
595                 // No need to keep the names array
596                 uprv_free(names);
597                 names = NULL;
598             }
599         }
600 
601         if (names != NULL) {
602             status = U_ZERO_ERROR;
603             UResourceBundle* cuRes = ures_getByKeyWithFallback(rbTable, gCuTag, NULL, &status);
604             int32_t cu = ures_getInt(cuRes, &status);
605             if (U_SUCCESS(status)) {
606                 shortCommonlyUsed = (cu != 0);
607             }
608             ures_close(cuRes);
609         }
610     }
611     ures_close(rbTable);
612     return names;
613 }
614 
615 // ---------------------------------------------------
616 // TZNames - names for a time zone
617 // ---------------------------------------------------
618 class TZNames : public ZNames {
619 public:
620     virtual ~TZNames();
621 
622     static TZNames* createInstance(UResourceBundle* rb, const char* key);
623     const UChar* getLocationName(void);
624 
625 private:
626     TZNames(const UChar** names, UBool shortCommonlyUsed, const UChar* locationName);
627     const UChar* fLocationName;
628 };
629 
TZNames(const UChar ** names,UBool shortCommonlyUsed,const UChar * locationName)630 TZNames::TZNames(const UChar** names, UBool shortCommonlyUsed, const UChar* locationName)
631 : ZNames(names, shortCommonlyUsed), fLocationName(locationName) {
632 }
633 
~TZNames()634 TZNames::~TZNames() {
635 }
636 
637 const UChar*
getLocationName()638 TZNames::getLocationName() {
639     return fLocationName;
640 }
641 
642 TZNames*
createInstance(UResourceBundle * rb,const char * key)643 TZNames::createInstance(UResourceBundle* rb, const char* key) {
644     if (rb == NULL || key == NULL || *key == 0) {
645         return NULL;
646     }
647     TZNames* tznames = NULL;
648     UErrorCode status = U_ZERO_ERROR;
649     UResourceBundle* rbTable = ures_getByKeyWithFallback(rb, key, NULL, &status);
650     if (U_SUCCESS(status)) {
651         int32_t len = 0;
652         const UChar* locationName = ures_getStringByKeyWithFallback(rbTable, gEcTag, &len, &status);
653         if (U_FAILURE(status) || len == 0) {
654             locationName = NULL;
655         }
656 
657         UBool shortCommonlyUsed = FALSE;
658         const UChar** names = loadData(rb, key, shortCommonlyUsed);
659 
660         if (locationName != NULL || names != NULL) {
661             tznames = new TZNames(names, shortCommonlyUsed, locationName);
662         }
663     }
664     ures_close(rbTable);
665     return tznames;
666 }
667 
668 // ---------------------------------------------------
669 // The meta zone ID enumeration class
670 // ---------------------------------------------------
671 class MetaZoneIDsEnumeration : public StringEnumeration {
672 public:
673     MetaZoneIDsEnumeration();
674     MetaZoneIDsEnumeration(const UVector& mzIDs);
675     MetaZoneIDsEnumeration(UVector* mzIDs);
676     virtual ~MetaZoneIDsEnumeration();
677     static UClassID U_EXPORT2 getStaticClassID(void);
678     virtual UClassID getDynamicClassID(void) const;
679     virtual const UnicodeString* snext(UErrorCode& status);
680     virtual void reset(UErrorCode& status);
681     virtual int32_t count(UErrorCode& status) const;
682 private:
683     int32_t fLen;
684     int32_t fPos;
685     const UVector* fMetaZoneIDs;
686     UVector *fLocalVector;
687 };
688 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration)689 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration)
690 
691 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration()
692 : fLen(0), fPos(0), fMetaZoneIDs(NULL), fLocalVector(NULL) {
693 }
694 
MetaZoneIDsEnumeration(const UVector & mzIDs)695 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector& mzIDs)
696 : fPos(0), fMetaZoneIDs(&mzIDs), fLocalVector(NULL) {
697     fLen = fMetaZoneIDs->size();
698 }
699 
MetaZoneIDsEnumeration(UVector * mzIDs)700 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector *mzIDs)
701 : fLen(0), fPos(0), fMetaZoneIDs(mzIDs), fLocalVector(mzIDs) {
702     if (fMetaZoneIDs) {
703         fLen = fMetaZoneIDs->size();
704     }
705 }
706 
707 const UnicodeString*
snext(UErrorCode & status)708 MetaZoneIDsEnumeration::snext(UErrorCode& status) {
709     if (U_SUCCESS(status) && fMetaZoneIDs != NULL && fPos < fLen) {
710         unistr.setTo((const UChar*)fMetaZoneIDs->elementAt(fPos++), -1);
711         return &unistr;
712     }
713     return NULL;
714 }
715 
716 void
reset(UErrorCode &)717 MetaZoneIDsEnumeration::reset(UErrorCode& /*status*/) {
718     fPos = 0;
719 }
720 
721 int32_t
count(UErrorCode &) const722 MetaZoneIDsEnumeration::count(UErrorCode& /*status*/) const {
723     return fLen;
724 }
725 
~MetaZoneIDsEnumeration()726 MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
727     if (fLocalVector) {
728         delete fLocalVector;
729     }
730 }
731 
732 U_CDECL_BEGIN
733 /**
734  * ZNameInfo stores zone name information in the trie
735  */
736 typedef struct ZNameInfo {
737     UTimeZoneNameType   type;
738     const UChar*        tzID;
739     const UChar*        mzID;
740 } ZNameInfo;
741 
742 /**
743  * ZMatchInfo stores zone name match information used by find method
744  */
745 typedef struct ZMatchInfo {
746     const ZNameInfo*    znameInfo;
747     int32_t             matchLength;
748 } ZMatchInfo;
749 U_CDECL_END
750 
751 // ---------------------------------------------------
752 // The class stores time zone name match information
753 // ---------------------------------------------------
754 class TimeZoneNameMatchInfoImpl : public TimeZoneNameMatchInfo {
755 public:
756     TimeZoneNameMatchInfoImpl(UVector* matches);
757     ~TimeZoneNameMatchInfoImpl();
758 
759     int32_t size() const;
760     UTimeZoneNameType getNameType(int32_t index) const;
761     int32_t getMatchLength(int32_t index) const;
762     UnicodeString& getTimeZoneID(int32_t index, UnicodeString& tzID) const;
763     UnicodeString& getMetaZoneID(int32_t index, UnicodeString& mzID) const;
764 
765 private:
766     UVector* fMatches;  // vector of MatchEntry
767 };
768 
TimeZoneNameMatchInfoImpl(UVector * matches)769 TimeZoneNameMatchInfoImpl::TimeZoneNameMatchInfoImpl(UVector* matches)
770 : fMatches(matches) {
771 }
772 
~TimeZoneNameMatchInfoImpl()773 TimeZoneNameMatchInfoImpl::~TimeZoneNameMatchInfoImpl() {
774     if (fMatches != NULL) {
775         delete fMatches;
776     }
777 }
778 
779 int32_t
size() const780 TimeZoneNameMatchInfoImpl::size() const {
781     if (fMatches == NULL) {
782         return 0;
783     }
784     return fMatches->size();
785 }
786 
787 UTimeZoneNameType
getNameType(int32_t index) const788 TimeZoneNameMatchInfoImpl::getNameType(int32_t index) const {
789     ZMatchInfo *minfo = (ZMatchInfo *)fMatches->elementAt(index);
790     if (minfo != NULL) {
791         return minfo->znameInfo->type;
792     }
793     return UTZNM_UNKNOWN;
794 }
795 
796 int32_t
getMatchLength(int32_t index) const797 TimeZoneNameMatchInfoImpl::getMatchLength(int32_t index) const {
798     ZMatchInfo *minfo = (ZMatchInfo *)fMatches->elementAt(index);
799     if (minfo != NULL) {
800         return minfo->matchLength;
801     }
802     return -1;
803 }
804 
805 UnicodeString&
getTimeZoneID(int32_t index,UnicodeString & tzID) const806 TimeZoneNameMatchInfoImpl::getTimeZoneID(int32_t index, UnicodeString& tzID) const {
807     ZMatchInfo *minfo = (ZMatchInfo *)fMatches->elementAt(index);
808     if (minfo != NULL && minfo->znameInfo->tzID != NULL) {
809         tzID.setTo(TRUE, minfo->znameInfo->tzID, -1);
810     } else {
811         tzID.setToBogus();
812     }
813     return tzID;
814 }
815 
816 UnicodeString&
getMetaZoneID(int32_t index,UnicodeString & mzID) const817 TimeZoneNameMatchInfoImpl::getMetaZoneID(int32_t index, UnicodeString& mzID) const {
818     ZMatchInfo *minfo = (ZMatchInfo *)fMatches->elementAt(index);
819     if (minfo != NULL && minfo->znameInfo->mzID != NULL) {
820         mzID.setTo(TRUE, minfo->znameInfo->mzID, -1);
821     } else {
822         mzID.setToBogus();
823     }
824     return mzID;
825 }
826 
827 // ---------------------------------------------------
828 // ZNameSearchHandler
829 // ---------------------------------------------------
830 class ZNameSearchHandler : public TextTrieMapSearchResultHandler {
831 public:
832     ZNameSearchHandler(uint32_t types);
833     virtual ~ZNameSearchHandler();
834 
835     UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
836     UVector* getMatches(int32_t& maxMatchLen);
837 
838 private:
839     uint32_t fTypes;
840     UVector* fResults;
841     int32_t fMaxMatchLen;
842 };
843 
ZNameSearchHandler(uint32_t types)844 ZNameSearchHandler::ZNameSearchHandler(uint32_t types)
845 : fTypes(types), fResults(NULL), fMaxMatchLen(0) {
846 }
847 
~ZNameSearchHandler()848 ZNameSearchHandler::~ZNameSearchHandler() {
849     if (fResults != NULL) {
850         delete fResults;
851     }
852 }
853 
854 UBool
handleMatch(int32_t matchLength,const CharacterNode * node,UErrorCode & status)855 ZNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
856     if (U_FAILURE(status)) {
857         return FALSE;
858     }
859     if (node->hasValues()) {
860         int32_t valuesCount = node->countValues();
861         for (int32_t i = 0; i < valuesCount; i++) {
862             ZNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
863             if (nameinfo == NULL) {
864                 break;
865             }
866             if ((nameinfo->type & fTypes) != 0) {
867                 // matches a requested type
868                 if (fResults == NULL) {
869                     fResults = new UVector(uprv_free, NULL, status);
870                     if (fResults == NULL) {
871                         status = U_MEMORY_ALLOCATION_ERROR;
872                     }
873                 }
874                 if (U_SUCCESS(status)) {
875                     ZMatchInfo *zmatch = (ZMatchInfo *)uprv_malloc(sizeof(ZMatchInfo));
876                     if (zmatch == NULL) {
877                         status = U_MEMORY_ALLOCATION_ERROR;
878                     } else {
879                         // add the match to the vector
880                         zmatch->znameInfo = nameinfo;
881                         zmatch->matchLength = matchLength;
882                         fResults->addElement(zmatch, status);
883                         if (U_FAILURE(status)) {
884                             uprv_free(zmatch);
885                         } else {
886                             if (matchLength > fMaxMatchLen) {
887                                 fMaxMatchLen = matchLength;
888                             }
889                         }
890                     }
891                 }
892             }
893         }
894     }
895     return TRUE;
896 }
897 
898 UVector*
getMatches(int32_t & maxMatchLen)899 ZNameSearchHandler::getMatches(int32_t& maxMatchLen) {
900     // give the ownership to the caller
901     UVector *results = fResults;
902     maxMatchLen = fMaxMatchLen;
903 
904     // reset
905     fResults = NULL;
906     fMaxMatchLen = 0;
907     return results;
908 }
909 
910 // ---------------------------------------------------
911 // TimeZoneNamesImpl
912 //
913 // TimeZoneNames implementation class. This is the main
914 // part of this module.
915 // ---------------------------------------------------
916 
917 U_CDECL_BEGIN
918 /**
919  * Deleter for ZNames
920  */
921 static void U_CALLCONV
deleteZNames(void * obj)922 deleteZNames(void *obj) {
923     if (obj != EMPTY) {
924         delete (ZNames *)obj;
925     }
926 }
927 /**
928  * Deleter for TZNames
929  */
930 static void U_CALLCONV
deleteTZNames(void * obj)931 deleteTZNames(void *obj) {
932     if (obj != EMPTY) {
933         delete (TZNames *)obj;
934     }
935 }
936 
937 /**
938  * Deleter for ZNameInfo
939  */
940 static void U_CALLCONV
deleteZNameInfo(void * obj)941 deleteZNameInfo(void *obj) {
942     uprv_free(obj);
943 }
944 
945 U_CDECL_END
946 
TimeZoneNamesImpl(const Locale & locale,UErrorCode & status)947 TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status)
948 : fLocale(locale),
949   fLock(NULL),
950   fZoneStrings(NULL),
951   fTZNamesMap(NULL),
952   fMZNamesMap(NULL),
953   fNamesTrieFullyLoaded(FALSE),
954   fNamesTrie(TRUE, deleteZNameInfo) {
955     initialize(locale, status);
956 }
957 
958 void
initialize(const Locale & locale,UErrorCode & status)959 TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) {
960     if (U_FAILURE(status)) {
961         return;
962     }
963 
964     // Load zoneStrings bundle
965     UErrorCode tmpsts = U_ZERO_ERROR;   // OK with fallback warning..
966     fZoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
967     fZoneStrings = ures_getByKeyWithFallback(fZoneStrings, gZoneStrings, fZoneStrings, &tmpsts);
968     if (U_FAILURE(tmpsts)) {
969         status = tmpsts;
970         cleanup();
971         return;
972     }
973 
974     // Initialize hashtables holding time zone/meta zone names
975     fMZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
976     fTZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
977     if (U_FAILURE(status)) {
978         cleanup();
979         return;
980     }
981 
982     uhash_setValueDeleter(fMZNamesMap, deleteZNames);
983     uhash_setValueDeleter(fTZNamesMap, deleteTZNames);
984     // no key deleters for name maps
985 
986     // preload zone strings for the default zone
987     TimeZone *tz = TimeZone::createDefault();
988     const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
989     if (tzID != NULL) {
990         loadStrings(UnicodeString(tzID));
991     }
992     delete tz;
993 
994     return;
995 }
996 
997 /*
998  * This method updates the cache and must be called with a lock,
999  * except initializer.
1000  */
1001 void
loadStrings(const UnicodeString & tzCanonicalID)1002 TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID) {
1003     loadTimeZoneNames(tzCanonicalID);
1004 
1005     UErrorCode status = U_ZERO_ERROR;
1006     StringEnumeration *mzIDs = getAvailableMetaZoneIDs(tzCanonicalID, status);
1007     if (U_SUCCESS(status) && mzIDs != NULL) {
1008         const UnicodeString *mzID;
1009         while ((mzID = mzIDs->snext(status))) {
1010             if (U_FAILURE(status)) {
1011                 break;
1012             }
1013             loadMetaZoneNames(*mzID);
1014         }
1015         delete mzIDs;
1016     }
1017 }
1018 
~TimeZoneNamesImpl()1019 TimeZoneNamesImpl::~TimeZoneNamesImpl() {
1020     cleanup();
1021     umtx_destroy(&fLock);
1022 }
1023 
1024 void
cleanup()1025 TimeZoneNamesImpl::cleanup() {
1026     if (fZoneStrings != NULL) {
1027         ures_close(fZoneStrings);
1028         fZoneStrings = NULL;
1029     }
1030     if (fMZNamesMap != NULL) {
1031         uhash_close(fMZNamesMap);
1032         fMZNamesMap = NULL;
1033     }
1034     if (fTZNamesMap != NULL) {
1035         uhash_close(fTZNamesMap);
1036         fTZNamesMap = NULL;
1037     }
1038 }
1039 
1040 StringEnumeration*
getAvailableMetaZoneIDs(UErrorCode & status) const1041 TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const {
1042     if (U_FAILURE(status)) {
1043         return NULL;
1044     }
1045     const UVector* mzIDs = ZoneMeta::getAvailableMetazoneIDs();
1046     if (mzIDs == NULL) {
1047         return new MetaZoneIDsEnumeration();
1048     }
1049     return new MetaZoneIDsEnumeration(*mzIDs);
1050 }
1051 
1052 StringEnumeration*
getAvailableMetaZoneIDs(const UnicodeString & tzID,UErrorCode & status) const1053 TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
1054     if (U_FAILURE(status)) {
1055         return NULL;
1056     }
1057     const UVector* mappings = ZoneMeta::getMetazoneMappings(tzID);
1058     if (mappings == NULL) {
1059         return new MetaZoneIDsEnumeration();
1060     }
1061 
1062     MetaZoneIDsEnumeration *senum = NULL;
1063     UVector* mzIDs = new UVector(NULL, uhash_compareUChars, status);
1064     if (mzIDs == NULL) {
1065         status = U_MEMORY_ALLOCATION_ERROR;
1066     }
1067     if (U_SUCCESS(status)) {
1068         for (int32_t i = 0; U_SUCCESS(status) && i < mappings->size(); i++) {
1069 
1070             OlsonToMetaMappingEntry *map = (OlsonToMetaMappingEntry *)mappings->elementAt(i);
1071             const UChar *mzID = map->mzid;
1072             if (!mzIDs->contains((void *)mzID)) {
1073                 mzIDs->addElement((void *)mzID, status);
1074             }
1075         }
1076         if (U_SUCCESS(status)) {
1077             senum = new MetaZoneIDsEnumeration(mzIDs);
1078         } else {
1079             delete mzIDs;
1080         }
1081     }
1082     return senum;
1083 }
1084 
1085 UnicodeString&
getMetaZoneID(const UnicodeString & tzID,UDate date,UnicodeString & mzID) const1086 TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
1087     ZoneMeta::getMetazoneID(tzID, date, mzID);
1088     return mzID;
1089 }
1090 
1091 UnicodeString&
getReferenceZoneID(const UnicodeString & mzID,const char * region,UnicodeString & tzID) const1092 TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
1093     ZoneMeta::getZoneIdByMetazone(mzID, UnicodeString(region, -1, US_INV), tzID);
1094     return tzID;
1095 }
1096 
1097 UnicodeString&
getMetaZoneDisplayName(const UnicodeString & mzID,UTimeZoneNameType type,UnicodeString & name) const1098 TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID,
1099                                           UTimeZoneNameType type,
1100                                           UnicodeString& name) const {
1101     name.setToBogus();  // cleanup result.
1102     if (mzID.isEmpty()) {
1103         return name;
1104     }
1105 
1106     ZNames *znames = NULL;
1107     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1108 
1109     umtx_lock(&nonConstThis->fLock);
1110     {
1111         znames = nonConstThis->loadMetaZoneNames(mzID);
1112     }
1113     umtx_unlock(&nonConstThis->fLock);
1114 
1115     if (znames != NULL) {
1116         const UChar* s = znames->getName(type);
1117         if (s != NULL) {
1118             name.setTo(TRUE, s, -1);
1119         }
1120     }
1121     return name;
1122 }
1123 
1124 UnicodeString&
getTimeZoneDisplayName(const UnicodeString & tzID,UTimeZoneNameType type,UnicodeString & name) const1125 TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
1126     name.setToBogus();  // cleanup result.
1127     if (tzID.isEmpty()) {
1128         return name;
1129     }
1130 
1131     TZNames *tznames = NULL;
1132     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1133 
1134     umtx_lock(&nonConstThis->fLock);
1135     {
1136         tznames = nonConstThis->loadTimeZoneNames(tzID);
1137     }
1138     umtx_unlock(&nonConstThis->fLock);
1139 
1140     if (tznames != NULL) {
1141         const UChar *s = tznames->getName(type);
1142         if (s != NULL) {
1143             name.setTo(TRUE, s, -1);
1144         }
1145     }
1146     return name;
1147 }
1148 
1149 UnicodeString&
getExemplarLocationName(const UnicodeString & tzID,UnicodeString & name) const1150 TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
1151     const UChar* locName = NULL;
1152     TZNames *tznames = NULL;
1153     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1154 
1155     umtx_lock(&nonConstThis->fLock);
1156     {
1157         tznames = nonConstThis->loadTimeZoneNames(tzID);
1158     }
1159     umtx_unlock(&nonConstThis->fLock);
1160 
1161     if (tznames != NULL) {
1162         locName = tznames->getLocationName();
1163     }
1164     if (locName != NULL) {
1165         name.setTo(TRUE, locName, -1);
1166         return name;
1167     }
1168 
1169     return TimeZoneNames::getExemplarLocationName(tzID, name);
1170 }
1171 
1172 
1173 // Merge the MZ_PREFIX and mzId
mergeTimeZoneKey(const UnicodeString & mzID,char * result)1174 static void mergeTimeZoneKey(const UnicodeString& mzID, char* result) {
1175     if (mzID.isEmpty()) {
1176         result[0] = '\0';
1177         return;
1178     }
1179 
1180     char mzIdChar[ZID_KEY_MAX + 1];
1181     int32_t keyLen;
1182     int32_t prefixLen = uprv_strlen(gMZPrefix);
1183     keyLen = mzID.extract(0, mzID.length(), mzIdChar, ZID_KEY_MAX + 1, US_INV);
1184     uprv_memcpy((void *)result, (void *)gMZPrefix, prefixLen);
1185     uprv_memcpy((void *)(result + prefixLen), (void *)mzIdChar, keyLen);
1186     result[keyLen + prefixLen] = '\0';
1187 }
1188 
1189 /*
1190  * This method updates the cache and must be called with a lock
1191  */
1192 ZNames*
loadMetaZoneNames(const UnicodeString & mzID)1193 TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID) {
1194     if (mzID.length() > (ZID_KEY_MAX - MZ_PREFIX_LEN)) {
1195         return NULL;
1196     }
1197 
1198     ZNames *znames = NULL;
1199 
1200     UErrorCode status = U_ZERO_ERROR;
1201     UChar mzIDKey[ZID_KEY_MAX + 1];
1202     mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status);
1203     U_ASSERT(status == U_ZERO_ERROR);   // already checked length above
1204     mzIDKey[mzID.length()] = 0;
1205 
1206     void *cacheVal = uhash_get(fMZNamesMap, mzIDKey);
1207     if (cacheVal == NULL) {
1208         char key[ZID_KEY_MAX + 1];
1209         mergeTimeZoneKey(mzID, key);
1210         znames = ZNames::createInstance(fZoneStrings, key);
1211 
1212         if (znames == NULL) {
1213             cacheVal = (void *)EMPTY;
1214         } else {
1215             cacheVal = znames;
1216         }
1217         // Use the persistent ID as the resource key, so we can
1218         // avoid duplications.
1219         const UChar* newKey = ZoneMeta::findMetaZoneID(mzID);
1220         if (newKey != NULL) {
1221             uhash_put(fMZNamesMap, (void *)newKey, cacheVal, &status);
1222             if (U_FAILURE(status)) {
1223                 if (znames != NULL) {
1224                     delete znames;
1225                 }
1226             } else if (znames != NULL) {
1227                 // put the name info into the trie
1228                 for (int32_t i = 0; ALL_NAME_TYPES[i] != UTZNM_UNKNOWN; i++) {
1229                     const UChar* name = znames->getName(ALL_NAME_TYPES[i]);
1230                     if (name != NULL) {
1231                         ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo));
1232                         if (nameinfo != NULL) {
1233                             nameinfo->type = ALL_NAME_TYPES[i];
1234                             nameinfo->tzID = NULL;
1235                             nameinfo->mzID = newKey;
1236                             fNamesTrie.put(name, nameinfo, status);
1237                         }
1238                     }
1239                 }
1240             }
1241 
1242         } else {
1243             // Should never happen with a valid input
1244             if (znames != NULL) {
1245                 // It's not possible that we get a valid ZNames with unknown ID.
1246                 // But just in case..
1247                 delete znames;
1248                 znames = NULL;
1249             }
1250         }
1251     } else if (cacheVal != EMPTY) {
1252         znames = (ZNames *)cacheVal;
1253     }
1254 
1255     return znames;
1256 }
1257 
1258 /*
1259  * This method updates the cache and must be called with a lock
1260  */
1261 TZNames*
loadTimeZoneNames(const UnicodeString & tzID)1262 TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID) {
1263     if (tzID.length() > ZID_KEY_MAX) {
1264         return NULL;
1265     }
1266 
1267     TZNames *tznames = NULL;
1268 
1269     UErrorCode status = U_ZERO_ERROR;
1270     UChar tzIDKey[ZID_KEY_MAX + 1];
1271     int32_t tzIDKeyLen = tzID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
1272     U_ASSERT(status == U_ZERO_ERROR);   // already checked length above
1273     tzIDKey[tzIDKeyLen] = 0;
1274 
1275     void *cacheVal = uhash_get(fTZNamesMap, tzIDKey);
1276     if (cacheVal == NULL) {
1277         char key[ZID_KEY_MAX + 1];
1278         UErrorCode status = U_ZERO_ERROR;
1279         // Replace "/" with ":".
1280         UnicodeString uKey(tzID);
1281         for (int32_t i = 0; i < uKey.length(); i++) {
1282             if (uKey.charAt(i) == (UChar)0x2F) {
1283                 uKey.setCharAt(i, (UChar)0x3A);
1284             }
1285         }
1286         uKey.extract(0, uKey.length(), key, sizeof(key), US_INV);
1287         tznames = TZNames::createInstance(fZoneStrings, key);
1288 
1289         if (tznames == NULL) {
1290             cacheVal = (void *)EMPTY;
1291         } else {
1292             cacheVal = tznames;
1293         }
1294         // Use the persistent ID as the resource key, so we can
1295         // avoid duplications.
1296         const UChar* newKey = ZoneMeta::findTimeZoneID(tzID);
1297         if (newKey != NULL) {
1298             uhash_put(fTZNamesMap, (void *)newKey, cacheVal, &status);
1299             if (U_FAILURE(status)) {
1300                 if (tznames != NULL) {
1301                     delete tznames;
1302                 }
1303             } else if (tznames != NULL) {
1304                 // put the name info into the trie
1305                 for (int32_t i = 0; ALL_NAME_TYPES[i] != UTZNM_UNKNOWN; i++) {
1306                     const UChar* name = tznames->getName(ALL_NAME_TYPES[i]);
1307                     if (name != NULL) {
1308                         ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo));
1309                         if (nameinfo != NULL) {
1310                             nameinfo->type = ALL_NAME_TYPES[i];
1311                             nameinfo->tzID = newKey;
1312                             nameinfo->mzID = NULL;
1313                             fNamesTrie.put(name, nameinfo, status);
1314                         }
1315                     }
1316                 }
1317             }
1318         } else {
1319             // Should never happen with a valid input
1320             if (tznames != NULL) {
1321                 // It's not possible that we get a valid TZNames with unknown ID.
1322                 // But just in case..
1323                 delete tznames;
1324                 tznames = NULL;
1325             }
1326         }
1327     } else if (cacheVal != EMPTY) {
1328         tznames = (TZNames *)cacheVal;
1329     }
1330 
1331     return tznames;
1332 }
1333 
1334 TimeZoneNameMatchInfo*
find(const UnicodeString & text,int32_t start,uint32_t types,UErrorCode & status) const1335 TimeZoneNamesImpl::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
1336     ZNameSearchHandler handler(types);
1337 
1338     TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1339 
1340     umtx_lock(&nonConstThis->fLock);
1341     {
1342         fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
1343     }
1344     umtx_unlock(&nonConstThis->fLock);
1345 
1346     if (U_FAILURE(status)) {
1347         return NULL;
1348     }
1349 
1350     TimeZoneNameMatchInfoImpl *matchInfo = NULL;
1351 
1352     int32_t maxLen = 0;
1353     UVector *results = handler.getMatches(maxLen);
1354     if (results != NULL && ((maxLen == (text.length() - start)) || fNamesTrieFullyLoaded)) {
1355         // perfect match
1356         matchInfo = new TimeZoneNameMatchInfoImpl(results);
1357         if (matchInfo == NULL) {
1358             status = U_MEMORY_ALLOCATION_ERROR;
1359             delete results;
1360             return NULL;
1361         }
1362         return matchInfo;
1363     }
1364 
1365     if (results != NULL) {
1366         delete results;
1367     }
1368 
1369     // All names are not yet loaded into the trie
1370     umtx_lock(&nonConstThis->fLock);
1371     {
1372         if (!fNamesTrieFullyLoaded) {
1373             const UnicodeString *id;
1374 
1375             // load strings for all zones
1376             StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
1377             if (U_SUCCESS(status)) {
1378                 while ((id = tzIDs->snext(status))) {
1379                     if (U_FAILURE(status)) {
1380                         break;
1381                     }
1382                     // loadStrings also load related metazone strings
1383                     nonConstThis->loadStrings(*id);
1384                 }
1385             }
1386             if (tzIDs != NULL) {
1387                 delete tzIDs;
1388             }
1389             if (U_SUCCESS(status)) {
1390                 nonConstThis->fNamesTrieFullyLoaded = TRUE;
1391             }
1392         }
1393     }
1394     umtx_unlock(&nonConstThis->fLock);
1395 
1396     if (U_FAILURE(status)) {
1397         return NULL;
1398     }
1399 
1400     umtx_lock(&nonConstThis->fLock);
1401     {
1402         // now try it again
1403         fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
1404     }
1405     umtx_unlock(&nonConstThis->fLock);
1406 
1407     results = handler.getMatches(maxLen);
1408     if (results != NULL && maxLen > 0) {
1409         matchInfo = new TimeZoneNameMatchInfoImpl(results);
1410         if (matchInfo == NULL) {
1411             status = U_MEMORY_ALLOCATION_ERROR;
1412             delete results;
1413             return NULL;
1414         }
1415     }
1416 
1417     return matchInfo;
1418 }
1419 
1420 
1421 U_NAMESPACE_END
1422 
1423 
1424 #endif /* #if !UCONFIG_NO_FORMATTING */
1425 
1426 //eof
1427