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