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