tznames_impl.cpp (78282B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2011-2016, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ******************************************************************************* 8 * 9 * File TZNAMES_IMPL.CPP 10 * 11 ******************************************************************************* 12 */ 13 14 #include "unicode/utypes.h" 15 16 #if !UCONFIG_NO_FORMATTING 17 18 #include "unicode/strenum.h" 19 #include "unicode/stringpiece.h" 20 #include "unicode/ustring.h" 21 #include "unicode/timezone.h" 22 #include "unicode/utf16.h" 23 24 #include "tznames_impl.h" 25 #include "charstr.h" 26 #include "cmemory.h" 27 #include "cstring.h" 28 #include "uassert.h" 29 #include "mutex.h" 30 #include "resource.h" 31 #include "ulocimp.h" 32 #include "uresimp.h" 33 #include "ureslocs.h" 34 #include "zonemeta.h" 35 #include "ucln_in.h" 36 #include "uinvchar.h" 37 #include "uvector.h" 38 #include "olsontz.h" 39 40 U_NAMESPACE_BEGIN 41 42 #define ZID_KEY_MAX 128 43 #define MZ_PREFIX_LEN 5 44 45 static const char gZoneStrings[] = "zoneStrings"; 46 static const char gMZPrefix[] = "meta:"; 47 48 static const char EMPTY[] = "<empty>"; // place holder for empty ZNames 49 static const char DUMMY_LOADER[] = "<dummy>"; // place holder for dummy ZNamesLoader 50 static const char16_t NO_NAME[] = { 0 }; // for empty no-fallback time zone names 51 52 // stuff for TZDBTimeZoneNames 53 static const char* TZDBNAMES_KEYS[] = {"ss", "sd"}; 54 static const int32_t TZDBNAMES_KEYS_SIZE = UPRV_LENGTHOF(TZDBNAMES_KEYS); 55 56 static UMutex gDataMutex; 57 58 static UHashtable* gTZDBNamesMap = nullptr; 59 static icu::UInitOnce gTZDBNamesMapInitOnce {}; 60 61 static TextTrieMap* gTZDBNamesTrie = nullptr; 62 static icu::UInitOnce gTZDBNamesTrieInitOnce {}; 63 64 // The order in which strings are stored may be different than the order in the public enum. 65 enum UTimeZoneNameTypeIndex { 66 UTZNM_INDEX_UNKNOWN = -1, 67 UTZNM_INDEX_EXEMPLAR_LOCATION, 68 UTZNM_INDEX_LONG_GENERIC, 69 UTZNM_INDEX_LONG_STANDARD, 70 UTZNM_INDEX_LONG_DAYLIGHT, 71 UTZNM_INDEX_SHORT_GENERIC, 72 UTZNM_INDEX_SHORT_STANDARD, 73 UTZNM_INDEX_SHORT_DAYLIGHT, 74 UTZNM_INDEX_COUNT 75 }; 76 static const char16_t* const EMPTY_NAMES[UTZNM_INDEX_COUNT] = { 77 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr 78 }; 79 80 U_CDECL_BEGIN 81 static UBool U_CALLCONV tzdbTimeZoneNames_cleanup() { 82 if (gTZDBNamesMap != nullptr) { 83 uhash_close(gTZDBNamesMap); 84 gTZDBNamesMap = nullptr; 85 } 86 gTZDBNamesMapInitOnce.reset(); 87 88 if (gTZDBNamesTrie != nullptr) { 89 delete gTZDBNamesTrie; 90 gTZDBNamesTrie = nullptr; 91 } 92 gTZDBNamesTrieInitOnce.reset(); 93 94 return true; 95 } 96 U_CDECL_END 97 98 /** 99 * ZNameInfo stores zone name information in the trie 100 */ 101 struct ZNameInfo { 102 UTimeZoneNameType type; 103 const char16_t* tzID; 104 const char16_t* mzID; 105 }; 106 107 /** 108 * ZMatchInfo stores zone name match information used by find method 109 */ 110 struct ZMatchInfo { 111 const ZNameInfo* znameInfo; 112 int32_t matchLength; 113 }; 114 115 // Helper functions 116 static void mergeTimeZoneKey(const UnicodeString& mzID, char* result, size_t capacity, UErrorCode& status); 117 118 #define DEFAULT_CHARACTERNODE_CAPACITY 1 119 120 // --------------------------------------------------- 121 // CharacterNode class implementation 122 // --------------------------------------------------- 123 void CharacterNode::clear() { 124 uprv_memset(this, 0, sizeof(*this)); 125 } 126 127 void CharacterNode::deleteValues(UObjectDeleter *valueDeleter) { 128 if (fValues == nullptr) { 129 // Do nothing. 130 } else if (!fHasValuesVector) { 131 if (valueDeleter) { 132 valueDeleter(fValues); 133 } 134 } else { 135 delete static_cast<UVector*>(fValues); 136 } 137 } 138 139 void 140 CharacterNode::addValue(void *value, UObjectDeleter *valueDeleter, UErrorCode &status) { 141 if (U_FAILURE(status)) { 142 if (valueDeleter) { 143 valueDeleter(value); 144 } 145 return; 146 } 147 if (fValues == nullptr) { 148 fValues = value; 149 } else { 150 // At least one value already. 151 if (!fHasValuesVector) { 152 // There is only one value so far, and not in a vector yet. 153 // Create a vector and add the old value. 154 LocalPointer<UVector> values( 155 new UVector(valueDeleter, nullptr, DEFAULT_CHARACTERNODE_CAPACITY, status), status); 156 if (U_FAILURE(status)) { 157 if (valueDeleter) { 158 valueDeleter(value); 159 } 160 return; 161 } 162 if (values->hasDeleter()) { 163 values->adoptElement(fValues, status); 164 } else { 165 values->addElement(fValues, status); 166 } 167 fValues = values.orphan(); 168 fHasValuesVector = true; 169 } 170 // Add the new value. 171 UVector* values = static_cast<UVector*>(fValues); 172 if (values->hasDeleter()) { 173 values->adoptElement(value, status); 174 } else { 175 values->addElement(value, status); 176 } 177 } 178 } 179 180 // --------------------------------------------------- 181 // TextTrieMapSearchResultHandler class implementation 182 // --------------------------------------------------- 183 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){ 184 } 185 186 // --------------------------------------------------- 187 // TextTrieMap class implementation 188 // --------------------------------------------------- 189 TextTrieMap::TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleter) 190 : fIgnoreCase(ignoreCase), fNodes(nullptr), fNodesCapacity(0), fNodesCount(0), 191 fLazyContents(nullptr), fIsEmpty(true), fValueDeleter(valueDeleter) { 192 } 193 194 TextTrieMap::~TextTrieMap() { 195 int32_t index; 196 for (index = 0; index < fNodesCount; ++index) { 197 fNodes[index].deleteValues(fValueDeleter); 198 } 199 uprv_free(fNodes); 200 if (fLazyContents != nullptr) { 201 for (int32_t i=0; i<fLazyContents->size(); i+=2) { 202 if (fValueDeleter) { 203 fValueDeleter(fLazyContents->elementAt(i+1)); 204 } 205 } 206 delete fLazyContents; 207 } 208 } 209 210 int32_t TextTrieMap::isEmpty() const { 211 // Use a separate field for fIsEmpty because it will remain unchanged once the 212 // Trie is built, while fNodes and fLazyContents change with the lazy init 213 // of the nodes structure. Trying to test the changing fields has 214 // thread safety complications. 215 return fIsEmpty; 216 } 217 218 219 // We defer actually building the TextTrieMap node structure until the first time a 220 // search is performed. put() simply saves the parameters in case we do 221 // eventually need to build it. 222 // 223 void 224 TextTrieMap::put(const UnicodeString &key, void *value, ZNStringPool &sp, UErrorCode &status) { 225 const char16_t *s = sp.get(key, status); 226 put(s, value, status); 227 } 228 229 // This method is designed for a persistent key, such as string key stored in 230 // resource bundle. 231 void 232 TextTrieMap::put(const char16_t *key, void *value, UErrorCode &status) { 233 fIsEmpty = false; 234 if (fLazyContents == nullptr) { 235 LocalPointer<UVector> lpLazyContents(new UVector(status), status); 236 fLazyContents = lpLazyContents.orphan(); 237 } 238 if (U_SUCCESS(status)) { 239 U_ASSERT(fLazyContents != nullptr); 240 char16_t *s = const_cast<char16_t *>(key); 241 fLazyContents->addElement(s, status); 242 if (U_SUCCESS(status)) { 243 fLazyContents->addElement(value, status); 244 return; 245 } 246 } 247 if (fValueDeleter) { 248 fValueDeleter(value); 249 } 250 } 251 252 void 253 TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) { 254 if (fNodes == nullptr) { 255 fNodesCapacity = 512; 256 fNodes = static_cast<CharacterNode*>(uprv_malloc(fNodesCapacity * sizeof(CharacterNode))); 257 if (fNodes == nullptr) { 258 status = U_MEMORY_ALLOCATION_ERROR; 259 return; 260 } 261 fNodes[0].clear(); // Init root node. 262 fNodesCount = 1; 263 } 264 265 UnicodeString foldedKey; 266 const char16_t *keyBuffer; 267 int32_t keyLength; 268 if (fIgnoreCase) { 269 // Ok to use fastCopyFrom() because we discard the copy when we return. 270 foldedKey.fastCopyFrom(key).foldCase(); 271 keyBuffer = foldedKey.getBuffer(); 272 keyLength = foldedKey.length(); 273 } else { 274 keyBuffer = key.getBuffer(); 275 keyLength = key.length(); 276 } 277 278 CharacterNode *node = fNodes; 279 int32_t index; 280 for (index = 0; index < keyLength; ++index) { 281 node = addChildNode(node, keyBuffer[index], status); 282 } 283 node->addValue(value, fValueDeleter, status); 284 } 285 286 UBool 287 TextTrieMap::growNodes() { 288 if (fNodesCapacity == 0xffff) { 289 return false; // We use 16-bit node indexes. 290 } 291 int32_t newCapacity = fNodesCapacity + 1000; 292 if (newCapacity > 0xffff) { 293 newCapacity = 0xffff; 294 } 295 CharacterNode* newNodes = static_cast<CharacterNode*>(uprv_malloc(newCapacity * sizeof(CharacterNode))); 296 if (newNodes == nullptr) { 297 return false; 298 } 299 uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode)); 300 uprv_free(fNodes); 301 fNodes = newNodes; 302 fNodesCapacity = newCapacity; 303 return true; 304 } 305 306 CharacterNode* 307 TextTrieMap::addChildNode(CharacterNode *parent, char16_t c, UErrorCode &status) { 308 if (U_FAILURE(status)) { 309 return nullptr; 310 } 311 // Linear search of the sorted list of children. 312 uint16_t prevIndex = 0; 313 uint16_t nodeIndex = parent->fFirstChild; 314 while (nodeIndex > 0) { 315 CharacterNode *current = fNodes + nodeIndex; 316 char16_t childCharacter = current->fCharacter; 317 if (childCharacter == c) { 318 return current; 319 } else if (childCharacter > c) { 320 break; 321 } 322 prevIndex = nodeIndex; 323 nodeIndex = current->fNextSibling; 324 } 325 326 // Ensure capacity. Grow fNodes[] if needed. 327 if (fNodesCount == fNodesCapacity) { 328 int32_t parentIndex = static_cast<int32_t>(parent - fNodes); 329 if (!growNodes()) { 330 status = U_MEMORY_ALLOCATION_ERROR; 331 return nullptr; 332 } 333 parent = fNodes + parentIndex; 334 } 335 336 // Insert a new child node with c in sorted order. 337 CharacterNode *node = fNodes + fNodesCount; 338 node->clear(); 339 node->fCharacter = c; 340 node->fNextSibling = nodeIndex; 341 if (prevIndex == 0) { 342 parent->fFirstChild = static_cast<uint16_t>(fNodesCount); 343 } else { 344 fNodes[prevIndex].fNextSibling = static_cast<uint16_t>(fNodesCount); 345 } 346 ++fNodesCount; 347 return node; 348 } 349 350 CharacterNode* 351 TextTrieMap::getChildNode(CharacterNode *parent, char16_t c) const { 352 // Linear search of the sorted list of children. 353 uint16_t nodeIndex = parent->fFirstChild; 354 while (nodeIndex > 0) { 355 CharacterNode *current = fNodes + nodeIndex; 356 char16_t childCharacter = current->fCharacter; 357 if (childCharacter == c) { 358 return current; 359 } else if (childCharacter > c) { 360 break; 361 } 362 nodeIndex = current->fNextSibling; 363 } 364 return nullptr; 365 } 366 367 368 // buildTrie() - The Trie node structure is needed. Create it from the data that was 369 // saved at the time the ZoneStringFormatter was created. The Trie is only 370 // needed for parsing operations, which are less common than formatting, 371 // and the Trie is big, which is why its creation is deferred until first use. 372 void TextTrieMap::buildTrie(UErrorCode &status) { 373 if (fLazyContents != nullptr) { 374 for (int32_t i=0; i<fLazyContents->size(); i+=2) { 375 const char16_t* key = static_cast<char16_t*>(fLazyContents->elementAt(i)); 376 void *val = fLazyContents->elementAt(i+1); 377 UnicodeString keyString(true, key, -1); // Aliasing UnicodeString constructor. 378 putImpl(keyString, val, status); 379 } 380 delete fLazyContents; 381 fLazyContents = nullptr; 382 } 383 } 384 385 void 386 TextTrieMap::search(const UnicodeString &text, int32_t start, 387 TextTrieMapSearchResultHandler *handler, UErrorCode &status) const { 388 { 389 // TODO: if locking the mutex for each check proves to be a performance problem, 390 // add a flag of type atomic_int32_t to class TextTrieMap, and use only 391 // the ICU atomic safe functions for assigning and testing. 392 // Don't test the pointer fLazyContents. 393 // Don't do unless it's really required. 394 395 // Mutex for protecting the lazy creation of the Trie node structure on the first call to search(). 396 static UMutex TextTrieMutex; 397 398 Mutex lock(&TextTrieMutex); 399 if (fLazyContents != nullptr) { 400 TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this); 401 nonConstThis->buildTrie(status); 402 } 403 } 404 if (fNodes == nullptr) { 405 return; 406 } 407 search(fNodes, text, start, start, handler, status); 408 } 409 410 void 411 TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start, 412 int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const { 413 if (U_FAILURE(status)) { 414 return; 415 } 416 if (node->hasValues()) { 417 if (!handler->handleMatch(index - start, node, status)) { 418 return; 419 } 420 if (U_FAILURE(status)) { 421 return; 422 } 423 } 424 if (fIgnoreCase) { 425 // for folding we need to get a complete code point. 426 // size of character may grow after fold operation; 427 // then we need to get result as UTF16 code units. 428 UChar32 c32 = text.char32At(index); 429 index += U16_LENGTH(c32); 430 UnicodeString tmp(c32); 431 tmp.foldCase(); 432 int32_t tmpidx = 0; 433 while (tmpidx < tmp.length()) { 434 char16_t c = tmp.charAt(tmpidx++); 435 node = getChildNode(node, c); 436 if (node == nullptr) { 437 break; 438 } 439 } 440 } else { 441 // here we just get the next UTF16 code unit 442 char16_t c = text.charAt(index++); 443 node = getChildNode(node, c); 444 } 445 if (node != nullptr) { 446 search(node, text, start, index, handler, status); 447 } 448 } 449 450 // --------------------------------------------------- 451 // ZNStringPool class implementation 452 // --------------------------------------------------- 453 static const int32_t POOL_CHUNK_SIZE = 2000; 454 struct ZNStringPoolChunk: public UMemory { 455 ZNStringPoolChunk *fNext; // Ptr to next pool chunk 456 int32_t fLimit; // Index to start of unused area at end of fStrings 457 char16_t fStrings[POOL_CHUNK_SIZE]; // Strings array 458 ZNStringPoolChunk(); 459 }; 460 461 ZNStringPoolChunk::ZNStringPoolChunk() { 462 fNext = nullptr; 463 fLimit = 0; 464 } 465 466 ZNStringPool::ZNStringPool(UErrorCode &status) { 467 fChunks = nullptr; 468 fHash = nullptr; 469 if (U_FAILURE(status)) { 470 return; 471 } 472 fChunks = new ZNStringPoolChunk; 473 if (fChunks == nullptr) { 474 status = U_MEMORY_ALLOCATION_ERROR; 475 return; 476 } 477 478 fHash = uhash_open(uhash_hashUChars /* keyHash */, 479 uhash_compareUChars /* keyComp */, 480 uhash_compareUChars /* valueComp */, 481 &status); 482 if (U_FAILURE(status)) { 483 return; 484 } 485 } 486 487 ZNStringPool::~ZNStringPool() { 488 if (fHash != nullptr) { 489 uhash_close(fHash); 490 fHash = nullptr; 491 } 492 493 while (fChunks != nullptr) { 494 ZNStringPoolChunk *nextChunk = fChunks->fNext; 495 delete fChunks; 496 fChunks = nextChunk; 497 } 498 } 499 500 static const char16_t EmptyString = 0; 501 502 const char16_t *ZNStringPool::get(const char16_t *s, UErrorCode &status) { 503 const char16_t *pooledString; 504 if (U_FAILURE(status)) { 505 return &EmptyString; 506 } 507 508 pooledString = static_cast<char16_t *>(uhash_get(fHash, s)); 509 if (pooledString != nullptr) { 510 return pooledString; 511 } 512 513 int32_t length = u_strlen(s); 514 int32_t remainingLength = POOL_CHUNK_SIZE - fChunks->fLimit; 515 if (remainingLength <= length) { 516 U_ASSERT(length < POOL_CHUNK_SIZE); 517 if (length >= POOL_CHUNK_SIZE) { 518 status = U_INTERNAL_PROGRAM_ERROR; 519 return &EmptyString; 520 } 521 ZNStringPoolChunk *oldChunk = fChunks; 522 fChunks = new ZNStringPoolChunk; 523 if (fChunks == nullptr) { 524 status = U_MEMORY_ALLOCATION_ERROR; 525 return &EmptyString; 526 } 527 fChunks->fNext = oldChunk; 528 } 529 530 char16_t *destString = &fChunks->fStrings[fChunks->fLimit]; 531 u_strcpy(destString, s); 532 fChunks->fLimit += (length + 1); 533 uhash_put(fHash, destString, destString, &status); 534 return destString; 535 } 536 537 538 // 539 // ZNStringPool::adopt() Put a string into the hash, but do not copy the string data 540 // into the pool's storage. Used for strings from resource bundles, 541 // which will persist for the life of the zone string formatter, and 542 // therefore can be used directly without copying. 543 const char16_t *ZNStringPool::adopt(const char16_t * s, UErrorCode &status) { 544 const char16_t *pooledString; 545 if (U_FAILURE(status)) { 546 return &EmptyString; 547 } 548 if (s != nullptr) { 549 pooledString = static_cast<char16_t *>(uhash_get(fHash, s)); 550 if (pooledString == nullptr) { 551 char16_t *ncs = const_cast<char16_t *>(s); 552 uhash_put(fHash, ncs, ncs, &status); 553 } 554 } 555 return s; 556 } 557 558 559 const char16_t *ZNStringPool::get(const UnicodeString &s, UErrorCode &status) { 560 UnicodeString &nonConstStr = const_cast<UnicodeString &>(s); 561 return this->get(nonConstStr.getTerminatedBuffer(), status); 562 } 563 564 /* 565 * freeze(). Close the hash table that maps to the pooled strings. 566 * After freezing, the pool can not be searched or added to, 567 * but all existing references to pooled strings remain valid. 568 * 569 * The main purpose is to recover the storage used for the hash. 570 */ 571 void ZNStringPool::freeze() { 572 uhash_close(fHash); 573 fHash = nullptr; 574 } 575 576 577 /** 578 * This class stores name data for a meta zone or time zone. 579 */ 580 class ZNames : public UMemory { 581 private: 582 friend class TimeZoneNamesImpl; 583 584 static UTimeZoneNameTypeIndex getTZNameTypeIndex(UTimeZoneNameType type) { 585 switch(type) { 586 case UTZNM_EXEMPLAR_LOCATION: return UTZNM_INDEX_EXEMPLAR_LOCATION; 587 case UTZNM_LONG_GENERIC: return UTZNM_INDEX_LONG_GENERIC; 588 case UTZNM_LONG_STANDARD: return UTZNM_INDEX_LONG_STANDARD; 589 case UTZNM_LONG_DAYLIGHT: return UTZNM_INDEX_LONG_DAYLIGHT; 590 case UTZNM_SHORT_GENERIC: return UTZNM_INDEX_SHORT_GENERIC; 591 case UTZNM_SHORT_STANDARD: return UTZNM_INDEX_SHORT_STANDARD; 592 case UTZNM_SHORT_DAYLIGHT: return UTZNM_INDEX_SHORT_DAYLIGHT; 593 default: return UTZNM_INDEX_UNKNOWN; 594 } 595 } 596 static UTimeZoneNameType getTZNameType(UTimeZoneNameTypeIndex index) { 597 switch(index) { 598 case UTZNM_INDEX_EXEMPLAR_LOCATION: return UTZNM_EXEMPLAR_LOCATION; 599 case UTZNM_INDEX_LONG_GENERIC: return UTZNM_LONG_GENERIC; 600 case UTZNM_INDEX_LONG_STANDARD: return UTZNM_LONG_STANDARD; 601 case UTZNM_INDEX_LONG_DAYLIGHT: return UTZNM_LONG_DAYLIGHT; 602 case UTZNM_INDEX_SHORT_GENERIC: return UTZNM_SHORT_GENERIC; 603 case UTZNM_INDEX_SHORT_STANDARD: return UTZNM_SHORT_STANDARD; 604 case UTZNM_INDEX_SHORT_DAYLIGHT: return UTZNM_SHORT_DAYLIGHT; 605 default: return UTZNM_UNKNOWN; 606 } 607 } 608 609 const char16_t* fNames[UTZNM_INDEX_COUNT]; 610 UBool fDidAddIntoTrie; 611 612 // Whether we own the location string, if computed rather than loaded from a bundle. 613 // A meta zone names instance never has an exemplar location string. 614 UBool fOwnsLocationName; 615 616 ZNames(const char16_t* names[], const char16_t* locationName) 617 : fDidAddIntoTrie(false) { 618 uprv_memcpy(fNames, names, sizeof(fNames)); 619 if (locationName != nullptr) { 620 fOwnsLocationName = true; 621 fNames[UTZNM_INDEX_EXEMPLAR_LOCATION] = locationName; 622 } else { 623 fOwnsLocationName = false; 624 } 625 } 626 627 public: 628 ~ZNames() { 629 if (fOwnsLocationName) { 630 const char16_t* locationName = fNames[UTZNM_INDEX_EXEMPLAR_LOCATION]; 631 U_ASSERT(locationName != nullptr); 632 uprv_free((void*) locationName); 633 } 634 } 635 636 private: 637 static void* createMetaZoneAndPutInCache(UHashtable* cache, const char16_t* names[], 638 const UnicodeString& mzID, UErrorCode& status) { 639 if (U_FAILURE(status)) { return nullptr; } 640 U_ASSERT(names != nullptr); 641 642 // Use the persistent ID as the resource key, so we can 643 // avoid duplications. 644 // TODO: Is there a more efficient way, like intern() in Java? 645 void* key = (void*) ZoneMeta::findMetaZoneID(mzID); 646 void* value; 647 if (uprv_memcmp(names, EMPTY_NAMES, sizeof(EMPTY_NAMES)) == 0) { 648 value = (void*) EMPTY; 649 } else { 650 value = (void*) (new ZNames(names, nullptr)); 651 if (value == nullptr) { 652 status = U_MEMORY_ALLOCATION_ERROR; 653 return nullptr; 654 } 655 } 656 uhash_put(cache, key, value, &status); 657 return value; 658 } 659 660 static void* createTimeZoneAndPutInCache(UHashtable* cache, const char16_t* names[], 661 const UnicodeString& tzID, UErrorCode& status) { 662 if (U_FAILURE(status)) { return nullptr; } 663 U_ASSERT(names != nullptr); 664 665 // If necessary, compute the location name from the time zone name. 666 char16_t* locationName = nullptr; 667 if (names[UTZNM_INDEX_EXEMPLAR_LOCATION] == nullptr) { 668 UnicodeString locationNameUniStr; 669 TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, locationNameUniStr); 670 671 // Copy the computed location name to the heap 672 if (locationNameUniStr.length() > 0) { 673 const char16_t* buff = locationNameUniStr.getTerminatedBuffer(); 674 int32_t len = sizeof(char16_t) * (locationNameUniStr.length() + 1); 675 locationName = static_cast<char16_t*>(uprv_malloc(len)); 676 if (locationName == nullptr) { 677 status = U_MEMORY_ALLOCATION_ERROR; 678 return nullptr; 679 } 680 uprv_memcpy(locationName, buff, len); 681 } 682 } 683 684 // Use the persistent ID as the resource key, so we can 685 // avoid duplications. 686 // TODO: Is there a more efficient way, like intern() in Java? 687 void* key = (void*) ZoneMeta::findTimeZoneID(tzID); 688 void* value = (void*) (new ZNames(names, locationName)); 689 if (value == nullptr) { 690 status = U_MEMORY_ALLOCATION_ERROR; 691 return nullptr; 692 } 693 uhash_put(cache, key, value, &status); 694 return value; 695 } 696 697 const char16_t* getName(UTimeZoneNameType type) const { 698 UTimeZoneNameTypeIndex index = getTZNameTypeIndex(type); 699 return index >= 0 ? fNames[index] : nullptr; 700 } 701 702 void addAsMetaZoneIntoTrie(const char16_t* mzID, TextTrieMap& trie, UErrorCode& status) { 703 addNamesIntoTrie(mzID, nullptr, trie, status); 704 } 705 void addAsTimeZoneIntoTrie(const char16_t* tzID, TextTrieMap& trie, UErrorCode& status) { 706 addNamesIntoTrie(nullptr, tzID, trie, status); 707 } 708 709 void addNamesIntoTrie(const char16_t* mzID, const char16_t* tzID, TextTrieMap& trie, 710 UErrorCode& status) { 711 if (U_FAILURE(status)) { return; } 712 if (fDidAddIntoTrie) { return; } 713 fDidAddIntoTrie = true; 714 715 for (int32_t i = 0; i < UTZNM_INDEX_COUNT; i++) { 716 const char16_t* name = fNames[i]; 717 if (name != nullptr) { 718 LocalMemory<ZNameInfo> nameinfo(static_cast<ZNameInfo*>(uprv_malloc(sizeof(ZNameInfo)))); 719 if (nameinfo.isNull()) { 720 status = U_MEMORY_ALLOCATION_ERROR; 721 return; 722 } 723 nameinfo->mzID = mzID; 724 nameinfo->tzID = tzID; 725 nameinfo->type = getTZNameType(static_cast<UTimeZoneNameTypeIndex>(i)); 726 trie.put(name, nameinfo.orphan(), status); // trie.put() takes ownership of the key 727 if (U_FAILURE(status)) { 728 return; 729 } 730 } 731 } 732 } 733 734 public: 735 struct ZNamesLoader; 736 }; 737 738 struct ZNames::ZNamesLoader : public ResourceSink { 739 const char16_t *names[UTZNM_INDEX_COUNT]; 740 741 ZNamesLoader() { 742 clear(); 743 } 744 virtual ~ZNamesLoader(); 745 746 /** Reset for loading another set of names. */ 747 void clear() { 748 uprv_memcpy(names, EMPTY_NAMES, sizeof(names)); 749 } 750 751 void loadMetaZone(const UResourceBundle* zoneStrings, const UnicodeString& mzID, UErrorCode& errorCode) { 752 if (U_FAILURE(errorCode)) { return; } 753 754 char key[ZID_KEY_MAX + 1]; 755 mergeTimeZoneKey(mzID, key, sizeof(key), errorCode); 756 757 loadNames(zoneStrings, key, errorCode); 758 } 759 760 void loadTimeZone(const UResourceBundle* zoneStrings, const UnicodeString& tzID, UErrorCode& errorCode) { 761 // Replace "/" with ":". 762 UnicodeString uKey(tzID); 763 for (int32_t i = 0; i < uKey.length(); i++) { 764 if (uKey.charAt(i) == static_cast<char16_t>(0x2F)) { 765 uKey.setCharAt(i, static_cast<char16_t>(0x3A)); 766 } 767 } 768 769 char key[ZID_KEY_MAX + 1]; 770 if (uKey.length() > ZID_KEY_MAX) { 771 errorCode = U_INTERNAL_PROGRAM_ERROR; 772 return; 773 } 774 uKey.extract(0, uKey.length(), key, sizeof(key), US_INV); 775 776 loadNames(zoneStrings, key, errorCode); 777 } 778 779 void loadNames(const UResourceBundle* zoneStrings, const char* key, UErrorCode& errorCode) { 780 U_ASSERT(zoneStrings != nullptr); 781 U_ASSERT(key != nullptr); 782 U_ASSERT(key[0] != '\0'); 783 784 UErrorCode localStatus = U_ZERO_ERROR; 785 clear(); 786 ures_getAllItemsWithFallback(zoneStrings, key, *this, localStatus); 787 788 // Ignore errors, but propagate possible warnings. 789 if (U_SUCCESS(localStatus)) { 790 errorCode = localStatus; 791 } 792 } 793 794 void setNameIfEmpty(const char* key, const ResourceValue* value, UErrorCode& errorCode) { 795 UTimeZoneNameTypeIndex type = nameTypeFromKey(key); 796 if (type == UTZNM_INDEX_UNKNOWN) { return; } 797 if (names[type] == nullptr) { 798 int32_t length; 799 // 'NO_NAME' indicates internally that this field should remain empty. It will be 800 // replaced by 'nullptr' in getNames() 801 names[type] = (value == nullptr) ? NO_NAME : value->getString(length, errorCode); 802 } 803 } 804 805 virtual void put(const char* key, ResourceValue& value, UBool /*noFallback*/, 806 UErrorCode &errorCode) override { 807 ResourceTable namesTable = value.getTable(errorCode); 808 if (U_FAILURE(errorCode)) { return; } 809 for (int32_t i = 0; namesTable.getKeyAndValue(i, key, value); ++i) { 810 if (value.isNoInheritanceMarker()) { 811 setNameIfEmpty(key, nullptr, errorCode); 812 } else { 813 setNameIfEmpty(key, &value, errorCode); 814 } 815 } 816 } 817 818 static UTimeZoneNameTypeIndex nameTypeFromKey(const char *key) { 819 char c0, c1; 820 if ((c0 = key[0]) == 0 || (c1 = key[1]) == 0 || key[2] != 0) { 821 return UTZNM_INDEX_UNKNOWN; 822 } 823 if (c0 == 'l') { 824 return c1 == 'g' ? UTZNM_INDEX_LONG_GENERIC : 825 c1 == 's' ? UTZNM_INDEX_LONG_STANDARD : 826 c1 == 'd' ? UTZNM_INDEX_LONG_DAYLIGHT : UTZNM_INDEX_UNKNOWN; 827 } else if (c0 == 's') { 828 return c1 == 'g' ? UTZNM_INDEX_SHORT_GENERIC : 829 c1 == 's' ? UTZNM_INDEX_SHORT_STANDARD : 830 c1 == 'd' ? UTZNM_INDEX_SHORT_DAYLIGHT : UTZNM_INDEX_UNKNOWN; 831 } else if (c0 == 'e' && c1 == 'c') { 832 return UTZNM_INDEX_EXEMPLAR_LOCATION; 833 } 834 return UTZNM_INDEX_UNKNOWN; 835 } 836 837 /** 838 * Returns an array of names. It is the caller's responsibility to copy the data into a 839 * permanent location, as the returned array is owned by the loader instance and may be 840 * cleared or leave scope. 841 * 842 * This is different than Java, where the array will no longer be modified and null 843 * may be returned. 844 */ 845 const char16_t** getNames() { 846 // Remove 'NO_NAME' references in the array and replace with 'nullptr' 847 for (int32_t i = 0; i < UTZNM_INDEX_COUNT; ++i) { 848 if (names[i] == NO_NAME) { 849 names[i] = nullptr; 850 } 851 } 852 return names; 853 } 854 }; 855 856 ZNames::ZNamesLoader::~ZNamesLoader() {} 857 858 859 // --------------------------------------------------- 860 // The meta zone ID enumeration class 861 // --------------------------------------------------- 862 class MetaZoneIDsEnumeration : public StringEnumeration { 863 public: 864 MetaZoneIDsEnumeration(); 865 MetaZoneIDsEnumeration(const UVector& mzIDs); 866 MetaZoneIDsEnumeration(LocalPointer<UVector> mzIDs); 867 virtual ~MetaZoneIDsEnumeration(); 868 static UClassID U_EXPORT2 getStaticClassID(); 869 virtual UClassID getDynamicClassID() const override; 870 virtual const UnicodeString* snext(UErrorCode& status) override; 871 virtual void reset(UErrorCode& status) override; 872 virtual int32_t count(UErrorCode& status) const override; 873 private: 874 int32_t fLen; 875 int32_t fPos; 876 const UVector* fMetaZoneIDs; 877 LocalPointer<UVector> fLocalVector; 878 }; 879 880 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration) 881 882 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration() 883 : fLen(0), fPos(0), fMetaZoneIDs(nullptr), fLocalVector(nullptr) { 884 } 885 886 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector& mzIDs) 887 : fPos(0), fMetaZoneIDs(&mzIDs), fLocalVector(nullptr) { 888 fLen = fMetaZoneIDs->size(); 889 } 890 891 MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(LocalPointer<UVector> mzIDs) 892 : fLen(0), fPos(0), fMetaZoneIDs(nullptr), fLocalVector(std::move(mzIDs)) { 893 fMetaZoneIDs = fLocalVector.getAlias(); 894 if (fMetaZoneIDs) { 895 fLen = fMetaZoneIDs->size(); 896 } 897 } 898 899 const UnicodeString* 900 MetaZoneIDsEnumeration::snext(UErrorCode& status) { 901 if (U_SUCCESS(status) && fMetaZoneIDs != nullptr && fPos < fLen) { 902 unistr.setTo(static_cast<const char16_t*>(fMetaZoneIDs->elementAt(fPos++)), -1); 903 return &unistr; 904 } 905 return nullptr; 906 } 907 908 void 909 MetaZoneIDsEnumeration::reset(UErrorCode& /*status*/) { 910 fPos = 0; 911 } 912 913 int32_t 914 MetaZoneIDsEnumeration::count(UErrorCode& /*status*/) const { 915 return fLen; 916 } 917 918 MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() { 919 } 920 921 922 // --------------------------------------------------- 923 // ZNameSearchHandler 924 // --------------------------------------------------- 925 class ZNameSearchHandler : public TextTrieMapSearchResultHandler { 926 public: 927 ZNameSearchHandler(uint32_t types); 928 virtual ~ZNameSearchHandler(); 929 930 UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) override; 931 TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen); 932 933 private: 934 uint32_t fTypes; 935 int32_t fMaxMatchLen; 936 TimeZoneNames::MatchInfoCollection* fResults; 937 }; 938 939 ZNameSearchHandler::ZNameSearchHandler(uint32_t types) 940 : fTypes(types), fMaxMatchLen(0), fResults(nullptr) { 941 } 942 943 ZNameSearchHandler::~ZNameSearchHandler() { 944 delete fResults; 945 } 946 947 UBool 948 ZNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) { 949 if (U_FAILURE(status)) { 950 return false; 951 } 952 if (node->hasValues()) { 953 int32_t valuesCount = node->countValues(); 954 for (int32_t i = 0; i < valuesCount; i++) { 955 ZNameInfo *nameinfo = (ZNameInfo *)node->getValue(i); 956 if (nameinfo == nullptr) { 957 continue; 958 } 959 if ((nameinfo->type & fTypes) != 0) { 960 // matches a requested type 961 if (fResults == nullptr) { 962 fResults = new TimeZoneNames::MatchInfoCollection(); 963 if (fResults == nullptr) { 964 status = U_MEMORY_ALLOCATION_ERROR; 965 } 966 } 967 if (U_SUCCESS(status)) { 968 U_ASSERT(fResults != nullptr); 969 if (nameinfo->tzID) { 970 fResults->addZone(nameinfo->type, matchLength, UnicodeString(nameinfo->tzID, -1), status); 971 } else { 972 U_ASSERT(nameinfo->mzID); 973 fResults->addMetaZone(nameinfo->type, matchLength, UnicodeString(nameinfo->mzID, -1), status); 974 } 975 if (U_SUCCESS(status) && matchLength > fMaxMatchLen) { 976 fMaxMatchLen = matchLength; 977 } 978 } 979 } 980 } 981 } 982 return true; 983 } 984 985 TimeZoneNames::MatchInfoCollection* 986 ZNameSearchHandler::getMatches(int32_t& maxMatchLen) { 987 // give the ownership to the caller 988 TimeZoneNames::MatchInfoCollection* results = fResults; 989 maxMatchLen = fMaxMatchLen; 990 991 // reset 992 fResults = nullptr; 993 fMaxMatchLen = 0; 994 return results; 995 } 996 997 // --------------------------------------------------- 998 // TimeZoneNamesImpl 999 // 1000 // TimeZoneNames implementation class. This is the main 1001 // part of this module. 1002 // --------------------------------------------------- 1003 1004 U_CDECL_BEGIN 1005 /** 1006 * Deleter for ZNames 1007 */ 1008 static void U_CALLCONV 1009 deleteZNames(void *obj) { 1010 if (obj != EMPTY) { 1011 delete (ZNames*) obj; 1012 } 1013 } 1014 1015 /** 1016 * Deleter for ZNameInfo 1017 */ 1018 static void U_CALLCONV 1019 deleteZNameInfo(void *obj) { 1020 uprv_free(obj); 1021 } 1022 1023 U_CDECL_END 1024 1025 TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status) 1026 : fLocale(locale), 1027 fZoneStrings(nullptr), 1028 fTZNamesMap(nullptr), 1029 fMZNamesMap(nullptr), 1030 fNamesTrieFullyLoaded(false), 1031 fNamesFullyLoaded(false), 1032 fNamesTrie(true, deleteZNameInfo) { 1033 initialize(locale, status); 1034 } 1035 1036 void 1037 TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) { 1038 if (U_FAILURE(status)) { 1039 return; 1040 } 1041 1042 // Load zoneStrings bundle 1043 UErrorCode tmpsts = U_ZERO_ERROR; // OK with fallback warning.. 1044 fZoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts); 1045 fZoneStrings = ures_getByKeyWithFallback(fZoneStrings, gZoneStrings, fZoneStrings, &tmpsts); 1046 if (U_FAILURE(tmpsts)) { 1047 status = tmpsts; 1048 cleanup(); 1049 return; 1050 } 1051 1052 // Initialize hashtables holding time zone/meta zone names 1053 fMZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status); 1054 fTZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status); 1055 if (U_FAILURE(status)) { 1056 cleanup(); 1057 return; 1058 } 1059 1060 uhash_setValueDeleter(fMZNamesMap, deleteZNames); 1061 uhash_setValueDeleter(fTZNamesMap, deleteZNames); 1062 // no key deleters for name maps 1063 1064 // preload zone strings for the default zone 1065 TimeZone *tz = TimeZone::createDefault(); 1066 const char16_t *tzID = ZoneMeta::getCanonicalCLDRID(*tz); 1067 if (tzID != nullptr) { 1068 loadStrings(UnicodeString(tzID), status); 1069 } 1070 delete tz; 1071 } 1072 1073 /* 1074 * This method updates the cache and must be called with a lock, 1075 * except initializer. 1076 */ 1077 void 1078 TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID, UErrorCode& status) { 1079 loadTimeZoneNames(tzCanonicalID, status); 1080 LocalPointer<StringEnumeration> mzIDs(getAvailableMetaZoneIDs(tzCanonicalID, status)); 1081 if (U_FAILURE(status)) { return; } 1082 U_ASSERT(!mzIDs.isNull()); 1083 1084 const UnicodeString *mzID; 1085 while (((mzID = mzIDs->snext(status)) != nullptr) && U_SUCCESS(status)) { 1086 loadMetaZoneNames(*mzID, status); 1087 } 1088 } 1089 1090 TimeZoneNamesImpl::~TimeZoneNamesImpl() { 1091 cleanup(); 1092 } 1093 1094 void 1095 TimeZoneNamesImpl::cleanup() { 1096 if (fZoneStrings != nullptr) { 1097 ures_close(fZoneStrings); 1098 fZoneStrings = nullptr; 1099 } 1100 if (fMZNamesMap != nullptr) { 1101 uhash_close(fMZNamesMap); 1102 fMZNamesMap = nullptr; 1103 } 1104 if (fTZNamesMap != nullptr) { 1105 uhash_close(fTZNamesMap); 1106 fTZNamesMap = nullptr; 1107 } 1108 } 1109 1110 bool 1111 TimeZoneNamesImpl::operator==(const TimeZoneNames& other) const { 1112 if (this == &other) { 1113 return true; 1114 } 1115 // No implementation for now 1116 return false; 1117 } 1118 1119 TimeZoneNamesImpl* 1120 TimeZoneNamesImpl::clone() const { 1121 UErrorCode status = U_ZERO_ERROR; 1122 return new TimeZoneNamesImpl(fLocale, status); 1123 } 1124 1125 StringEnumeration* 1126 TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const { 1127 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status); 1128 } 1129 1130 // static implementation of getAvailableMetaZoneIDs(UErrorCode&) 1131 StringEnumeration* 1132 TimeZoneNamesImpl::_getAvailableMetaZoneIDs(UErrorCode& status) { 1133 if (U_FAILURE(status)) { 1134 return nullptr; 1135 } 1136 const UVector* mzIDs = ZoneMeta::getAvailableMetazoneIDs(); 1137 if (mzIDs == nullptr) { 1138 return new MetaZoneIDsEnumeration(); 1139 } 1140 return new MetaZoneIDsEnumeration(*mzIDs); 1141 } 1142 1143 StringEnumeration* 1144 TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const { 1145 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status); 1146 } 1147 1148 // static implementation of getAvailableMetaZoneIDs(const UnicodeString&, UErrorCode&) 1149 StringEnumeration* 1150 TimeZoneNamesImpl::_getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) { 1151 if (U_FAILURE(status)) { 1152 return nullptr; 1153 } 1154 const UVector* mappings = ZoneMeta::getMetazoneMappings(tzID); 1155 if (mappings == nullptr) { 1156 return new MetaZoneIDsEnumeration(); 1157 } 1158 1159 LocalPointer<MetaZoneIDsEnumeration> senum; 1160 LocalPointer<UVector> mzIDs(new UVector(nullptr, uhash_compareUChars, status), status); 1161 if (U_SUCCESS(status)) { 1162 U_ASSERT(mzIDs.isValid()); 1163 for (int32_t i = 0; U_SUCCESS(status) && i < mappings->size(); i++) { 1164 1165 OlsonToMetaMappingEntry* map = static_cast<OlsonToMetaMappingEntry*>(mappings->elementAt(i)); 1166 const char16_t *mzID = map->mzid; 1167 if (!mzIDs->contains((void *)mzID)) { 1168 mzIDs->addElement((void *)mzID, status); 1169 } 1170 } 1171 if (U_SUCCESS(status)) { 1172 senum.adoptInsteadAndCheckErrorCode(new MetaZoneIDsEnumeration(std::move(mzIDs)), status); 1173 } 1174 } 1175 return U_SUCCESS(status) ? senum.orphan() : nullptr; 1176 } 1177 1178 UnicodeString& 1179 TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const { 1180 return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID); 1181 } 1182 1183 // static implementation of getMetaZoneID 1184 UnicodeString& 1185 TimeZoneNamesImpl::_getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) { 1186 ZoneMeta::getMetazoneID(tzID, date, mzID); 1187 return mzID; 1188 } 1189 1190 UnicodeString& 1191 TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const { 1192 return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID); 1193 } 1194 1195 // static implementation of getReferenceZoneID 1196 UnicodeString& 1197 TimeZoneNamesImpl::_getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) { 1198 ZoneMeta::getZoneIdByMetazone(mzID, UnicodeString(region, -1, US_INV), tzID); 1199 return tzID; 1200 } 1201 1202 UnicodeString& 1203 TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID, 1204 UTimeZoneNameType type, 1205 UnicodeString& name) const { 1206 name.setToBogus(); // cleanup result. 1207 if (mzID.isEmpty()) { 1208 return name; 1209 } 1210 1211 ZNames *znames = nullptr; 1212 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this); 1213 1214 { 1215 Mutex lock(&gDataMutex); 1216 UErrorCode status = U_ZERO_ERROR; 1217 znames = nonConstThis->loadMetaZoneNames(mzID, status); 1218 if (U_FAILURE(status)) { return name; } 1219 } 1220 1221 if (znames != nullptr) { 1222 const char16_t* s = znames->getName(type); 1223 if (s != nullptr) { 1224 name.setTo(true, s, -1); 1225 } 1226 } 1227 return name; 1228 } 1229 1230 UnicodeString& 1231 TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const { 1232 name.setToBogus(); // cleanup result. 1233 if (tzID.isEmpty()) { 1234 return name; 1235 } 1236 1237 ZNames *tznames = nullptr; 1238 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this); 1239 1240 { 1241 Mutex lock(&gDataMutex); 1242 UErrorCode status = U_ZERO_ERROR; 1243 tznames = nonConstThis->loadTimeZoneNames(tzID, status); 1244 if (U_FAILURE(status)) { return name; } 1245 } 1246 1247 if (tznames != nullptr) { 1248 const char16_t *s = tznames->getName(type); 1249 if (s != nullptr) { 1250 name.setTo(true, s, -1); 1251 } 1252 } 1253 return name; 1254 } 1255 1256 UnicodeString& 1257 TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const { 1258 name.setToBogus(); // cleanup result. 1259 const char16_t* locName = nullptr; 1260 ZNames *tznames = nullptr; 1261 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this); 1262 1263 { 1264 Mutex lock(&gDataMutex); 1265 UErrorCode status = U_ZERO_ERROR; 1266 tznames = nonConstThis->loadTimeZoneNames(tzID, status); 1267 if (U_FAILURE(status)) { return name; } 1268 } 1269 1270 if (tznames != nullptr) { 1271 locName = tznames->getName(UTZNM_EXEMPLAR_LOCATION); 1272 } 1273 if (locName != nullptr) { 1274 name.setTo(true, locName, -1); 1275 } 1276 1277 return name; 1278 } 1279 1280 1281 // Merge the MZ_PREFIX and mzId 1282 static void mergeTimeZoneKey(const UnicodeString& mzID, char* result, size_t capacity, 1283 UErrorCode& status) { 1284 if (U_FAILURE(status)) { 1285 return; 1286 } 1287 if (mzID.isEmpty()) { 1288 result[0] = '\0'; 1289 return; 1290 } 1291 1292 if (MZ_PREFIX_LEN + 1 > capacity) { 1293 result[0] = '\0'; 1294 status = U_INTERNAL_PROGRAM_ERROR; 1295 return; 1296 } 1297 uprv_memcpy((void *)result, (void *)gMZPrefix, MZ_PREFIX_LEN); 1298 if (static_cast<size_t>(MZ_PREFIX_LEN + mzID.length() + 1) > capacity) { 1299 result[0] = '\0'; 1300 status = U_INTERNAL_PROGRAM_ERROR; 1301 return; 1302 } 1303 int32_t keyLen = mzID.extract(0, mzID.length(), result + MZ_PREFIX_LEN, 1304 static_cast<int32_t>(capacity - MZ_PREFIX_LEN), US_INV); 1305 result[keyLen + MZ_PREFIX_LEN] = '\0'; 1306 } 1307 1308 /* 1309 * This method updates the cache and must be called with a lock 1310 */ 1311 ZNames* 1312 TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) { 1313 if (U_FAILURE(status)) { return nullptr; } 1314 if (mzID.length() > ZID_KEY_MAX - MZ_PREFIX_LEN) { 1315 status = U_INTERNAL_PROGRAM_ERROR; 1316 return nullptr; 1317 } 1318 1319 char16_t mzIDKey[ZID_KEY_MAX + 1]; 1320 mzID.extract(mzIDKey, ZID_KEY_MAX, status); 1321 if (U_FAILURE(status)) { 1322 return nullptr; 1323 } 1324 mzIDKey[mzID.length()] = 0; 1325 1326 void* mznames = uhash_get(fMZNamesMap, mzIDKey); 1327 if (mznames == nullptr) { 1328 ZNames::ZNamesLoader loader; 1329 loader.loadMetaZone(fZoneStrings, mzID, status); 1330 mznames = ZNames::createMetaZoneAndPutInCache(fMZNamesMap, loader.getNames(), mzID, status); 1331 if (U_FAILURE(status)) { return nullptr; } 1332 } 1333 1334 if (mznames != EMPTY) { 1335 return static_cast<ZNames*>(mznames); 1336 } else { 1337 return nullptr; 1338 } 1339 } 1340 1341 /* 1342 * This method updates the cache and must be called with a lock 1343 */ 1344 ZNames* 1345 TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID, UErrorCode& status) { 1346 if (U_FAILURE(status)) { return nullptr; } 1347 if (tzID.length() > ZID_KEY_MAX) { 1348 status = U_INTERNAL_PROGRAM_ERROR; 1349 return nullptr; 1350 } 1351 1352 char16_t tzIDKey[ZID_KEY_MAX + 1]; 1353 int32_t tzIDKeyLen = tzID.extract(tzIDKey, ZID_KEY_MAX, status); 1354 U_ASSERT(U_SUCCESS(status)); // already checked length above 1355 tzIDKey[tzIDKeyLen] = 0; 1356 1357 void *tznames = uhash_get(fTZNamesMap, tzIDKey); 1358 if (tznames == nullptr) { 1359 ZNames::ZNamesLoader loader; 1360 loader.loadTimeZone(fZoneStrings, tzID, status); 1361 tznames = ZNames::createTimeZoneAndPutInCache(fTZNamesMap, loader.getNames(), tzID, status); 1362 if (U_FAILURE(status)) { return nullptr; } 1363 } 1364 1365 // tznames is never EMPTY 1366 return static_cast<ZNames*>(tznames); 1367 } 1368 1369 TimeZoneNames::MatchInfoCollection* 1370 TimeZoneNamesImpl::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { 1371 ZNameSearchHandler handler(types); 1372 TimeZoneNames::MatchInfoCollection* matches; 1373 TimeZoneNamesImpl* nonConstThis = const_cast<TimeZoneNamesImpl*>(this); 1374 1375 // Synchronize so that data is not loaded multiple times. 1376 // TODO: Consider more fine-grained synchronization. 1377 { 1378 Mutex lock(&gDataMutex); 1379 1380 // First try of lookup. 1381 matches = doFind(handler, text, start, status); 1382 if (U_FAILURE(status)) { return nullptr; } 1383 if (matches != nullptr) { 1384 return matches; 1385 } 1386 1387 // All names are not yet loaded into the trie. 1388 // We may have loaded names for formatting several time zones, 1389 // and might be parsing one of those. 1390 // Populate the parsing trie from all of the already-loaded names. 1391 nonConstThis->addAllNamesIntoTrie(status); 1392 1393 // Second try of lookup. 1394 matches = doFind(handler, text, start, status); 1395 if (U_FAILURE(status)) { return nullptr; } 1396 if (matches != nullptr) { 1397 return matches; 1398 } 1399 1400 // There are still some names we haven't loaded into the trie yet. 1401 // Load everything now. 1402 nonConstThis->internalLoadAllDisplayNames(status); 1403 nonConstThis->addAllNamesIntoTrie(status); 1404 nonConstThis->fNamesTrieFullyLoaded = true; 1405 if (U_FAILURE(status)) { return nullptr; } 1406 1407 // Third try: we must return this one. 1408 return doFind(handler, text, start, status); 1409 } 1410 } 1411 1412 TimeZoneNames::MatchInfoCollection* 1413 TimeZoneNamesImpl::doFind(ZNameSearchHandler& handler, 1414 const UnicodeString& text, int32_t start, UErrorCode& status) const { 1415 1416 fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); 1417 if (U_FAILURE(status)) { return nullptr; } 1418 1419 int32_t maxLen = 0; 1420 TimeZoneNames::MatchInfoCollection* matches = handler.getMatches(maxLen); 1421 if (matches != nullptr && ((maxLen == (text.length() - start)) || fNamesTrieFullyLoaded)) { 1422 // perfect match, or no more names available 1423 return matches; 1424 } 1425 delete matches; 1426 return nullptr; 1427 } 1428 1429 // Caller must synchronize. 1430 void TimeZoneNamesImpl::addAllNamesIntoTrie(UErrorCode& status) { 1431 if (U_FAILURE(status)) return; 1432 int32_t pos; 1433 const UHashElement* element; 1434 1435 pos = UHASH_FIRST; 1436 while ((element = uhash_nextElement(fMZNamesMap, &pos)) != nullptr) { 1437 if (element->value.pointer == EMPTY) { continue; } 1438 char16_t* mzID = static_cast<char16_t*>(element->key.pointer); 1439 ZNames* znames = static_cast<ZNames*>(element->value.pointer); 1440 znames->addAsMetaZoneIntoTrie(mzID, fNamesTrie, status); 1441 if (U_FAILURE(status)) { return; } 1442 } 1443 1444 pos = UHASH_FIRST; 1445 while ((element = uhash_nextElement(fTZNamesMap, &pos)) != nullptr) { 1446 if (element->value.pointer == EMPTY) { continue; } 1447 char16_t* tzID = static_cast<char16_t*>(element->key.pointer); 1448 ZNames* znames = static_cast<ZNames*>(element->value.pointer); 1449 znames->addAsTimeZoneIntoTrie(tzID, fNamesTrie, status); 1450 if (U_FAILURE(status)) { return; } 1451 } 1452 } 1453 1454 U_CDECL_BEGIN 1455 static void U_CALLCONV 1456 deleteZNamesLoader(void* obj) { 1457 if (obj == DUMMY_LOADER) { return; } 1458 const ZNames::ZNamesLoader* loader = (const ZNames::ZNamesLoader*) obj; 1459 delete loader; 1460 } 1461 U_CDECL_END 1462 1463 struct TimeZoneNamesImpl::ZoneStringsLoader : public ResourceSink { 1464 TimeZoneNamesImpl& tzn; 1465 UHashtable* keyToLoader; 1466 1467 ZoneStringsLoader(TimeZoneNamesImpl& _tzn, UErrorCode& status) 1468 : tzn(_tzn) { 1469 keyToLoader = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status); 1470 if (U_FAILURE(status)) { return; } 1471 uhash_setKeyDeleter(keyToLoader, uprv_free); 1472 uhash_setValueDeleter(keyToLoader, deleteZNamesLoader); 1473 } 1474 virtual ~ZoneStringsLoader(); 1475 1476 void* createKey(const char* key, UErrorCode& status) { 1477 int32_t len = sizeof(char) * (static_cast<int32_t>(uprv_strlen(key)) + 1); 1478 char* newKey = static_cast<char*>(uprv_malloc(len)); 1479 if (newKey == nullptr) { 1480 status = U_MEMORY_ALLOCATION_ERROR; 1481 return nullptr; 1482 } 1483 uprv_memcpy(newKey, key, len); 1484 newKey[len-1] = '\0'; 1485 return (void*) newKey; 1486 } 1487 1488 UBool isMetaZone(const char* key) { 1489 return (uprv_strlen(key) >= MZ_PREFIX_LEN && uprv_memcmp(key, gMZPrefix, MZ_PREFIX_LEN) == 0); 1490 } 1491 1492 UnicodeString mzIDFromKey(const char* key) { 1493 return UnicodeString(key + MZ_PREFIX_LEN, static_cast<int32_t>(uprv_strlen(key)) - MZ_PREFIX_LEN, US_INV); 1494 } 1495 1496 UnicodeString tzIDFromKey(const char* key) { 1497 UnicodeString tzID(key, -1, US_INV); 1498 // Replace all colons ':' with slashes '/' 1499 for (int i=0; i<tzID.length(); i++) { 1500 if (tzID.charAt(i) == 0x003A) { 1501 tzID.setCharAt(i, 0x002F); 1502 } 1503 } 1504 return tzID; 1505 } 1506 1507 void load(UErrorCode& status) { 1508 ures_getAllItemsWithFallback(tzn.fZoneStrings, "", *this, status); 1509 if (U_FAILURE(status)) { return; } 1510 1511 int32_t pos = UHASH_FIRST; 1512 const UHashElement* element; 1513 while ((element = uhash_nextElement(keyToLoader, &pos)) != nullptr) { 1514 if (element->value.pointer == DUMMY_LOADER) { continue; } 1515 ZNames::ZNamesLoader* loader = static_cast<ZNames::ZNamesLoader*>(element->value.pointer); 1516 char* key = static_cast<char*>(element->key.pointer); 1517 1518 if (isMetaZone(key)) { 1519 UnicodeString mzID = mzIDFromKey(key); 1520 ZNames::createMetaZoneAndPutInCache(tzn.fMZNamesMap, loader->getNames(), mzID, status); 1521 } else { 1522 UnicodeString tzID = tzIDFromKey(key); 1523 ZNames::createTimeZoneAndPutInCache(tzn.fTZNamesMap, loader->getNames(), tzID, status); 1524 } 1525 if (U_FAILURE(status)) { return; } 1526 } 1527 } 1528 1529 void consumeNamesTable(const char *key, ResourceValue &value, UBool noFallback, 1530 UErrorCode &status) { 1531 if (U_FAILURE(status)) { return; } 1532 1533 void* loader = uhash_get(keyToLoader, key); 1534 if (loader == nullptr) { 1535 if (isMetaZone(key)) { 1536 UnicodeString mzID = mzIDFromKey(key); 1537 void* cacheVal = uhash_get(tzn.fMZNamesMap, mzID.getTerminatedBuffer()); 1538 if (cacheVal != nullptr) { 1539 // We have already loaded the names for this meta zone. 1540 loader = (void*) DUMMY_LOADER; 1541 } else { 1542 loader = (void*) new ZNames::ZNamesLoader(); 1543 if (loader == nullptr) { 1544 status = U_MEMORY_ALLOCATION_ERROR; 1545 return; 1546 } 1547 } 1548 } else { 1549 UnicodeString tzID = tzIDFromKey(key); 1550 void* cacheVal = uhash_get(tzn.fTZNamesMap, tzID.getTerminatedBuffer()); 1551 if (cacheVal != nullptr) { 1552 // We have already loaded the names for this time zone. 1553 loader = (void*) DUMMY_LOADER; 1554 } else { 1555 loader = (void*) new ZNames::ZNamesLoader(); 1556 if (loader == nullptr) { 1557 status = U_MEMORY_ALLOCATION_ERROR; 1558 return; 1559 } 1560 } 1561 } 1562 1563 void* newKey = createKey(key, status); 1564 if (U_FAILURE(status)) { 1565 deleteZNamesLoader(loader); 1566 return; 1567 } 1568 1569 uhash_put(keyToLoader, newKey, loader, &status); 1570 if (U_FAILURE(status)) { return; } 1571 } 1572 1573 if (loader != DUMMY_LOADER) { 1574 // Let the ZNamesLoader consume the names table. 1575 static_cast<ZNames::ZNamesLoader*>(loader)->put(key, value, noFallback, status); 1576 } 1577 } 1578 1579 virtual void put(const char *key, ResourceValue &value, UBool noFallback, 1580 UErrorCode &status) override { 1581 ResourceTable timeZonesTable = value.getTable(status); 1582 if (U_FAILURE(status)) { return; } 1583 for (int32_t i = 0; timeZonesTable.getKeyAndValue(i, key, value); ++i) { 1584 U_ASSERT(!value.isNoInheritanceMarker()); 1585 if (value.getType() == URES_TABLE) { 1586 consumeNamesTable(key, value, noFallback, status); 1587 } else { 1588 // Ignore fields that aren't tables (e.g., fallbackFormat and regionFormatStandard). 1589 // All time zone fields are tables. 1590 } 1591 if (U_FAILURE(status)) { return; } 1592 } 1593 } 1594 }; 1595 1596 // Virtual destructors must be defined out of line. 1597 TimeZoneNamesImpl::ZoneStringsLoader::~ZoneStringsLoader() { 1598 uhash_close(keyToLoader); 1599 } 1600 1601 void TimeZoneNamesImpl::loadAllDisplayNames(UErrorCode& status) { 1602 if (U_FAILURE(status)) return; 1603 1604 { 1605 Mutex lock(&gDataMutex); 1606 internalLoadAllDisplayNames(status); 1607 } 1608 } 1609 1610 void TimeZoneNamesImpl::getDisplayNames(const UnicodeString& tzID, 1611 const UTimeZoneNameType types[], int32_t numTypes, 1612 UDate date, UnicodeString dest[], UErrorCode& status) const { 1613 if (U_FAILURE(status)) return; 1614 1615 if (tzID.isEmpty()) { return; } 1616 void* tznames = nullptr; 1617 void* mznames = nullptr; 1618 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl*>(this); 1619 1620 // Load the time zone strings 1621 { 1622 Mutex lock(&gDataMutex); 1623 tznames = (void*) nonConstThis->loadTimeZoneNames(tzID, status); 1624 if (U_FAILURE(status)) { return; } 1625 } 1626 U_ASSERT(tznames != nullptr); 1627 1628 // Load the values into the dest array 1629 for (int i = 0; i < numTypes; i++) { 1630 UTimeZoneNameType type = types[i]; 1631 const char16_t* name = static_cast<ZNames*>(tznames)->getName(type); 1632 if (name == nullptr) { 1633 if (mznames == nullptr) { 1634 // Load the meta zone name 1635 UnicodeString mzID; 1636 getMetaZoneID(tzID, date, mzID); 1637 if (mzID.isEmpty()) { 1638 mznames = (void*) EMPTY; 1639 } else { 1640 // Load the meta zone strings 1641 // Mutex is scoped to the "else" statement 1642 Mutex lock(&gDataMutex); 1643 mznames = (void*) nonConstThis->loadMetaZoneNames(mzID, status); 1644 if (U_FAILURE(status)) { return; } 1645 // Note: when the metazone doesn't exist, in Java, loadMetaZoneNames returns 1646 // a dummy object instead of nullptr. 1647 if (mznames == nullptr) { 1648 mznames = (void*) EMPTY; 1649 } 1650 } 1651 } 1652 U_ASSERT(mznames != nullptr); 1653 if (mznames != EMPTY) { 1654 name = static_cast<ZNames*>(mznames)->getName(type); 1655 } 1656 } 1657 if (name != nullptr) { 1658 dest[i].setTo(true, name, -1); 1659 } else { 1660 dest[i].setToBogus(); 1661 } 1662 } 1663 } 1664 1665 // Caller must synchronize. 1666 void TimeZoneNamesImpl::internalLoadAllDisplayNames(UErrorCode& status) { 1667 if (!fNamesFullyLoaded) { 1668 fNamesFullyLoaded = true; 1669 1670 ZoneStringsLoader loader(*this, status); 1671 loader.load(status); 1672 if (U_FAILURE(status)) { return; } 1673 1674 const UnicodeString *id; 1675 1676 // load strings for all zones 1677 StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration( 1678 UCAL_ZONE_TYPE_CANONICAL, nullptr, nullptr, status); 1679 if (U_SUCCESS(status)) { 1680 while ((id = tzIDs->snext(status)) != nullptr) { 1681 if (U_FAILURE(status)) { 1682 break; 1683 } 1684 UnicodeString copy(*id); 1685 void* value = uhash_get(fTZNamesMap, copy.getTerminatedBuffer()); 1686 if (value == nullptr) { 1687 // loadStrings also loads related metazone strings 1688 loadStrings(*id, status); 1689 } 1690 } 1691 } 1692 delete tzIDs; 1693 } 1694 } 1695 1696 1697 1698 static const char16_t gEtcPrefix[] = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/" 1699 static const int32_t gEtcPrefixLen = 4; 1700 static const char16_t gSystemVPrefix[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/ 1701 static const int32_t gSystemVPrefixLen = 8; 1702 static const char16_t gRiyadh8[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8" 1703 static const int32_t gRiyadh8Len = 7; 1704 1705 UnicodeString& U_EXPORT2 1706 TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) { 1707 if (tzID.isEmpty() || tzID.startsWith(gEtcPrefix, gEtcPrefixLen) 1708 || tzID.startsWith(gSystemVPrefix, gSystemVPrefixLen) || tzID.indexOf(gRiyadh8, gRiyadh8Len, 0) > 0) { 1709 name.setToBogus(); 1710 return name; 1711 } 1712 1713 int32_t sep = tzID.lastIndexOf(static_cast<char16_t>(0x2F) /* '/' */); 1714 if (sep > 0 && sep + 1 < tzID.length()) { 1715 name.setTo(tzID, sep + 1); 1716 name.findAndReplace(UnicodeString(static_cast<char16_t>(0x5f) /* _ */), 1717 UnicodeString(static_cast<char16_t>(0x20) /* space */)); 1718 } else { 1719 name.setToBogus(); 1720 } 1721 return name; 1722 } 1723 1724 // --------------------------------------------------- 1725 // TZDBTimeZoneNames and its supporting classes 1726 // 1727 // TZDBTimeZoneNames is an implementation class of 1728 // TimeZoneNames holding the IANA tz database abbreviations. 1729 // --------------------------------------------------- 1730 1731 class TZDBNames : public UMemory { 1732 public: 1733 virtual ~TZDBNames(); 1734 1735 static TZDBNames* createInstance(UResourceBundle* rb, const char* key); 1736 const char16_t* getName(UTimeZoneNameType type) const; 1737 const char** getParseRegions(int32_t& numRegions) const; 1738 1739 protected: 1740 TZDBNames(const char16_t** names, char** regions, int32_t numRegions); 1741 1742 private: 1743 const char16_t** fNames; 1744 char** fRegions; 1745 int32_t fNumRegions; 1746 }; 1747 1748 TZDBNames::TZDBNames(const char16_t** names, char** regions, int32_t numRegions) 1749 : fNames(names), 1750 fRegions(regions), 1751 fNumRegions(numRegions) { 1752 } 1753 1754 TZDBNames::~TZDBNames() { 1755 if (fNames != nullptr) { 1756 uprv_free(fNames); 1757 } 1758 if (fRegions != nullptr) { 1759 char **p = fRegions; 1760 for (int32_t i = 0; i < fNumRegions; p++, i++) { 1761 uprv_free(*p); 1762 } 1763 uprv_free(fRegions); 1764 } 1765 } 1766 1767 TZDBNames* 1768 TZDBNames::createInstance(UResourceBundle* rb, const char* key) { 1769 if (rb == nullptr || key == nullptr || *key == 0) { 1770 return nullptr; 1771 } 1772 1773 UErrorCode status = U_ZERO_ERROR; 1774 1775 const char16_t **names = nullptr; 1776 char** regions = nullptr; 1777 int32_t numRegions = 0; 1778 1779 int32_t len = 0; 1780 1781 UResourceBundle* rbTable = nullptr; 1782 rbTable = ures_getByKey(rb, key, rbTable, &status); 1783 if (U_FAILURE(status)) { 1784 return nullptr; 1785 } 1786 1787 names = static_cast<const char16_t**>(uprv_malloc(sizeof(const char16_t*) * TZDBNAMES_KEYS_SIZE)); 1788 UBool isEmpty = true; 1789 if (names != nullptr) { 1790 for (int32_t i = 0; i < TZDBNAMES_KEYS_SIZE; i++) { 1791 status = U_ZERO_ERROR; 1792 const char16_t *value = ures_getStringByKey(rbTable, TZDBNAMES_KEYS[i], &len, &status); 1793 if (U_FAILURE(status) || len == 0) { 1794 names[i] = nullptr; 1795 } else { 1796 names[i] = value; 1797 isEmpty = false; 1798 } 1799 } 1800 } 1801 1802 if (isEmpty) { 1803 if (names != nullptr) { 1804 uprv_free(names); 1805 } 1806 return nullptr; 1807 } 1808 1809 UResourceBundle *regionsRes = ures_getByKey(rbTable, "parseRegions", nullptr, &status); 1810 UBool regionError = false; 1811 if (U_SUCCESS(status)) { 1812 numRegions = ures_getSize(regionsRes); 1813 if (numRegions > 0) { 1814 regions = static_cast<char**>(uprv_malloc(sizeof(char*) * numRegions)); 1815 if (regions != nullptr) { 1816 char **pRegion = regions; 1817 for (int32_t i = 0; i < numRegions; i++, pRegion++) { 1818 *pRegion = nullptr; 1819 } 1820 // filling regions 1821 pRegion = regions; 1822 for (int32_t i = 0; i < numRegions; i++, pRegion++) { 1823 status = U_ZERO_ERROR; 1824 const char16_t *uregion = ures_getStringByIndex(regionsRes, i, &len, &status); 1825 if (U_FAILURE(status)) { 1826 regionError = true; 1827 break; 1828 } 1829 *pRegion = static_cast<char*>(uprv_malloc(sizeof(char) * (len + 1))); 1830 if (*pRegion == nullptr) { 1831 regionError = true; 1832 break; 1833 } 1834 u_UCharsToChars(uregion, *pRegion, len); 1835 (*pRegion)[len] = 0; 1836 } 1837 } 1838 } 1839 } 1840 ures_close(regionsRes); 1841 ures_close(rbTable); 1842 1843 if (regionError) { 1844 if (names != nullptr) { 1845 uprv_free(names); 1846 } 1847 if (regions != nullptr) { 1848 char **p = regions; 1849 for (int32_t i = 0; i < numRegions; p++, i++) { 1850 uprv_free(*p); 1851 } 1852 uprv_free(regions); 1853 } 1854 return nullptr; 1855 } 1856 1857 return new TZDBNames(names, regions, numRegions); 1858 } 1859 1860 const char16_t* 1861 TZDBNames::getName(UTimeZoneNameType type) const { 1862 if (fNames == nullptr) { 1863 return nullptr; 1864 } 1865 const char16_t *name = nullptr; 1866 switch(type) { 1867 case UTZNM_SHORT_STANDARD: 1868 name = fNames[0]; 1869 break; 1870 case UTZNM_SHORT_DAYLIGHT: 1871 name = fNames[1]; 1872 break; 1873 default: 1874 name = nullptr; 1875 } 1876 return name; 1877 } 1878 1879 const char** 1880 TZDBNames::getParseRegions(int32_t& numRegions) const { 1881 if (fRegions == nullptr) { 1882 numRegions = 0; 1883 } else { 1884 numRegions = fNumRegions; 1885 } 1886 return (const char**)fRegions; 1887 } 1888 1889 U_CDECL_BEGIN 1890 /** 1891 * TZDBNameInfo stores metazone name information for the IANA abbreviations 1892 * in the trie 1893 */ 1894 typedef struct TZDBNameInfo { 1895 const char16_t* mzID; 1896 UTimeZoneNameType type; 1897 UBool ambiguousType; 1898 const char** parseRegions; 1899 int32_t nRegions; 1900 } TZDBNameInfo; 1901 U_CDECL_END 1902 1903 1904 class TZDBNameSearchHandler : public TextTrieMapSearchResultHandler { 1905 public: 1906 TZDBNameSearchHandler(uint32_t types, StringPiece region); 1907 virtual ~TZDBNameSearchHandler(); 1908 1909 UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) override; 1910 TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen); 1911 1912 private: 1913 uint32_t fTypes; 1914 int32_t fMaxMatchLen; 1915 TimeZoneNames::MatchInfoCollection* fResults; 1916 StringPiece fRegion; 1917 }; 1918 1919 TZDBNameSearchHandler::TZDBNameSearchHandler(uint32_t types, StringPiece region) 1920 : fTypes(types), fMaxMatchLen(0), fResults(nullptr), fRegion(region) { 1921 } 1922 1923 TZDBNameSearchHandler::~TZDBNameSearchHandler() { 1924 delete fResults; 1925 } 1926 1927 UBool 1928 TZDBNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) { 1929 if (U_FAILURE(status)) { 1930 return false; 1931 } 1932 1933 TZDBNameInfo *match = nullptr; 1934 TZDBNameInfo *defaultRegionMatch = nullptr; 1935 1936 if (node->hasValues()) { 1937 int32_t valuesCount = node->countValues(); 1938 for (int32_t i = 0; i < valuesCount; i++) { 1939 TZDBNameInfo *ninfo = (TZDBNameInfo *)node->getValue(i); 1940 if (ninfo == nullptr) { 1941 continue; 1942 } 1943 if ((ninfo->type & fTypes) != 0) { 1944 // Some tz database abbreviations are ambiguous. For example, 1945 // CST means either Central Standard Time or China Standard Time. 1946 // Unlike CLDR time zone display names, this implementation 1947 // does not use unique names. And TimeZoneFormat does not expect 1948 // multiple results returned for the same time zone type. 1949 // For this reason, this implementation resolve one among same 1950 // zone type with a same name at this level. 1951 if (ninfo->parseRegions == nullptr) { 1952 // parseRegions == null means this is the default metazone 1953 // mapping for the abbreviation. 1954 if (defaultRegionMatch == nullptr) { 1955 match = defaultRegionMatch = ninfo; 1956 } 1957 } else { 1958 UBool matchRegion = false; 1959 // non-default metazone mapping for an abbreviation 1960 // comes with applicable regions. For example, the default 1961 // metazone mapping for "CST" is America_Central, 1962 // but if region is one of CN/MO/TW, "CST" is parsed 1963 // as metazone China (China Standard Time). 1964 for (int32_t j = 0; j < ninfo->nRegions; j++) { 1965 const char *region = ninfo->parseRegions[j]; 1966 if (fRegion == region) { 1967 match = ninfo; 1968 matchRegion = true; 1969 break; 1970 } 1971 } 1972 if (matchRegion) { 1973 break; 1974 } 1975 if (match == nullptr) { 1976 match = ninfo; 1977 } 1978 } 1979 } 1980 } 1981 1982 if (match != nullptr) { 1983 UTimeZoneNameType ntype = match->type; 1984 // Note: Workaround for duplicated standard/daylight names 1985 // The tz database contains a few zones sharing a 1986 // same name for both standard time and daylight saving 1987 // time. For example, Australia/Sydney observes DST, 1988 // but "EST" is used for both standard and daylight. 1989 // When both SHORT_STANDARD and SHORT_DAYLIGHT are included 1990 // in the find operation, we cannot tell which one was 1991 // actually matched. 1992 // TimeZoneFormat#parse returns a matched name type (standard 1993 // or daylight) and DateFormat implementation uses the info to 1994 // to adjust actual time. To avoid false type information, 1995 // this implementation replaces the name type with SHORT_GENERIC. 1996 if (match->ambiguousType 1997 && (ntype == UTZNM_SHORT_STANDARD || ntype == UTZNM_SHORT_DAYLIGHT) 1998 && (fTypes & UTZNM_SHORT_STANDARD) != 0 1999 && (fTypes & UTZNM_SHORT_DAYLIGHT) != 0) { 2000 ntype = UTZNM_SHORT_GENERIC; 2001 } 2002 2003 if (fResults == nullptr) { 2004 fResults = new TimeZoneNames::MatchInfoCollection(); 2005 if (fResults == nullptr) { 2006 status = U_MEMORY_ALLOCATION_ERROR; 2007 } 2008 } 2009 if (U_SUCCESS(status)) { 2010 U_ASSERT(fResults != nullptr); 2011 U_ASSERT(match->mzID != nullptr); 2012 fResults->addMetaZone(ntype, matchLength, UnicodeString(match->mzID, -1), status); 2013 if (U_SUCCESS(status) && matchLength > fMaxMatchLen) { 2014 fMaxMatchLen = matchLength; 2015 } 2016 } 2017 } 2018 } 2019 return true; 2020 } 2021 2022 TimeZoneNames::MatchInfoCollection* 2023 TZDBNameSearchHandler::getMatches(int32_t& maxMatchLen) { 2024 // give the ownership to the caller 2025 TimeZoneNames::MatchInfoCollection* results = fResults; 2026 maxMatchLen = fMaxMatchLen; 2027 2028 // reset 2029 fResults = nullptr; 2030 fMaxMatchLen = 0; 2031 return results; 2032 } 2033 2034 U_CDECL_BEGIN 2035 /** 2036 * Deleter for TZDBNames 2037 */ 2038 static void U_CALLCONV 2039 deleteTZDBNames(void *obj) { 2040 if (obj != EMPTY) { 2041 delete (TZDBNames *)obj; 2042 } 2043 } 2044 2045 static void U_CALLCONV initTZDBNamesMap(UErrorCode &status) { 2046 gTZDBNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status); 2047 if (U_FAILURE(status)) { 2048 gTZDBNamesMap = nullptr; 2049 return; 2050 } 2051 // no key deleters for tzdb name maps 2052 uhash_setValueDeleter(gTZDBNamesMap, deleteTZDBNames); 2053 ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup); 2054 } 2055 2056 /** 2057 * Deleter for TZDBNameInfo 2058 */ 2059 static void U_CALLCONV 2060 deleteTZDBNameInfo(void *obj) { 2061 if (obj != nullptr) { 2062 uprv_free(obj); 2063 } 2064 } 2065 2066 static void U_CALLCONV prepareFind(UErrorCode &status) { 2067 if (U_FAILURE(status)) { 2068 return; 2069 } 2070 gTZDBNamesTrie = new TextTrieMap(true, deleteTZDBNameInfo); 2071 if (gTZDBNamesTrie == nullptr) { 2072 status = U_MEMORY_ALLOCATION_ERROR; 2073 return; 2074 } 2075 2076 const UnicodeString *mzID; 2077 StringEnumeration *mzIDs = TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status); 2078 if (U_SUCCESS(status)) { 2079 while ((mzID = mzIDs->snext(status)) != nullptr && U_SUCCESS(status)) { 2080 const TZDBNames *names = TZDBTimeZoneNames::getMetaZoneNames(*mzID, status); 2081 if (U_FAILURE(status)) { 2082 break; 2083 } 2084 if (names == nullptr) { 2085 continue; 2086 } 2087 const char16_t *std = names->getName(UTZNM_SHORT_STANDARD); 2088 const char16_t *dst = names->getName(UTZNM_SHORT_DAYLIGHT); 2089 if (std == nullptr && dst == nullptr) { 2090 continue; 2091 } 2092 int32_t numRegions = 0; 2093 const char **parseRegions = names->getParseRegions(numRegions); 2094 2095 // The tz database contains a few zones sharing a 2096 // same name for both standard time and daylight saving 2097 // time. For example, Australia/Sydney observes DST, 2098 // but "EST" is used for both standard and daylight. 2099 // we need to store the information for later processing. 2100 UBool ambiguousType = (std != nullptr && dst != nullptr && u_strcmp(std, dst) == 0); 2101 2102 const char16_t *uMzID = ZoneMeta::findMetaZoneID(*mzID); 2103 if (std != nullptr) { 2104 TZDBNameInfo *stdInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo)); 2105 if (stdInf == nullptr) { 2106 status = U_MEMORY_ALLOCATION_ERROR; 2107 break; 2108 } 2109 stdInf->mzID = uMzID; 2110 stdInf->type = UTZNM_SHORT_STANDARD; 2111 stdInf->ambiguousType = ambiguousType; 2112 stdInf->parseRegions = parseRegions; 2113 stdInf->nRegions = numRegions; 2114 gTZDBNamesTrie->put(std, stdInf, status); 2115 } 2116 if (U_SUCCESS(status) && dst != nullptr) { 2117 TZDBNameInfo *dstInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo)); 2118 if (dstInf == nullptr) { 2119 status = U_MEMORY_ALLOCATION_ERROR; 2120 break; 2121 } 2122 dstInf->mzID = uMzID; 2123 dstInf->type = UTZNM_SHORT_DAYLIGHT; 2124 dstInf->ambiguousType = ambiguousType; 2125 dstInf->parseRegions = parseRegions; 2126 dstInf->nRegions = numRegions; 2127 gTZDBNamesTrie->put(dst, dstInf, status); 2128 } 2129 } 2130 } 2131 delete mzIDs; 2132 2133 if (U_FAILURE(status)) { 2134 delete gTZDBNamesTrie; 2135 gTZDBNamesTrie = nullptr; 2136 return; 2137 } 2138 2139 ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup); 2140 } 2141 2142 U_CDECL_END 2143 2144 TZDBTimeZoneNames::TZDBTimeZoneNames(const Locale& locale) 2145 : fLocale(locale), fRegion() { 2146 UBool useWorld = true; 2147 const char* region = fLocale.getCountry(); 2148 int32_t regionLen = static_cast<int32_t>(uprv_strlen(region)); 2149 if (regionLen == 0) { 2150 UErrorCode status = U_ZERO_ERROR; 2151 CharString loc = ulocimp_addLikelySubtags(fLocale.getName(), status); 2152 CharString tmp; 2153 ulocimp_getSubtags(loc.toStringPiece(), nullptr, nullptr, &tmp, nullptr, nullptr, status); 2154 fRegion = tmp.toStringPiece(); 2155 U_ASSERT(fRegion.isEmpty() == tmp.isEmpty()); 2156 if (U_SUCCESS(status)) { 2157 useWorld = false; 2158 } 2159 } else { 2160 fRegion = {region, static_cast<std::string_view::size_type>(regionLen)}; 2161 U_ASSERT(!fRegion.isEmpty()); 2162 useWorld = false; 2163 } 2164 if (useWorld) { 2165 fRegion = "001"; 2166 U_ASSERT(!fRegion.isEmpty()); 2167 } 2168 } 2169 2170 TZDBTimeZoneNames::~TZDBTimeZoneNames() { 2171 } 2172 2173 bool 2174 TZDBTimeZoneNames::operator==(const TimeZoneNames& other) const { 2175 if (this == &other) { 2176 return true; 2177 } 2178 // No implementation for now 2179 return false; 2180 } 2181 2182 TZDBTimeZoneNames* 2183 TZDBTimeZoneNames::clone() const { 2184 return new TZDBTimeZoneNames(fLocale); 2185 } 2186 2187 StringEnumeration* 2188 TZDBTimeZoneNames::getAvailableMetaZoneIDs(UErrorCode& status) const { 2189 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status); 2190 } 2191 2192 StringEnumeration* 2193 TZDBTimeZoneNames::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const { 2194 return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status); 2195 } 2196 2197 UnicodeString& 2198 TZDBTimeZoneNames::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const { 2199 return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID); 2200 } 2201 2202 UnicodeString& 2203 TZDBTimeZoneNames::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const { 2204 return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID); 2205 } 2206 2207 UnicodeString& 2208 TZDBTimeZoneNames::getMetaZoneDisplayName(const UnicodeString& mzID, 2209 UTimeZoneNameType type, 2210 UnicodeString& name) const { 2211 name.setToBogus(); 2212 if (mzID.isEmpty()) { 2213 return name; 2214 } 2215 2216 UErrorCode status = U_ZERO_ERROR; 2217 const TZDBNames *tzdbNames = TZDBTimeZoneNames::getMetaZoneNames(mzID, status); 2218 if (U_SUCCESS(status)) { 2219 if (tzdbNames != nullptr) { 2220 const char16_t *s = tzdbNames->getName(type); 2221 if (s != nullptr) { 2222 name.setTo(true, s, -1); 2223 } 2224 } 2225 } 2226 2227 return name; 2228 } 2229 2230 UnicodeString& 2231 TZDBTimeZoneNames::getTimeZoneDisplayName(const UnicodeString& /* tzID */, UTimeZoneNameType /* type */, UnicodeString& name) const { 2232 // No abbreviations associated a zone directly for now. 2233 name.setToBogus(); 2234 return name; 2235 } 2236 2237 TZDBTimeZoneNames::MatchInfoCollection* 2238 TZDBTimeZoneNames::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { 2239 umtx_initOnce(gTZDBNamesTrieInitOnce, &prepareFind, status); 2240 if (U_FAILURE(status)) { 2241 return nullptr; 2242 } 2243 2244 TZDBNameSearchHandler handler(types, fRegion.data()); 2245 gTZDBNamesTrie->search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); 2246 if (U_FAILURE(status)) { 2247 return nullptr; 2248 } 2249 int32_t maxLen = 0; 2250 return handler.getMatches(maxLen); 2251 } 2252 2253 const TZDBNames* 2254 TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) { 2255 umtx_initOnce(gTZDBNamesMapInitOnce, &initTZDBNamesMap, status); 2256 if (U_FAILURE(status)) { 2257 return nullptr; 2258 } 2259 2260 TZDBNames* tzdbNames = nullptr; 2261 2262 char16_t mzIDKey[ZID_KEY_MAX + 1]; 2263 mzID.extract(mzIDKey, ZID_KEY_MAX, status); 2264 if (U_FAILURE(status)) { 2265 return nullptr; 2266 } 2267 mzIDKey[mzID.length()] = 0; 2268 if (!uprv_isInvariantUString(mzIDKey, mzID.length())) { 2269 status = U_ILLEGAL_ARGUMENT_ERROR; 2270 return nullptr; 2271 } 2272 2273 static UMutex gTZDBNamesMapLock; 2274 umtx_lock(&gTZDBNamesMapLock); 2275 { 2276 void *cacheVal = uhash_get(gTZDBNamesMap, mzIDKey); 2277 if (cacheVal == nullptr) { 2278 UResourceBundle *zoneStringsRes = ures_openDirect(U_ICUDATA_ZONE, "tzdbNames", &status); 2279 zoneStringsRes = ures_getByKey(zoneStringsRes, gZoneStrings, zoneStringsRes, &status); 2280 char key[ZID_KEY_MAX + 1]; 2281 mergeTimeZoneKey(mzID, key, sizeof(key), status); 2282 if (U_SUCCESS(status)) { 2283 tzdbNames = TZDBNames::createInstance(zoneStringsRes, key); 2284 2285 if (tzdbNames == nullptr) { 2286 cacheVal = (void *)EMPTY; 2287 } else { 2288 cacheVal = tzdbNames; 2289 } 2290 // Use the persistent ID as the resource key, so we can 2291 // avoid duplications. 2292 // TODO: Is there a more efficient way, like intern() in Java? 2293 void* newKey = (void*) ZoneMeta::findMetaZoneID(mzID); 2294 if (newKey != nullptr) { 2295 uhash_put(gTZDBNamesMap, newKey, cacheVal, &status); 2296 if (U_FAILURE(status)) { 2297 if (tzdbNames != nullptr) { 2298 delete tzdbNames; 2299 tzdbNames = nullptr; 2300 } 2301 } 2302 } else { 2303 // Should never happen with a valid input 2304 if (tzdbNames != nullptr) { 2305 // It's not possible that we get a valid tzdbNames with unknown ID. 2306 // But just in case.. 2307 delete tzdbNames; 2308 tzdbNames = nullptr; 2309 } 2310 } 2311 } 2312 ures_close(zoneStringsRes); 2313 } else if (cacheVal != EMPTY) { 2314 tzdbNames = static_cast<TZDBNames*>(cacheVal); 2315 } 2316 } 2317 umtx_unlock(&gTZDBNamesMapLock); 2318 2319 return tzdbNames; 2320 } 2321 2322 U_NAMESPACE_END 2323 2324 2325 #endif /* #if !UCONFIG_NO_FORMATTING */ 2326 2327 //eof