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