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