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 // --------------------------------------------------- 59 void CharacterNode::clear() { 60 uprv_memset(this, 0, sizeof(*this)); 61 } 62 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 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 // --------------------------------------------------- 109 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){ 110 } 111 112 // --------------------------------------------------- 113 // TextTrieMap class implementation 114 // --------------------------------------------------- 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 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 136 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 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 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 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 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* 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* 269 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. 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 308 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 323 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 369 ZNStringPoolChunk::ZNStringPoolChunk() { 370 fNext = NULL; 371 fLimit = 0; 372 } 373 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 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 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. 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 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 */ 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 504 ZNames::ZNames(const UChar** names, UBool shortCommonlyUsed) 505 : fNames(names), fShortCommonlyUsed(shortCommonlyUsed) { 506 } 507 508 ZNames::~ZNames() { 509 if (fNames != NULL) { 510 uprv_free(fNames); 511 } 512 } 513 514 ZNames* 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* 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** 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 630 TZNames::TZNames(const UChar** names, UBool shortCommonlyUsed, const UChar* locationName) 631 : ZNames(names, shortCommonlyUsed), fLocationName(locationName) { 632 } 633 634 TZNames::~TZNames() { 635 } 636 637 const UChar* 638 TZNames::getLocationName() { 639 return fLocationName; 640 } 641 642 TZNames* 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 689 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration) 690 691 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration() 692 : fLen(0), fPos(0), fMetaZoneIDs(NULL), fLocalVector(NULL) { 693 } 694 695 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector& mzIDs) 696 : fPos(0), fMetaZoneIDs(&mzIDs), fLocalVector(NULL) { 697 fLen = fMetaZoneIDs->size(); 698 } 699 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* 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 717 MetaZoneIDsEnumeration::reset(UErrorCode& /*status*/) { 718 fPos = 0; 719 } 720 721 int32_t 722 MetaZoneIDsEnumeration::count(UErrorCode& /*status*/) const { 723 return fLen; 724 } 725 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 769 TimeZoneNameMatchInfoImpl::TimeZoneNameMatchInfoImpl(UVector* matches) 770 : fMatches(matches) { 771 } 772 773 TimeZoneNameMatchInfoImpl::~TimeZoneNameMatchInfoImpl() { 774 if (fMatches != NULL) { 775 delete fMatches; 776 } 777 } 778 779 int32_t 780 TimeZoneNameMatchInfoImpl::size() const { 781 if (fMatches == NULL) { 782 return 0; 783 } 784 return fMatches->size(); 785 } 786 787 UTimeZoneNameType 788 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 797 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& 806 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& 817 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 844 ZNameSearchHandler::ZNameSearchHandler(uint32_t types) 845 : fTypes(types), fResults(NULL), fMaxMatchLen(0) { 846 } 847 848 ZNameSearchHandler::~ZNameSearchHandler() { 849 if (fResults != NULL) { 850 delete fResults; 851 } 852 } 853 854 UBool 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* 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 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 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 941 deleteZNameInfo(void *obj) { 942 uprv_free(obj); 943 } 944 945 U_CDECL_END 946 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 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 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 1019 TimeZoneNamesImpl::~TimeZoneNamesImpl() { 1020 cleanup(); 1021 umtx_destroy(&fLock); 1022 } 1023 1024 void 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* 1041 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* 1053 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& 1086 TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const { 1087 ZoneMeta::getMetazoneID(tzID, date, mzID); 1088 return mzID; 1089 } 1090 1091 UnicodeString& 1092 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& 1098 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& 1125 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& 1150 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 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* 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* 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* 1335 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