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