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