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