1 /* 2 ******************************************************************************* 3 * Copyright (C) 2007-2010, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7 8 #include "unicode/utypes.h" 9 10 #if !UCONFIG_NO_FORMATTING 11 12 #include "zstrfmt.h" 13 14 #include "unicode/ustring.h" 15 #include "unicode/putil.h" 16 #include "unicode/msgfmt.h" 17 #include "unicode/basictz.h" 18 #include "unicode/simpletz.h" 19 #include "unicode/rbtz.h" 20 #include "unicode/vtzone.h" 21 22 #include "uvector.h" 23 #include "cstring.h" 24 #include "cmemory.h" 25 #include "uresimp.h" 26 #include "zonemeta.h" 27 #include "olsontz.h" 28 #include "umutex.h" 29 #include "ucln_in.h" 30 #include "uassert.h" 31 #include "ureslocs.h" 32 33 /** 34 * global ZoneStringFormatCache stuffs 35 */ 36 static UMTX gZSFCacheLock = NULL; 37 static U_NAMESPACE_QUALIFIER ZSFCache *gZoneStringFormatCache = NULL; 38 39 U_CDECL_BEGIN 40 /** 41 * ZoneStringFormatCache cleanup callback func 42 */ 43 static UBool U_CALLCONV zoneStringFormat_cleanup(void) 44 { 45 umtx_destroy(&gZSFCacheLock); 46 if (gZoneStringFormatCache != NULL) { 47 delete gZoneStringFormatCache; 48 gZoneStringFormatCache = NULL; 49 } 50 gZoneStringFormatCache = NULL; 51 return TRUE; 52 } 53 54 /** 55 * Deleter for ZoneStringInfo 56 */ 57 static void U_CALLCONV 58 deleteZoneStringInfo(void *obj) { 59 delete (U_NAMESPACE_QUALIFIER ZoneStringInfo*)obj; 60 } 61 62 /** 63 * Deleter for ZoneStrings 64 */ 65 static void U_CALLCONV 66 deleteZoneStrings(void *obj) { 67 delete (U_NAMESPACE_QUALIFIER ZoneStrings*)obj; 68 } 69 U_CDECL_END 70 71 U_NAMESPACE_BEGIN 72 73 #define ZID_KEY_MAX 128 74 75 static const char gCountriesTag[] = "Countries"; 76 static const char gZoneStringsTag[] = "zoneStrings"; 77 static const char gShortGenericTag[] = "sg"; 78 static const char gShortStandardTag[] = "ss"; 79 static const char gShortDaylightTag[] = "sd"; 80 static const char gLongGenericTag[] = "lg"; 81 static const char gLongStandardTag[] = "ls"; 82 static const char gLongDaylightTag[] = "ld"; 83 static const char gExemplarCityTag[] = "ec"; 84 static const char gCommonlyUsedTag[] = "cu"; 85 static const char gFallbackFormatTag[] = "fallbackFormat"; 86 static const char gRegionFormatTag[] = "regionFormat"; 87 88 #define MZID_PREFIX_LEN 5 89 static const char gMetazoneIdPrefix[] = "meta:"; 90 91 #define MAX_METAZONES_PER_ZONE 10 92 93 static const UChar gDefFallbackPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})" 94 static const UChar gDefRegionPattern[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}" 95 static const UChar gCommonlyUsedTrue[] = {0x31, 0x00}; // "1" 96 97 static const double kDstCheckRange = (double)184*U_MILLIS_PER_DAY; 98 99 static int32_t 100 getTimeZoneTranslationTypeIndex(TimeZoneTranslationType type) { 101 int32_t typeIdx = 0; 102 switch (type) { 103 case LOCATION: 104 typeIdx = ZSIDX_LOCATION; 105 break; 106 case GENERIC_LONG: 107 typeIdx = ZSIDX_LONG_GENERIC; 108 break; 109 case GENERIC_SHORT: 110 typeIdx = ZSIDX_SHORT_GENERIC; 111 break; 112 case STANDARD_LONG: 113 typeIdx = ZSIDX_LONG_STANDARD; 114 break; 115 case STANDARD_SHORT: 116 typeIdx = ZSIDX_SHORT_STANDARD; 117 break; 118 case DAYLIGHT_LONG: 119 typeIdx = ZSIDX_LONG_DAYLIGHT; 120 break; 121 case DAYLIGHT_SHORT: 122 typeIdx = ZSIDX_SHORT_DAYLIGHT; 123 break; 124 } 125 return typeIdx; 126 } 127 128 static int32_t 129 getTimeZoneTranslationType(TimeZoneTranslationTypeIndex typeIdx) { 130 int32_t type = 0; 131 switch (typeIdx) { 132 case ZSIDX_LOCATION: 133 type = LOCATION; 134 break; 135 case ZSIDX_LONG_GENERIC: 136 type = GENERIC_LONG; 137 break; 138 case ZSIDX_SHORT_GENERIC: 139 type = GENERIC_SHORT; 140 break; 141 case ZSIDX_LONG_STANDARD: 142 type = STANDARD_LONG; 143 break; 144 case ZSIDX_SHORT_STANDARD: 145 type = STANDARD_SHORT; 146 break; 147 case ZSIDX_LONG_DAYLIGHT: 148 type = DAYLIGHT_LONG; 149 break; 150 case ZSIDX_COUNT: 151 case ZSIDX_SHORT_DAYLIGHT: 152 type = DAYLIGHT_SHORT; 153 break; 154 default: 155 break; 156 } 157 return type; 158 } 159 160 #define DEFAULT_CHARACTERNODE_CAPACITY 1 161 162 // ---------------------------------------------------------------------------- 163 void CharacterNode::clear() { 164 uprv_memset(this, 0, sizeof(*this)); 165 } 166 167 void CharacterNode::deleteValues() { 168 if (fValues == NULL) { 169 // Do nothing. 170 } else if (!fHasValuesVector) { 171 deleteZoneStringInfo(fValues); 172 } else { 173 delete (UVector *)fValues; 174 } 175 } 176 177 void 178 CharacterNode::addValue(void *value, UErrorCode &status) { 179 if (U_FAILURE(status)) { 180 deleteZoneStringInfo(value); 181 return; 182 } 183 if (fValues == NULL) { 184 fValues = value; 185 } else { 186 // At least one value already. 187 if (!fHasValuesVector) { 188 // There is only one value so far, and not in a vector yet. 189 // Create a vector and add the old value. 190 UVector *values = new UVector(deleteZoneStringInfo, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status); 191 if (U_FAILURE(status)) { 192 deleteZoneStringInfo(value); 193 return; 194 } 195 values->addElement(fValues, status); 196 fValues = values; 197 fHasValuesVector = TRUE; 198 } 199 // Add the new value. 200 ((UVector *)fValues)->addElement(value, status); 201 } 202 } 203 204 //---------------------------------------------------------------------------- 205 // Virtual destructor to avoid warning 206 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){ 207 } 208 209 // ---------------------------------------------------------------------------- 210 TextTrieMap::TextTrieMap(UBool ignoreCase) 211 : fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0), 212 fLazyContents(NULL), fIsEmpty(TRUE) { 213 } 214 215 TextTrieMap::~TextTrieMap() { 216 int32_t index; 217 for (index = 0; index < fNodesCount; ++index) { 218 fNodes[index].deleteValues(); 219 } 220 uprv_free(fNodes); 221 if (fLazyContents != NULL) { 222 for (int32_t i=0; i<fLazyContents->size(); i+=2) { 223 ZoneStringInfo *zsinf = (ZoneStringInfo *)fLazyContents->elementAt(i+1); 224 delete zsinf; 225 } 226 delete fLazyContents; 227 } 228 } 229 230 int32_t TextTrieMap::isEmpty() const { 231 // Use a separate field for fIsEmpty because it will remain unchanged once the 232 // Trie is built, while fNodes and fLazyContents change with the lazy init 233 // of the nodes structure. Trying to test the changing fields has 234 // thread safety complications. 235 return fIsEmpty; 236 } 237 238 239 // We defer actually building the TextTrieMap node structure until the first time a 240 // search is performed. put() simply saves the parameters in case we do 241 // eventually need to build it. 242 // 243 void 244 TextTrieMap::put(const UnicodeString &key, void *value, ZSFStringPool &sp, UErrorCode &status) { 245 fIsEmpty = FALSE; 246 if (fLazyContents == NULL) { 247 fLazyContents = new UVector(status); 248 if (fLazyContents == NULL) { 249 status = U_MEMORY_ALLOCATION_ERROR; 250 } 251 } 252 if (U_FAILURE(status)) { 253 return; 254 } 255 UChar *s = const_cast<UChar *>(sp.get(key, status)); 256 fLazyContents->addElement(s, status); 257 fLazyContents->addElement(value, status); 258 } 259 260 261 void 262 TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) { 263 if (fNodes == NULL) { 264 fNodesCapacity = 512; 265 fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode)); 266 fNodes[0].clear(); // Init root node. 267 fNodesCount = 1; 268 } 269 270 UnicodeString foldedKey; 271 const UChar *keyBuffer; 272 int32_t keyLength; 273 if (fIgnoreCase) { 274 // Ok to use fastCopyFrom() because we discard the copy when we return. 275 foldedKey.fastCopyFrom(key).foldCase(); 276 keyBuffer = foldedKey.getBuffer(); 277 keyLength = foldedKey.length(); 278 } else { 279 keyBuffer = key.getBuffer(); 280 keyLength = key.length(); 281 } 282 283 CharacterNode *node = fNodes; 284 int32_t index; 285 for (index = 0; index < keyLength; ++index) { 286 node = addChildNode(node, keyBuffer[index], status); 287 } 288 node->addValue(value, status); 289 } 290 291 UBool 292 TextTrieMap::growNodes() { 293 if (fNodesCapacity == 0xffff) { 294 return FALSE; // We use 16-bit node indexes. 295 } 296 int32_t newCapacity = fNodesCapacity + 1000; 297 if (newCapacity > 0xffff) { 298 newCapacity = 0xffff; 299 } 300 CharacterNode *newNodes = (CharacterNode *)uprv_malloc(newCapacity * sizeof(CharacterNode)); 301 if (newNodes == NULL) { 302 return FALSE; 303 } 304 uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode)); 305 uprv_free(fNodes); 306 fNodes = newNodes; 307 fNodesCapacity = newCapacity; 308 return TRUE; 309 } 310 311 CharacterNode* 312 TextTrieMap::addChildNode(CharacterNode *parent, UChar c, UErrorCode &status) { 313 if (U_FAILURE(status)) { 314 return NULL; 315 } 316 // Linear search of the sorted list of children. 317 uint16_t prevIndex = 0; 318 uint16_t nodeIndex = parent->fFirstChild; 319 while (nodeIndex > 0) { 320 CharacterNode *current = fNodes + nodeIndex; 321 UChar childCharacter = current->fCharacter; 322 if (childCharacter == c) { 323 return current; 324 } else if (childCharacter > c) { 325 break; 326 } 327 prevIndex = nodeIndex; 328 nodeIndex = current->fNextSibling; 329 } 330 331 // Ensure capacity. Grow fNodes[] if needed. 332 if (fNodesCount == fNodesCapacity) { 333 int32_t parentIndex = (int32_t)(parent - fNodes); 334 if (!growNodes()) { 335 status = U_MEMORY_ALLOCATION_ERROR; 336 return NULL; 337 } 338 parent = fNodes + parentIndex; 339 } 340 341 // Insert a new child node with c in sorted order. 342 CharacterNode *node = fNodes + fNodesCount; 343 node->clear(); 344 node->fCharacter = c; 345 node->fNextSibling = nodeIndex; 346 if (prevIndex == 0) { 347 parent->fFirstChild = (uint16_t)fNodesCount; 348 } else { 349 fNodes[prevIndex].fNextSibling = (uint16_t)fNodesCount; 350 } 351 ++fNodesCount; 352 return node; 353 } 354 355 CharacterNode* 356 TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const { 357 // Linear search of the sorted list of children. 358 uint16_t nodeIndex = parent->fFirstChild; 359 while (nodeIndex > 0) { 360 CharacterNode *current = fNodes + nodeIndex; 361 UChar childCharacter = current->fCharacter; 362 if (childCharacter == c) { 363 return current; 364 } else if (childCharacter > c) { 365 break; 366 } 367 nodeIndex = current->fNextSibling; 368 } 369 return NULL; 370 } 371 372 // Mutex for protecting the lazy creation of the Trie node structure on the first call to search(). 373 static UMTX TextTrieMutex; 374 375 // buildTrie() - The Trie node structure is needed. Create it from the data that was 376 // saved at the time the ZoneStringFormatter was created. The Trie is only 377 // needed for parsing operations, which are less common than formatting, 378 // and the Trie is big, which is why its creation is deferred until first use. 379 void TextTrieMap::buildTrie(UErrorCode &status) { 380 umtx_lock(&TextTrieMutex); 381 if (fLazyContents != NULL) { 382 for (int32_t i=0; i<fLazyContents->size(); i+=2) { 383 const UChar *key = (UChar *)fLazyContents->elementAt(i); 384 void *val = fLazyContents->elementAt(i+1); 385 UnicodeString keyString(TRUE, key, -1); // Aliasing UnicodeString constructor. 386 putImpl(keyString, val, status); 387 } 388 delete fLazyContents; 389 fLazyContents = NULL; 390 } 391 umtx_unlock(&TextTrieMutex); 392 } 393 394 395 void 396 TextTrieMap::search(const UnicodeString &text, int32_t start, 397 TextTrieMapSearchResultHandler *handler, UErrorCode &status) const { 398 UBool trieNeedsInitialization = FALSE; 399 UMTX_CHECK(&TextTrieMutex, fLazyContents != NULL, trieNeedsInitialization); 400 if (trieNeedsInitialization) { 401 TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this); 402 nonConstThis->buildTrie(status); 403 } 404 if (fNodes == NULL) { 405 return; 406 } 407 search(fNodes, text, start, start, handler, status); 408 } 409 410 void 411 TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start, 412 int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const { 413 if (U_FAILURE(status)) { 414 return; 415 } 416 if (node->hasValues()) { 417 if (!handler->handleMatch(index - start, node, status)) { 418 return; 419 } 420 if (U_FAILURE(status)) { 421 return; 422 } 423 } 424 UChar32 c = text.char32At(index); 425 if (fIgnoreCase) { 426 // size of character may grow after fold operation 427 UnicodeString tmp(c); 428 tmp.foldCase(); 429 int32_t tmpidx = 0; 430 while (tmpidx < tmp.length()) { 431 c = tmp.char32At(tmpidx); 432 node = getChildNode(node, c); 433 if (node == NULL) { 434 break; 435 } 436 tmpidx = tmp.moveIndex32(tmpidx, 1); 437 } 438 } else { 439 node = getChildNode(node, c); 440 } 441 if (node != NULL) { 442 search(node, text, start, index+1, handler, status); 443 } 444 } 445 446 // ---------------------------------------------------------------------------- 447 ZoneStringInfo::ZoneStringInfo(const UnicodeString &id, const UnicodeString &str, 448 TimeZoneTranslationType type, ZSFStringPool &sp, UErrorCode &status) 449 : fType(type) { 450 fId = sp.get(id, status); 451 fStr = sp.get(str, status); 452 } 453 454 ZoneStringInfo::~ZoneStringInfo() { 455 } 456 457 458 // ---------------------------------------------------------------------------- 459 ZoneStringSearchResultHandler::ZoneStringSearchResultHandler(UErrorCode &status) 460 : fResults(status) 461 { 462 clear(); 463 } 464 465 ZoneStringSearchResultHandler::~ZoneStringSearchResultHandler() { 466 clear(); 467 } 468 469 UBool 470 ZoneStringSearchResultHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) { 471 if (U_FAILURE(status)) { 472 return FALSE; 473 } 474 if (node->hasValues()) { 475 int32_t valuesCount = node->countValues(); 476 for (int32_t i = 0; i < valuesCount; i++) { 477 ZoneStringInfo *zsinfo = (ZoneStringInfo*)node->getValue(i); 478 if (zsinfo == NULL) { 479 break; 480 } 481 // Update the results 482 UBool foundType = FALSE; 483 for (int32_t j = 0; j < fResults.size(); j++) { 484 ZoneStringInfo *tmp = (ZoneStringInfo*)fResults.elementAt(j); 485 if (zsinfo->fType == tmp->fType) { 486 int32_t lenidx = getTimeZoneTranslationTypeIndex(tmp->fType); 487 if (matchLength > fMatchLen[lenidx]) { 488 // Same type, longer match 489 fResults.setElementAt(zsinfo, j); 490 fMatchLen[lenidx] = matchLength; 491 } 492 foundType = TRUE; 493 break; 494 } 495 } 496 if (!foundType) { 497 // not found in the current list 498 fResults.addElement(zsinfo, status); 499 fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)] = matchLength; 500 } 501 } 502 } 503 return TRUE; 504 } 505 506 int32_t 507 ZoneStringSearchResultHandler::countMatches(void) { 508 return fResults.size(); 509 } 510 511 const ZoneStringInfo* 512 ZoneStringSearchResultHandler::getMatch(int32_t index, int32_t &matchLength) { 513 ZoneStringInfo *zsinfo = NULL; 514 if (index < fResults.size()) { 515 zsinfo = (ZoneStringInfo*)fResults.elementAt(index); 516 matchLength = fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)]; 517 } 518 return zsinfo; 519 } 520 521 void 522 ZoneStringSearchResultHandler::clear(void) { 523 fResults.removeAllElements(); 524 for (int32_t i = 0; i < (int32_t)(sizeof(fMatchLen)/sizeof(fMatchLen[0])); i++) { 525 fMatchLen[i] = 0; 526 } 527 } 528 529 // Mutex for protecting the lazy load of a zone ID (or a full load) to ZoneStringFormat structures. 530 static UMTX ZoneStringFormatMutex; 531 532 533 // ---------------------------------------------------------------------------- 534 ZoneStringFormat::ZoneStringFormat(const UnicodeString* const* strings, 535 int32_t rowCount, int32_t columnCount, UErrorCode &status) 536 : fLocale(""), 537 fTzidToStrings(NULL), 538 fMzidToStrings(NULL), 539 fZoneStringsTrie(TRUE), 540 fStringPool(status), 541 fZoneStringsArray(NULL), 542 fMetazoneItem(NULL), 543 fZoneItem(NULL), 544 fIsFullyLoaded(FALSE) 545 { 546 if (U_FAILURE(status)) { 547 return; 548 } 549 fLocale.setToBogus(); 550 if (strings == NULL || columnCount <= 0 || rowCount <= 0) { 551 status = U_ILLEGAL_ARGUMENT_ERROR; 552 return; 553 } 554 fTzidToStrings = uhash_open(uhash_hashUChars, // key hash function 555 uhash_compareUChars, // key comparison function 556 NULL, // Value comparison function 557 &status); 558 fMzidToStrings = uhash_open(uhash_hashUChars, 559 uhash_compareUChars, 560 NULL, 561 &status); 562 563 uhash_setValueDeleter(fTzidToStrings, deleteZoneStrings); 564 uhash_setValueDeleter(fMzidToStrings, deleteZoneStrings); 565 566 for (int32_t row = 0; row < rowCount; row++) { 567 if (strings[row][0].isEmpty()) { 568 continue; 569 } 570 UnicodeString *names = new UnicodeString[ZSIDX_COUNT]; 571 for (int32_t col = 1; col < columnCount; col++) { 572 if (!strings[row][col].isEmpty()) { 573 int32_t typeIdx = -1; 574 switch (col) { 575 case 1: 576 typeIdx = ZSIDX_LONG_STANDARD; 577 break; 578 case 2: 579 typeIdx = ZSIDX_SHORT_STANDARD; 580 break; 581 case 3: 582 typeIdx = ZSIDX_LONG_DAYLIGHT; 583 break; 584 case 4: 585 typeIdx = ZSIDX_SHORT_DAYLIGHT; 586 break; 587 case 5: 588 typeIdx = ZSIDX_LOCATION; 589 break; 590 case 6: 591 typeIdx = ZSIDX_LONG_GENERIC; 592 break; 593 case 7: 594 typeIdx = ZSIDX_SHORT_GENERIC; 595 break; 596 } 597 if (typeIdx != -1) { 598 names[typeIdx].setTo(strings[row][col]); 599 600 // Put the name into the trie 601 int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeIdx); 602 ZoneStringInfo *zsinf = new ZoneStringInfo(strings[row][0], 603 strings[row][col], 604 (TimeZoneTranslationType)type, 605 fStringPool, 606 status); 607 fZoneStringsTrie.put(strings[row][col], zsinf, fStringPool, status); 608 if (U_FAILURE(status)) { 609 delete zsinf; 610 goto error_cleanup; 611 } 612 } 613 } 614 } 615 // Note: ZoneStrings constructor adopts and delete the names array. 616 ZoneStrings *zstrings = new ZoneStrings(names, ZSIDX_COUNT, TRUE, NULL, 0, 0, 617 fStringPool, status); 618 UChar *utzid = const_cast<UChar *>(fStringPool.get(strings[row][0], status)); 619 uhash_put(fTzidToStrings, utzid, zstrings, &status); 620 if (U_FAILURE(status)) { 621 delete zstrings; 622 goto error_cleanup; 623 } 624 } 625 fStringPool.freeze(); 626 fIsFullyLoaded = TRUE; 627 return; 628 629 error_cleanup: 630 return; 631 } 632 633 ZoneStringFormat::ZoneStringFormat(const Locale &locale, UErrorCode &status) 634 : fLocale(locale), 635 fTzidToStrings(NULL), 636 fMzidToStrings(NULL), 637 fZoneStringsTrie(TRUE), 638 fStringPool(status), 639 fZoneStringsArray(NULL), 640 fMetazoneItem(NULL), 641 fZoneItem(NULL), 642 fIsFullyLoaded(FALSE) 643 { 644 if (U_FAILURE(status)) { 645 return; 646 } 647 fTzidToStrings = uhash_open(uhash_hashUChars, // key hash function 648 uhash_compareUChars, // key comparison function 649 NULL, // Value comparison function 650 &status); 651 fMzidToStrings = uhash_open(uhash_hashUChars, // key hash function 652 uhash_compareUChars, // key comparison function 653 NULL, // Value comparison function 654 &status); 655 uhash_setValueDeleter(fTzidToStrings, deleteZoneStrings); 656 uhash_setValueDeleter(fMzidToStrings, deleteZoneStrings); 657 } 658 659 // Load only a single zone 660 void 661 ZoneStringFormat::loadZone(const UnicodeString &utzid, UErrorCode &status) 662 { 663 if (fIsFullyLoaded) { 664 return; 665 } 666 667 if (U_FAILURE(status)) { 668 return; 669 } 670 671 umtx_lock(&ZoneStringFormatMutex); 672 673 if (fZoneStringsArray == NULL) { 674 fZoneStringsArray = ures_open(U_ICUDATA_ZONE, fLocale.getName(), &status); 675 fZoneStringsArray = ures_getByKeyWithFallback(fZoneStringsArray, gZoneStringsTag, fZoneStringsArray, &status); 676 if (U_FAILURE(status)) { 677 // If no locale bundles are available, zoneStrings will be null. 678 // We still want to go through the rest of zone strings initialization, 679 // because generic location format is generated from tzid for the case. 680 // The rest of code should work even zoneStrings is null. 681 status = U_ZERO_ERROR; 682 ures_close(fZoneStringsArray); 683 fZoneStringsArray = NULL; 684 } 685 } 686 687 // Skip non-canonical IDs 688 UnicodeString canonicalID; 689 TimeZone::getCanonicalID(utzid, canonicalID, status); 690 if (U_FAILURE(status)) { 691 // Ignore unknown ID - we should not get here, but just in case. 692 // status = U_ZERO_ERROR; 693 umtx_unlock(&ZoneStringFormatMutex); 694 return; 695 } 696 697 if (U_SUCCESS(status)) { 698 if (uhash_count(fTzidToStrings) > 0) { 699 ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer()); 700 if (zstrings != NULL) { 701 umtx_unlock(&ZoneStringFormatMutex); 702 return; // We already about this one 703 } 704 } 705 } 706 707 addSingleZone(canonicalID, status); 708 709 umtx_unlock(&ZoneStringFormatMutex); 710 } 711 712 // Load only a single zone 713 void 714 ZoneStringFormat::addSingleZone(UnicodeString &utzid, UErrorCode &status) 715 { 716 if (U_FAILURE(status)) { 717 return; 718 } 719 720 if (uhash_count(fTzidToStrings) > 0) { 721 ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, utzid.getTerminatedBuffer()); 722 if (zstrings != NULL) { 723 return; // We already about this one 724 } 725 } 726 727 MessageFormat *fallbackFmt = NULL; 728 MessageFormat *regionFmt = NULL; 729 730 fallbackFmt = getFallbackFormat(fLocale, status); 731 if (U_FAILURE(status)) { 732 goto error_cleanup; 733 } 734 regionFmt = getRegionFormat(fLocale, status); 735 if (U_FAILURE(status)) { 736 goto error_cleanup; 737 } 738 739 740 { 741 char zidkey[ZID_KEY_MAX+1]; 742 char tzid[ZID_KEY_MAX+1]; 743 utzid.extract(0, utzid.length(), zidkey, ZID_KEY_MAX, US_INV); 744 utzid.extract(0, utzid.length(), tzid, ZID_KEY_MAX, US_INV); 745 746 const UChar *zstrarray[ZSIDX_COUNT]; 747 const UChar *mzstrarray[ZSIDX_COUNT]; 748 UnicodeString mzPartialLoc[MAX_METAZONES_PER_ZONE][4]; 749 750 // Replace '/' with ':' 751 char *pCity = NULL; 752 char *p = zidkey; 753 while (*p) { 754 if (*p == '/') { 755 *p = ':'; 756 pCity = p + 1; 757 } 758 p++; 759 } 760 761 if (fZoneStringsArray != NULL) { 762 fZoneItem = ures_getByKeyWithFallback(fZoneStringsArray, zidkey, fZoneItem, &status); 763 if (U_FAILURE(status)) { 764 // If failed to open the zone item, create only location string 765 ures_close(fZoneItem); 766 fZoneItem = NULL; 767 status = U_ZERO_ERROR; 768 } 769 } 770 771 UnicodeString region; 772 getRegion(region); 773 774 zstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(fZoneItem, gLongStandardTag); 775 zstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(fZoneItem, gShortStandardTag); 776 zstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(fZoneItem, gLongDaylightTag); 777 zstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(fZoneItem, gShortDaylightTag); 778 zstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(fZoneItem, gLongGenericTag); 779 zstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(fZoneItem, gShortGenericTag); 780 781 // Compose location format string 782 UnicodeString location; 783 UnicodeString country; 784 UnicodeString city; 785 UnicodeString countryCode; 786 ZoneMeta::getCanonicalCountry(utzid, countryCode); 787 if (!countryCode.isEmpty()) { 788 const UChar* tmpCity = getZoneStringFromBundle(fZoneItem, gExemplarCityTag); 789 if (tmpCity != NULL) { 790 city.setTo(TRUE, tmpCity, -1); 791 } else { 792 city.setTo(UnicodeString(pCity, -1, US_INV)); 793 // Replace '_' with ' ' 794 for (int32_t i = 0; i < city.length(); i++) { 795 if (city.charAt(i) == (UChar)0x5F /*'_'*/) { 796 city.setCharAt(i, (UChar)0x20 /*' '*/); 797 } 798 } 799 } 800 getLocalizedCountry(countryCode, fLocale, country); 801 UnicodeString singleCountry; 802 ZoneMeta::getSingleCountry(utzid, singleCountry); 803 FieldPosition fpos; 804 if (singleCountry.isEmpty()) { 805 Formattable params [] = { 806 Formattable(city), 807 Formattable(country) 808 }; 809 fallbackFmt->format(params, 2, location, fpos, status); 810 } else { 811 // If the zone is only one zone in the country, do not add city 812 Formattable params [] = { 813 Formattable(country) 814 }; 815 regionFmt->format(params, 1, location, fpos, status); 816 } 817 if (U_FAILURE(status)) { 818 goto error_cleanup; 819 } 820 821 zstrarray[ZSIDX_LOCATION] = location.getTerminatedBuffer(); 822 } else { 823 if (uprv_strlen(tzid) > 4 && uprv_strncmp(tzid, "Etc/", 4) == 0) { 824 // "Etc/xxx" is not associated with a specific location, so localized 825 // GMT format is always used as generic location format. 826 zstrarray[ZSIDX_LOCATION] = NULL; 827 } else { 828 // When a new time zone ID, which is actually associated with a specific 829 // location, is added in tzdata, but the current CLDR data does not have 830 // the information yet, ICU creates a generic location string based on 831 // the ID. This implementation supports canonical time zone round trip 832 // with format pattern "VVVV". See #6602 for the details. 833 UnicodeString loc(utzid); 834 int32_t slashIdx = loc.lastIndexOf((UChar)0x2f); 835 if (slashIdx == -1) { 836 // A time zone ID without slash in the tz database is not 837 // associated with a specific location. For instances, 838 // MET, CET, EET and WET fall into this category. 839 // In this case, we still use GMT format as fallback. 840 zstrarray[ZSIDX_LOCATION] = NULL; 841 } else { 842 FieldPosition fpos; 843 Formattable params[] = { 844 Formattable(loc) 845 }; 846 regionFmt->format(params, 1, location, fpos, status); 847 if (U_FAILURE(status)) { 848 goto error_cleanup; 849 } 850 zstrarray[ZSIDX_LOCATION] = location.getTerminatedBuffer(); 851 } 852 } 853 } 854 855 UBool commonlyUsed = isCommonlyUsed(fZoneItem); 856 857 // Resolve metazones used by this zone 858 int32_t mzPartialLocIdx = 0; 859 const UVector *metazoneMappings = ZoneMeta::getMetazoneMappings(utzid); 860 if (metazoneMappings != NULL) { 861 for (int32_t i = 0; i < metazoneMappings->size(); i++) { 862 const OlsonToMetaMappingEntry *mzmap = 863 (const OlsonToMetaMappingEntry*)metazoneMappings->elementAt(i); 864 UnicodeString mzid(mzmap->mzid); 865 const ZoneStrings *mzStrings = 866 (const ZoneStrings*)uhash_get(fMzidToStrings, mzid.getTerminatedBuffer()); 867 if (mzStrings == NULL) { 868 // If the metazone strings are not yet processed, do it now. 869 char mzidkey[ZID_KEY_MAX]; 870 uprv_strcpy(mzidkey, gMetazoneIdPrefix); 871 u_UCharsToChars(mzmap->mzid, mzidkey + MZID_PREFIX_LEN, u_strlen(mzmap->mzid) + 1); 872 fMetazoneItem = ures_getByKeyWithFallback(fZoneStringsArray, mzidkey, fMetazoneItem, &status); 873 if (U_FAILURE(status)) { 874 // No resources available for this metazone 875 // Resource bundle will be cleaned up after end of the loop. 876 status = U_ZERO_ERROR; 877 continue; 878 } 879 UBool mzCommonlyUsed = isCommonlyUsed(fMetazoneItem); 880 mzstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(fMetazoneItem, gLongStandardTag); 881 mzstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(fMetazoneItem, gShortStandardTag); 882 mzstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(fMetazoneItem, gLongDaylightTag); 883 mzstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(fMetazoneItem, gShortDaylightTag); 884 mzstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(fMetazoneItem, gLongGenericTag); 885 mzstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(fMetazoneItem, gShortGenericTag); 886 mzstrarray[ZSIDX_LOCATION] = NULL; 887 888 int32_t lastNonNullIdx = ZSIDX_COUNT - 1; 889 while (lastNonNullIdx >= 0) { 890 if (mzstrarray[lastNonNullIdx] != NULL) { 891 break; 892 } 893 lastNonNullIdx--; 894 } 895 UnicodeString *strings_mz = NULL; 896 ZoneStrings *tmp_mzStrings = NULL; 897 if (lastNonNullIdx >= 0) { 898 // Create UnicodeString array and put strings to the zone string trie 899 strings_mz = new UnicodeString[lastNonNullIdx + 1]; 900 901 UnicodeString preferredIdForLocale; 902 ZoneMeta::getZoneIdByMetazone(mzid, region, preferredIdForLocale); 903 904 for (int32_t typeidx = 0; typeidx <= lastNonNullIdx; typeidx++) { 905 if (mzstrarray[typeidx] != NULL) { 906 strings_mz[typeidx].setTo(TRUE, mzstrarray[typeidx], -1); 907 908 // Add a metazone string to the zone string trie 909 int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeidx); 910 ZoneStringInfo *zsinfo = new ZoneStringInfo( 911 preferredIdForLocale, 912 strings_mz[typeidx], 913 (TimeZoneTranslationType)type, 914 fStringPool, 915 status); 916 fZoneStringsTrie.put(strings_mz[typeidx], zsinfo, fStringPool, status); 917 if (U_FAILURE(status)) { 918 delete []strings_mz; 919 goto error_cleanup; 920 } 921 } 922 } 923 // Note: ZoneStrings constructor adopts and deletes the strings_mz array. 924 tmp_mzStrings = new ZoneStrings(strings_mz, lastNonNullIdx + 1, 925 mzCommonlyUsed, NULL, 0, 0, fStringPool, status); 926 } else { 927 // Create ZoneStrings with empty contents 928 tmp_mzStrings = new ZoneStrings(NULL, 0, FALSE, NULL, 0, 0, fStringPool, status); 929 } 930 931 UChar *umzid = const_cast<UChar *>(fStringPool.get(mzid, status)); 932 uhash_put(fMzidToStrings, umzid, tmp_mzStrings, &status); 933 if (U_FAILURE(status)) { 934 goto error_cleanup; 935 } 936 937 mzStrings = tmp_mzStrings; 938 } 939 940 // Compose generic partial location format 941 UnicodeString lg; 942 UnicodeString sg; 943 944 mzStrings->getString(ZSIDX_LONG_GENERIC, lg); 945 mzStrings->getString(ZSIDX_SHORT_GENERIC, sg); 946 947 if (!lg.isEmpty() || !sg.isEmpty()) { 948 UBool addMzPartialLocationNames = TRUE; 949 for (int32_t j = 0; j < mzPartialLocIdx; j++) { 950 if (mzPartialLoc[j][0] == mzid) { 951 // already processed 952 addMzPartialLocationNames = FALSE; 953 break; 954 } 955 } 956 if (addMzPartialLocationNames) { 957 UnicodeString *locationPart = NULL; 958 // Check if the zone is the preferred zone for the territory associated with the zone 959 UnicodeString preferredID; 960 ZoneMeta::getZoneIdByMetazone(mzid, countryCode, preferredID); 961 if (utzid == preferredID) { 962 // Use country for the location 963 locationPart = &country; 964 } else { 965 // Use city for the location 966 locationPart = &city; 967 } 968 // Reset the partial location string array 969 mzPartialLoc[mzPartialLocIdx][0].setTo(mzid); 970 mzPartialLoc[mzPartialLocIdx][1].remove(); 971 mzPartialLoc[mzPartialLocIdx][2].remove(); 972 mzPartialLoc[mzPartialLocIdx][3].remove(); 973 974 if (locationPart->length() != 0) { 975 FieldPosition fpos; 976 if (!lg.isEmpty()) { 977 Formattable params [] = { 978 Formattable(*locationPart), 979 Formattable(lg) 980 }; 981 fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][1], fpos, status); 982 } 983 if (!sg.isEmpty()) { 984 Formattable params [] = { 985 Formattable(*locationPart), 986 Formattable(sg) 987 }; 988 fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][2], fpos, status); 989 if (mzStrings->isShortFormatCommonlyUsed()) { 990 mzPartialLoc[mzPartialLocIdx][3].setTo(TRUE, gCommonlyUsedTrue, -1); 991 } 992 } 993 if (U_FAILURE(status)) { 994 goto error_cleanup; 995 } 996 } 997 mzPartialLocIdx++; 998 } 999 } 1000 } 1001 } 1002 // Collected names for a zone 1003 1004 // Create UnicodeString array for localized zone strings 1005 int32_t lastIdx = ZSIDX_COUNT - 1; 1006 while (lastIdx >= 0) { 1007 if (zstrarray[lastIdx] != NULL) { 1008 break; 1009 } 1010 lastIdx--; 1011 } 1012 UnicodeString *strings = NULL; 1013 int32_t stringsCount = lastIdx + 1; 1014 1015 if (stringsCount > 0) { 1016 strings = new UnicodeString[stringsCount]; 1017 for (int32_t i = 0; i < stringsCount; i++) { 1018 if (zstrarray[i] != NULL) { 1019 strings[i].setTo(zstrarray[i], -1); 1020 1021 // Add names to the trie 1022 int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)i); 1023 ZoneStringInfo *zsinfo = new ZoneStringInfo(utzid, 1024 strings[i], 1025 (TimeZoneTranslationType)type, 1026 fStringPool, 1027 status); 1028 fZoneStringsTrie.put(strings[i], zsinfo, fStringPool, status); 1029 if (U_FAILURE(status)) { 1030 delete zsinfo; 1031 delete[] strings; 1032 goto error_cleanup; 1033 } 1034 } 1035 } 1036 } 1037 1038 // Create UnicodeString array for generic partial location strings 1039 UnicodeString **genericPartialLocationNames = NULL; 1040 int32_t genericPartialRowCount = mzPartialLocIdx; 1041 int32_t genericPartialColCount = 4; 1042 1043 if (genericPartialRowCount != 0) { 1044 genericPartialLocationNames = 1045 (UnicodeString**)uprv_malloc(genericPartialRowCount * sizeof(UnicodeString*)); 1046 if (genericPartialLocationNames == NULL) { 1047 status = U_MEMORY_ALLOCATION_ERROR; 1048 delete[] strings; 1049 goto error_cleanup; 1050 } 1051 for (int32_t i = 0; i < genericPartialRowCount; i++) { 1052 genericPartialLocationNames[i] = new UnicodeString[genericPartialColCount]; 1053 for (int32_t j = 0; j < genericPartialColCount; j++) { 1054 genericPartialLocationNames[i][j].setTo(mzPartialLoc[i][j]); 1055 // Add names to the trie 1056 if ((j == 1 || j == 2) &&!genericPartialLocationNames[i][j].isEmpty()) { 1057 ZoneStringInfo *zsinfo; 1058 TimeZoneTranslationType type = (j == 1) ? GENERIC_LONG : GENERIC_SHORT; 1059 zsinfo = new ZoneStringInfo(utzid, genericPartialLocationNames[i][j], type, 1060 fStringPool, status); 1061 fZoneStringsTrie.put(genericPartialLocationNames[i][j], zsinfo, fStringPool, status); 1062 if (U_FAILURE(status)) { 1063 delete[] genericPartialLocationNames[i]; 1064 uprv_free(genericPartialLocationNames); 1065 delete[] strings; 1066 goto error_cleanup; 1067 } 1068 } 1069 } 1070 } 1071 } 1072 1073 // Finally, create ZoneStrings instance and put it into the tzidToStinrgs map 1074 ZoneStrings *zstrings = new ZoneStrings(strings, stringsCount, commonlyUsed, 1075 genericPartialLocationNames, genericPartialRowCount, 1076 genericPartialColCount, fStringPool, status); 1077 1078 UChar *uutzid = const_cast<UChar *>(fStringPool.get(utzid, status)); 1079 uhash_put(fTzidToStrings, uutzid, zstrings, &status); 1080 if (U_FAILURE(status)) { 1081 delete zstrings; 1082 goto error_cleanup; 1083 } 1084 } 1085 1086 error_cleanup: 1087 if (fallbackFmt != NULL) { 1088 delete fallbackFmt; 1089 } 1090 if (regionFmt != NULL) { 1091 delete regionFmt; 1092 } 1093 // fStringPool.freeze(); 1094 } 1095 1096 void 1097 ZoneStringFormat::loadFull(UErrorCode &status) 1098 { 1099 if (U_FAILURE(status)) { 1100 return; 1101 } 1102 if (fIsFullyLoaded) { 1103 return; 1104 } 1105 1106 umtx_lock(&ZoneStringFormatMutex); 1107 1108 if (fZoneStringsArray == NULL) { 1109 fZoneStringsArray = ures_open(U_ICUDATA_ZONE, fLocale.getName(), &status); 1110 fZoneStringsArray = ures_getByKeyWithFallback(fZoneStringsArray, gZoneStringsTag, fZoneStringsArray, &status); 1111 if (U_FAILURE(status)) { 1112 // If no locale bundles are available, zoneStrings will be null. 1113 // We still want to go through the rest of zone strings initialization, 1114 // because generic location format is generated from tzid for the case. 1115 // The rest of code should work even zoneStrings is null. 1116 status = U_ZERO_ERROR; 1117 ures_close(fZoneStringsArray); 1118 fZoneStringsArray = NULL; 1119 } 1120 } 1121 1122 StringEnumeration *tzids = NULL; 1123 1124 tzids = TimeZone::createEnumeration(); 1125 const char *tzid; 1126 while ((tzid = tzids->next(NULL, status))) { 1127 if (U_FAILURE(status)) { 1128 goto error_cleanup; 1129 } 1130 // Skip non-canonical IDs 1131 UnicodeString utzid(tzid, -1, US_INV); 1132 UnicodeString canonicalID; 1133 TimeZone::getCanonicalID(utzid, canonicalID, status); 1134 if (U_FAILURE(status)) { 1135 // Ignore unknown ID - we should not get here, but just in case. 1136 status = U_ZERO_ERROR; 1137 continue; 1138 } 1139 1140 if (U_SUCCESS(status)) { 1141 if (uhash_count(fTzidToStrings) > 0) { 1142 ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer()); 1143 if (zstrings != NULL) { 1144 continue; // We already about this one 1145 } 1146 } 1147 } 1148 1149 addSingleZone(canonicalID, status); 1150 1151 if (U_FAILURE(status)) { 1152 goto error_cleanup; 1153 } 1154 } 1155 1156 fIsFullyLoaded = TRUE; 1157 1158 error_cleanup: 1159 if (tzids != NULL) { 1160 delete tzids; 1161 } 1162 fStringPool.freeze(); 1163 1164 umtx_unlock(&ZoneStringFormatMutex); 1165 } 1166 1167 1168 ZoneStringFormat::~ZoneStringFormat() { 1169 uhash_close(fTzidToStrings); 1170 uhash_close(fMzidToStrings); 1171 ures_close(fZoneItem); 1172 ures_close(fMetazoneItem); 1173 ures_close(fZoneStringsArray); 1174 } 1175 1176 SafeZoneStringFormatPtr* 1177 ZoneStringFormat::getZoneStringFormat(const Locale& locale, UErrorCode &status) { 1178 umtx_lock(&gZSFCacheLock); 1179 if (gZoneStringFormatCache == NULL) { 1180 gZoneStringFormatCache = new ZSFCache(10 /* capacity */); 1181 ucln_i18n_registerCleanup(UCLN_I18N_ZSFORMAT, zoneStringFormat_cleanup); 1182 } 1183 umtx_unlock(&gZSFCacheLock); 1184 1185 return gZoneStringFormatCache->get(locale, status); 1186 } 1187 1188 1189 UnicodeString** 1190 ZoneStringFormat::createZoneStringsArray(UDate date, int32_t &rowCount, int32_t &colCount, UErrorCode &status) const { 1191 if (U_FAILURE(status)) { 1192 return NULL; 1193 } 1194 ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this); 1195 nonConstThis->loadFull(status); 1196 1197 UnicodeString **result = NULL; 1198 rowCount = 0; 1199 colCount = 0; 1200 1201 // Collect canonical time zone IDs 1202 UVector canonicalIDs(uhash_deleteUnicodeString, uhash_compareUnicodeString, status); 1203 if (U_FAILURE(status)) { 1204 return NULL; 1205 } 1206 StringEnumeration *tzids = TimeZone::createEnumeration(); 1207 const UChar *tzid; 1208 while ((tzid = tzids->unext(NULL, status))) { 1209 if (U_FAILURE(status)) { 1210 delete tzids; 1211 return NULL; 1212 } 1213 UnicodeString utzid(tzid); 1214 UnicodeString canonicalID; 1215 TimeZone::getCanonicalID(UnicodeString(tzid), canonicalID, status); 1216 if (U_FAILURE(status)) { 1217 // Ignore unknown ID - we should not get here, but just in case. 1218 status = U_ZERO_ERROR; 1219 continue; 1220 } 1221 if (utzid == canonicalID) { 1222 canonicalIDs.addElement(new UnicodeString(utzid), status); 1223 if (U_FAILURE(status)) { 1224 delete tzids; 1225 return NULL; 1226 } 1227 } 1228 } 1229 delete tzids; 1230 1231 // Allocate array 1232 result = (UnicodeString**)uprv_malloc(canonicalIDs.size() * sizeof(UnicodeString*)); 1233 if (result == NULL) { 1234 status = U_MEMORY_ALLOCATION_ERROR; 1235 return NULL; 1236 } 1237 for (int32_t i = 0; i < canonicalIDs.size(); i++) { 1238 result[i] = new UnicodeString[8]; 1239 UnicodeString *id = (UnicodeString*)canonicalIDs.elementAt(i); 1240 result[i][0].setTo(*id); 1241 getLongStandard(*id, date, result[i][1]); 1242 getShortStandard(*id, date, FALSE, result[i][2]); 1243 getLongDaylight(*id, date, result[i][3]); 1244 getShortDaylight(*id, date, FALSE, result[i][4]); 1245 getGenericLocation(*id, result[i][5]); 1246 getLongGenericNonLocation(*id, date, result[i][6]); 1247 getShortGenericNonLocation(*id, date, FALSE, result[i][7]); 1248 } 1249 1250 rowCount = canonicalIDs.size(); 1251 colCount = 8; 1252 return result; 1253 } 1254 1255 UnicodeString& 1256 ZoneStringFormat::getSpecificLongString(const Calendar &cal, UnicodeString &result, 1257 UErrorCode &status) const { 1258 result.remove(); 1259 if (U_FAILURE(status)) { 1260 return result; 1261 } 1262 UnicodeString tzid; 1263 cal.getTimeZone().getID(tzid); 1264 UDate date = cal.getTime(status); 1265 if (cal.get(UCAL_DST_OFFSET, status) == 0) { 1266 return getString(tzid, ZSIDX_LONG_STANDARD, date, FALSE /*not used*/, result); 1267 } else { 1268 return getString(tzid, ZSIDX_LONG_DAYLIGHT, date, FALSE /*not used*/, result); 1269 } 1270 } 1271 1272 UnicodeString& 1273 ZoneStringFormat::getSpecificShortString(const Calendar &cal, UBool commonlyUsedOnly, 1274 UnicodeString &result, UErrorCode &status) const { 1275 result.remove(); 1276 if (U_FAILURE(status)) { 1277 return result; 1278 } 1279 UnicodeString tzid; 1280 cal.getTimeZone().getID(tzid); 1281 UDate date = cal.getTime(status); 1282 if (cal.get(UCAL_DST_OFFSET, status) == 0) { 1283 return getString(tzid, ZSIDX_SHORT_STANDARD, date, commonlyUsedOnly, result); 1284 } else { 1285 return getString(tzid, ZSIDX_SHORT_DAYLIGHT, date, commonlyUsedOnly, result); 1286 } 1287 } 1288 1289 UnicodeString& 1290 ZoneStringFormat::getGenericLongString(const Calendar &cal, UnicodeString &result, 1291 UErrorCode &status) const { 1292 return getGenericString(cal, FALSE /*long*/, FALSE /* not used */, result, status); 1293 } 1294 1295 UnicodeString& 1296 ZoneStringFormat::getGenericShortString(const Calendar &cal, UBool commonlyUsedOnly, 1297 UnicodeString &result, UErrorCode &status) const { 1298 return getGenericString(cal, TRUE /*short*/, commonlyUsedOnly, result, status); 1299 } 1300 1301 UnicodeString& 1302 ZoneStringFormat::getGenericLocationString(const Calendar &cal, UnicodeString &result, 1303 UErrorCode &status) const { 1304 UnicodeString tzid; 1305 cal.getTimeZone().getID(tzid); 1306 UDate date = cal.getTime(status); 1307 return getString(tzid, ZSIDX_LOCATION, date, FALSE /*not used*/, result); 1308 } 1309 1310 const ZoneStringInfo* 1311 ZoneStringFormat::findSpecificLong(const UnicodeString &text, int32_t start, 1312 int32_t &matchLength, UErrorCode &status) const { 1313 return find(text, start, STANDARD_LONG | DAYLIGHT_LONG, matchLength, status); 1314 } 1315 1316 const ZoneStringInfo* 1317 ZoneStringFormat::findSpecificShort(const UnicodeString &text, int32_t start, 1318 int32_t &matchLength, UErrorCode &status) const { 1319 return find(text, start, STANDARD_SHORT | DAYLIGHT_SHORT, matchLength, status); 1320 } 1321 1322 const ZoneStringInfo* 1323 ZoneStringFormat::findGenericLong(const UnicodeString &text, int32_t start, 1324 int32_t &matchLength, UErrorCode &status) const { 1325 return find(text, start, GENERIC_LONG | STANDARD_LONG | LOCATION, matchLength, status); 1326 } 1327 1328 const ZoneStringInfo* 1329 ZoneStringFormat::findGenericShort(const UnicodeString &text, int32_t start, 1330 int32_t &matchLength, UErrorCode &status) const { 1331 return find(text, start, GENERIC_SHORT | STANDARD_SHORT | LOCATION, matchLength, status); 1332 } 1333 1334 const ZoneStringInfo* 1335 ZoneStringFormat::findGenericLocation(const UnicodeString &text, int32_t start, 1336 int32_t &matchLength, UErrorCode &status) const { 1337 return find(text, start, LOCATION, matchLength, status); 1338 } 1339 1340 UnicodeString& 1341 ZoneStringFormat::getString(const UnicodeString &tzid, TimeZoneTranslationTypeIndex typeIdx, UDate date, 1342 UBool commonlyUsedOnly, UnicodeString& result) const { 1343 UErrorCode status = U_ZERO_ERROR; 1344 result.remove(); 1345 if (!fIsFullyLoaded) { 1346 // Lazy loading 1347 ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this); 1348 nonConstThis->loadZone(tzid, status); 1349 } 1350 1351 // ICU's own array does not have entries for aliases 1352 UnicodeString canonicalID; 1353 TimeZone::getCanonicalID(tzid, canonicalID, status); 1354 if (U_FAILURE(status)) { 1355 // Unknown ID, but users might have their own data. 1356 canonicalID.setTo(tzid); 1357 } 1358 1359 if (uhash_count(fTzidToStrings) > 0) { 1360 ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer()); 1361 if (zstrings != NULL) { 1362 switch (typeIdx) { 1363 case ZSIDX_LONG_STANDARD: 1364 case ZSIDX_LONG_DAYLIGHT: 1365 case ZSIDX_LONG_GENERIC: 1366 case ZSIDX_LOCATION: 1367 zstrings->getString(typeIdx, result); 1368 break; 1369 case ZSIDX_SHORT_STANDARD: 1370 case ZSIDX_SHORT_DAYLIGHT: 1371 case ZSIDX_COUNT: //added to avoid warning 1372 case ZSIDX_SHORT_GENERIC: 1373 if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) { 1374 zstrings->getString(typeIdx, result); 1375 } 1376 break; 1377 default: 1378 break; 1379 } 1380 } 1381 } 1382 if (result.isEmpty() && uhash_count(fMzidToStrings) > 0 && typeIdx != ZSIDX_LOCATION) { 1383 // Try metazone 1384 UnicodeString mzid; 1385 ZoneMeta::getMetazoneID(canonicalID, date, mzid); 1386 if (!mzid.isEmpty()) { 1387 ZoneStrings *mzstrings = (ZoneStrings*)uhash_get(fMzidToStrings, mzid.getTerminatedBuffer()); 1388 if (mzstrings != NULL) { 1389 switch (typeIdx) { 1390 case ZSIDX_LONG_STANDARD: 1391 case ZSIDX_LONG_DAYLIGHT: 1392 case ZSIDX_LONG_GENERIC: 1393 case ZSIDX_LOCATION: 1394 mzstrings->getString(typeIdx, result); 1395 break; 1396 case ZSIDX_SHORT_STANDARD: 1397 case ZSIDX_SHORT_DAYLIGHT: 1398 case ZSIDX_COUNT: //added to avoid warning 1399 case ZSIDX_SHORT_GENERIC: 1400 if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) { 1401 mzstrings->getString(typeIdx, result); 1402 } 1403 break; 1404 default: 1405 break; 1406 } 1407 } 1408 } 1409 } 1410 return result; 1411 } 1412 1413 UnicodeString& 1414 ZoneStringFormat::getGenericString(const Calendar &cal, UBool isShort, UBool commonlyUsedOnly, 1415 UnicodeString &result, UErrorCode &status) const { 1416 result.remove(); 1417 UDate time = cal.getTime(status); 1418 if (U_FAILURE(status)) { 1419 return result; 1420 } 1421 const TimeZone &tz = cal.getTimeZone(); 1422 UnicodeString tzid; 1423 tz.getID(tzid); 1424 1425 if (!fIsFullyLoaded) { 1426 // Lazy loading 1427 ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this); 1428 nonConstThis->loadZone(tzid, status); 1429 } 1430 1431 // ICU's own array does not have entries for aliases 1432 UnicodeString canonicalID; 1433 TimeZone::getCanonicalID(tzid, canonicalID, status); 1434 if (U_FAILURE(status)) { 1435 // Unknown ID, but users might have their own data. 1436 status = U_ZERO_ERROR; 1437 canonicalID.setTo(tzid); 1438 } 1439 1440 ZoneStrings *zstrings = NULL; 1441 if (uhash_count(fTzidToStrings) > 0) { 1442 zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer()); 1443 if (zstrings != NULL) { 1444 if (isShort) { 1445 if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) { 1446 zstrings->getString(ZSIDX_SHORT_GENERIC, result); 1447 } 1448 } else { 1449 zstrings->getString(ZSIDX_LONG_GENERIC, result); 1450 } 1451 } 1452 } 1453 if (result.isEmpty() && uhash_count(fMzidToStrings) > 0) { 1454 // try metazone 1455 int32_t raw, sav; 1456 UnicodeString mzid; 1457 ZoneMeta::getMetazoneID(canonicalID, time, mzid); 1458 if (!mzid.isEmpty()) { 1459 UBool useStandard = FALSE; 1460 sav = cal.get(UCAL_DST_OFFSET, status); 1461 if (U_FAILURE(status)) { 1462 return result; 1463 } 1464 if (sav == 0) { 1465 useStandard = TRUE; 1466 // Check if the zone actually uses daylight saving time around the time 1467 TimeZone *tmptz = tz.clone(); 1468 BasicTimeZone *btz = NULL; 1469 if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL 1470 || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL 1471 || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL 1472 || dynamic_cast<VTimeZone *>(tmptz) != NULL) { 1473 btz = (BasicTimeZone*)tmptz; 1474 } 1475 1476 if (btz != NULL) { 1477 TimeZoneTransition before; 1478 UBool beforTrs = btz->getPreviousTransition(time, TRUE, before); 1479 if (beforTrs 1480 && (time - before.getTime() < kDstCheckRange) 1481 && before.getFrom()->getDSTSavings() != 0) { 1482 useStandard = FALSE; 1483 } else { 1484 TimeZoneTransition after; 1485 UBool afterTrs = btz->getNextTransition(time, FALSE, after); 1486 if (afterTrs 1487 && (after.getTime() - time < kDstCheckRange) 1488 && after.getTo()->getDSTSavings() != 0) { 1489 useStandard = FALSE; 1490 } 1491 } 1492 } else { 1493 // If not BasicTimeZone... only if the instance is not an ICU's implementation. 1494 // We may get a wrong answer in edge case, but it should practically work OK. 1495 tmptz->getOffset(time - kDstCheckRange, FALSE, raw, sav, status); 1496 if (sav != 0) { 1497 useStandard = FALSE; 1498 } else { 1499 tmptz->getOffset(time + kDstCheckRange, FALSE, raw, sav, status); 1500 if (sav != 0){ 1501 useStandard = FALSE; 1502 } 1503 } 1504 if (U_FAILURE(status)) { 1505 delete tmptz; 1506 result.remove(); 1507 return result; 1508 } 1509 } 1510 delete tmptz; 1511 } 1512 if (useStandard) { 1513 getString(canonicalID, (isShort ? ZSIDX_SHORT_STANDARD : ZSIDX_LONG_STANDARD), 1514 time, commonlyUsedOnly, result); 1515 1516 // Note: 1517 // In CLDR 1.5.1, a same localization is used for both generic and standard 1518 // for some metazones in some locales. This is actually data bugs and should 1519 // be resolved in later versions of CLDR. For now, we check if the standard 1520 // name is different from its generic name below. 1521 if (!result.isEmpty()) { 1522 UnicodeString genericNonLocation; 1523 getString(canonicalID, (isShort ? ZSIDX_SHORT_GENERIC : ZSIDX_LONG_GENERIC), 1524 time, commonlyUsedOnly, genericNonLocation); 1525 if (!genericNonLocation.isEmpty() && result == genericNonLocation) { 1526 result.remove(); 1527 } 1528 } 1529 } 1530 if (result.isEmpty()) { 1531 ZoneStrings *mzstrings = (ZoneStrings*)uhash_get(fMzidToStrings, mzid.getTerminatedBuffer()); 1532 if (mzstrings != NULL) { 1533 if (isShort) { 1534 if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) { 1535 mzstrings->getString(ZSIDX_SHORT_GENERIC, result); 1536 } 1537 } else { 1538 mzstrings->getString(ZSIDX_LONG_GENERIC, result); 1539 } 1540 } 1541 if (!result.isEmpty()) { 1542 // Check if the offsets at the given time matches the preferred zone's offsets 1543 UnicodeString preferredId; 1544 UnicodeString region; 1545 ZoneMeta::getZoneIdByMetazone(mzid, getRegion(region), preferredId); 1546 if (canonicalID != preferredId) { 1547 // Check if the offsets at the given time are identical with the preferred zone 1548 raw = cal.get(UCAL_ZONE_OFFSET, status); 1549 if (U_FAILURE(status)) { 1550 result.remove(); 1551 return result; 1552 } 1553 TimeZone *preferredZone = TimeZone::createTimeZone(preferredId); 1554 int32_t prfRaw, prfSav; 1555 // Check offset in preferred time zone with wall time. 1556 // With getOffset(time, false, preferredOffsets), 1557 // you may get incorrect results because of time overlap at DST->STD 1558 // transition. 1559 preferredZone->getOffset(time + raw + sav, TRUE, prfRaw, prfSav, status); 1560 delete preferredZone; 1561 1562 if (U_FAILURE(status)) { 1563 result.remove(); 1564 return result; 1565 } 1566 if ((raw != prfRaw || sav != prfSav) && zstrings != NULL) { 1567 // Use generic partial location string as fallback 1568 zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result); 1569 } 1570 } 1571 } 1572 } 1573 } 1574 } 1575 if (result.isEmpty()) { 1576 // Use location format as the final fallback 1577 getString(canonicalID, ZSIDX_LOCATION, time, FALSE /*not used*/, result); 1578 } 1579 1580 return result; 1581 } 1582 1583 UnicodeString& 1584 ZoneStringFormat::getGenericPartialLocationString(const UnicodeString &tzid, UBool isShort, 1585 UDate date, UBool commonlyUsedOnly, UnicodeString &result) const { 1586 UErrorCode status = U_ZERO_ERROR; 1587 result.remove(); 1588 if (!fIsFullyLoaded) { 1589 // Lazy loading 1590 ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this); 1591 nonConstThis->loadZone(tzid, status); 1592 } 1593 1594 if (uhash_count(fTzidToStrings) <= 0) { 1595 return result; 1596 } 1597 1598 UnicodeString canonicalID; 1599 TimeZone::getCanonicalID(tzid, canonicalID, status); 1600 if (U_FAILURE(status)) { 1601 // Unknown ID, so no corresponding meta data. 1602 return result; 1603 } 1604 1605 UnicodeString mzid; 1606 ZoneMeta::getMetazoneID(canonicalID, date, mzid); 1607 1608 if (!mzid.isEmpty()) { 1609 ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer()); 1610 if (zstrings != NULL) { 1611 zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result); 1612 } 1613 } 1614 return result; 1615 } 1616 1617 // This method does lazy zone string loading 1618 const ZoneStringInfo* 1619 ZoneStringFormat::find(const UnicodeString &text, int32_t start, int32_t types, 1620 int32_t &matchLength, UErrorCode &status) const { 1621 1622 if (U_FAILURE(status)) { 1623 return NULL; 1624 } 1625 1626 const ZoneStringInfo * result = subFind(text, start, types, matchLength, status); 1627 if (fIsFullyLoaded) { 1628 return result; 1629 } 1630 // When zone string data is partially loaded, 1631 // this method return the result only when 1632 // the input text is fully consumed. 1633 if (result != NULL) { 1634 UnicodeString tmpString; 1635 matchLength = (result->getString(tmpString)).length(); 1636 if (text.length() - start == matchLength) { 1637 return result; 1638 } 1639 } 1640 1641 // Now load all zone strings 1642 ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this); 1643 nonConstThis->loadFull(status); 1644 1645 return subFind(text, start, types, matchLength, status); 1646 } 1647 1648 1649 /* 1650 * Find a prefix matching time zone for the given zone string types. 1651 * @param text The text contains a time zone string 1652 * @param start The start index within the text 1653 * @param types The bit mask representing a set of requested types 1654 * @return If any zone string matched for the requested types, returns a 1655 * ZoneStringInfo for the longest match. If no matches are found for 1656 * the requested types, returns a ZoneStringInfo for the longest match 1657 * for any other types. If nothing matches at all, returns null. 1658 */ 1659 const ZoneStringInfo* 1660 ZoneStringFormat::subFind(const UnicodeString &text, int32_t start, int32_t types, 1661 int32_t &matchLength, UErrorCode &status) const { 1662 matchLength = 0; 1663 if (U_FAILURE(status)) { 1664 return NULL; 1665 } 1666 if (fZoneStringsTrie.isEmpty()) { 1667 return NULL; 1668 } 1669 1670 const ZoneStringInfo *result = NULL; 1671 const ZoneStringInfo *fallback = NULL; 1672 int32_t fallbackMatchLen = 0; 1673 1674 ZoneStringSearchResultHandler handler(status); 1675 fZoneStringsTrie.search(text, start, (TextTrieMapSearchResultHandler*)&handler, status); 1676 if (U_SUCCESS(status)) { 1677 int32_t numMatches = handler.countMatches(); 1678 for (int32_t i = 0; i < numMatches; i++) { 1679 int32_t tmpMatchLen = 0; // init. output only param to silence gcc 1680 const ZoneStringInfo *tmp = handler.getMatch(i, tmpMatchLen); 1681 if ((types & tmp->fType) != 0) { 1682 if (result == NULL || matchLength < tmpMatchLen) { 1683 result = tmp; 1684 matchLength = tmpMatchLen; 1685 } else if (matchLength == tmpMatchLen) { 1686 // Tie breaker - there are some examples that a 1687 // long standard name is identical with a location 1688 // name - for example, "Uruguay Time". In this case, 1689 // we interpret it as generic, not specific. 1690 if (tmp->isGeneric() && !result->isGeneric()) { 1691 result = tmp; 1692 } 1693 } 1694 } else if (result == NULL) { 1695 if (fallback == NULL || fallbackMatchLen < tmpMatchLen) { 1696 fallback = tmp; 1697 fallbackMatchLen = tmpMatchLen; 1698 } else if (fallbackMatchLen == tmpMatchLen) { 1699 if (tmp->isGeneric() && !fallback->isGeneric()) { 1700 fallback = tmp; 1701 } 1702 } 1703 } 1704 } 1705 if (result == NULL && fallback != NULL) { 1706 result = fallback; 1707 matchLength = fallbackMatchLen; 1708 } 1709 } 1710 return result; 1711 } 1712 1713 1714 UnicodeString& 1715 ZoneStringFormat::getRegion(UnicodeString ®ion) const { 1716 const char* country = fLocale.getCountry(); 1717 // TODO: Utilize addLikelySubtag in Locale to resolve default region 1718 // when the implementation is ready. 1719 region.setTo(UnicodeString(country, -1, US_INV)); 1720 return region; 1721 } 1722 1723 MessageFormat* 1724 ZoneStringFormat::getFallbackFormat(const Locale &locale, UErrorCode &status) { 1725 if (U_FAILURE(status)) { 1726 return NULL; 1727 } 1728 UnicodeString pattern(TRUE, gDefFallbackPattern, -1); 1729 UResourceBundle *zoneStringsArray = ures_open(U_ICUDATA_ZONE, locale.getName(), &status); 1730 zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status); 1731 int32_t len; 1732 const UChar *flbkfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gFallbackFormatTag, &len, &status); 1733 if (U_SUCCESS(status)) { 1734 pattern.setTo(flbkfmt); 1735 } else { 1736 status = U_ZERO_ERROR; 1737 } 1738 ures_close(zoneStringsArray); 1739 1740 MessageFormat *fallbackFmt = new MessageFormat(pattern, status); 1741 return fallbackFmt; 1742 } 1743 1744 MessageFormat* 1745 ZoneStringFormat::getRegionFormat(const Locale& locale, UErrorCode &status) { 1746 if (U_FAILURE(status)) { 1747 return NULL; 1748 } 1749 UnicodeString pattern(TRUE, gDefRegionPattern, -1); 1750 UResourceBundle *zoneStringsArray = ures_open(U_ICUDATA_ZONE, locale.getName(), &status); 1751 zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status); 1752 int32_t len; 1753 const UChar *regionfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gRegionFormatTag, &len, &status); 1754 if (U_SUCCESS(status)) { 1755 pattern.setTo(regionfmt); 1756 } else { 1757 status = U_ZERO_ERROR; 1758 } 1759 ures_close(zoneStringsArray); 1760 1761 MessageFormat *regionFmt = new MessageFormat(pattern, status); 1762 return regionFmt; 1763 } 1764 1765 const UChar* 1766 ZoneStringFormat::getZoneStringFromBundle(const UResourceBundle *zoneitem, const char *key) { 1767 const UChar *str = NULL; 1768 if (zoneitem != NULL) { 1769 UErrorCode status = U_ZERO_ERROR; 1770 int32_t len; 1771 str = ures_getStringByKeyWithFallback(zoneitem, key, &len, &status); 1772 str = fStringPool.adopt(str, status); 1773 if (U_FAILURE(status)) { 1774 str = NULL; 1775 } 1776 } 1777 return str; 1778 } 1779 1780 UBool 1781 ZoneStringFormat::isCommonlyUsed(const UResourceBundle *zoneitem) { 1782 if (zoneitem == NULL) { 1783 return TRUE; 1784 } 1785 1786 UBool commonlyUsed = FALSE; 1787 UErrorCode status = U_ZERO_ERROR; 1788 UResourceBundle *cuRes = ures_getByKey(zoneitem, gCommonlyUsedTag, NULL, &status); 1789 int32_t cuValue = ures_getInt(cuRes, &status); 1790 if (U_SUCCESS(status)) { 1791 if (cuValue == 1) { 1792 commonlyUsed = TRUE; 1793 } 1794 } 1795 ures_close(cuRes); 1796 return commonlyUsed; 1797 } 1798 1799 UnicodeString& 1800 ZoneStringFormat::getLocalizedCountry(const UnicodeString &countryCode, const Locale &locale, UnicodeString &displayCountry) { 1801 // We do not want to use display country names only from the target language bundle 1802 // Note: we should do this in better way. 1803 displayCountry.remove(); 1804 int32_t ccLen = countryCode.length(); 1805 if (ccLen > 0 && ccLen < ULOC_COUNTRY_CAPACITY) { 1806 UErrorCode status = U_ZERO_ERROR; 1807 UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status); 1808 if (U_SUCCESS(status)) { 1809 const char *bundleLocStr = ures_getLocale(localeBundle, &status); 1810 if (U_SUCCESS(status) && uprv_strlen(bundleLocStr) > 0) { 1811 Locale bundleLoc(bundleLocStr); 1812 if (uprv_strcmp(bundleLocStr, "root") != 0 && 1813 uprv_strcmp(bundleLoc.getLanguage(), locale.getLanguage()) == 0) { 1814 // Create a fake locale strings 1815 char tmpLocStr[ULOC_COUNTRY_CAPACITY + 3]; 1816 uprv_strcpy(tmpLocStr, "xx_"); 1817 u_UCharsToChars(countryCode.getBuffer(), &tmpLocStr[3], ccLen); 1818 tmpLocStr[3 + ccLen] = 0; 1819 1820 Locale tmpLoc(tmpLocStr); 1821 tmpLoc.getDisplayCountry(locale, displayCountry); 1822 } 1823 } 1824 } 1825 ures_close(localeBundle); 1826 } 1827 if (displayCountry.isEmpty()) { 1828 // Use the country code as the fallback 1829 displayCountry.setTo(countryCode); 1830 } 1831 return displayCountry; 1832 } 1833 1834 // ---------------------------------------------------------------------------- 1835 /* 1836 * ZoneStrings constructor adopts (and promptly copies and deletes) 1837 * the input UnicodeString arrays. 1838 */ 1839 ZoneStrings::ZoneStrings(UnicodeString *strings, 1840 int32_t stringsCount, 1841 UBool commonlyUsed, 1842 UnicodeString **genericPartialLocationStrings, 1843 int32_t genericRowCount, 1844 int32_t genericColCount, 1845 ZSFStringPool &sp, 1846 UErrorCode &status) 1847 : fStrings(NULL), 1848 fStringsCount(stringsCount), 1849 fIsCommonlyUsed(commonlyUsed), 1850 fGenericPartialLocationStrings(NULL), 1851 fGenericPartialLocationRowCount(genericRowCount), 1852 fGenericPartialLocationColCount(genericColCount) 1853 { 1854 if (U_FAILURE(status)) { 1855 return; 1856 } 1857 int32_t i, j; 1858 if (strings != NULL) { 1859 fStrings = (const UChar **)uprv_malloc(sizeof(const UChar **) * stringsCount); 1860 if (fStrings == NULL) { 1861 status = U_MEMORY_ALLOCATION_ERROR; 1862 return; 1863 } 1864 for (i=0; i<fStringsCount; i++) { 1865 fStrings[i] = sp.get(strings[i], status); 1866 } 1867 delete[] strings; 1868 } 1869 if (genericPartialLocationStrings != NULL) { 1870 fGenericPartialLocationStrings = 1871 (const UChar ***)uprv_malloc(sizeof(const UChar ***) * genericRowCount); 1872 if (fGenericPartialLocationStrings == NULL) { 1873 status = U_MEMORY_ALLOCATION_ERROR; 1874 return; 1875 } 1876 for (i=0; i < fGenericPartialLocationRowCount; i++) { 1877 fGenericPartialLocationStrings[i] = 1878 (const UChar **)uprv_malloc(sizeof(const UChar **) * genericColCount); 1879 if (fGenericPartialLocationStrings[i] == NULL) { 1880 status = U_MEMORY_ALLOCATION_ERROR; 1881 continue; // Continue so that fGenericPartialLocationStrings will not contain uninitialized junk, 1882 } // which would crash the destructor. 1883 for (j=0; j<genericColCount; j++) { 1884 fGenericPartialLocationStrings[i][j] = 1885 sp.get(genericPartialLocationStrings[i][j], status); 1886 } 1887 delete[] genericPartialLocationStrings[i]; 1888 } 1889 uprv_free(genericPartialLocationStrings); 1890 } 1891 } 1892 1893 ZoneStrings::~ZoneStrings() { 1894 uprv_free(fStrings); 1895 if (fGenericPartialLocationStrings != NULL) { 1896 for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) { 1897 uprv_free(fGenericPartialLocationStrings[i]); 1898 } 1899 uprv_free(fGenericPartialLocationStrings); 1900 } 1901 } 1902 1903 1904 UnicodeString& 1905 ZoneStrings::getString(int32_t typeIdx, UnicodeString &result) const { 1906 if (typeIdx >= 0 && typeIdx < fStringsCount) { 1907 result.setTo(fStrings[typeIdx], -1); 1908 } else { 1909 result.remove(); 1910 } 1911 return result; 1912 } 1913 1914 UnicodeString& 1915 ZoneStrings::getGenericPartialLocationString(const UnicodeString &mzid, UBool isShort, 1916 UBool commonlyUsedOnly, UnicodeString &result) const { 1917 UBool isSet = FALSE; 1918 if (fGenericPartialLocationColCount >= 2) { 1919 for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) { 1920 if (mzid.compare(fGenericPartialLocationStrings[i][0], -1) == 0) { 1921 if (isShort) { 1922 if (fGenericPartialLocationColCount >= 3) { 1923 if (!commonlyUsedOnly || 1924 fGenericPartialLocationColCount == 3 || 1925 fGenericPartialLocationStrings[i][3][0] != 0) { 1926 result.setTo(fGenericPartialLocationStrings[i][2], -1); 1927 isSet = TRUE; 1928 } 1929 } 1930 } else { 1931 result.setTo(fGenericPartialLocationStrings[i][1], -1); 1932 isSet = TRUE; 1933 } 1934 break; 1935 } 1936 } 1937 } 1938 if (!isSet) { 1939 result.remove(); 1940 } 1941 return result; 1942 } 1943 1944 // -------------------------------------------------------------- 1945 SafeZoneStringFormatPtr::SafeZoneStringFormatPtr(ZSFCacheEntry *cacheEntry) 1946 : fCacheEntry(cacheEntry) { 1947 } 1948 1949 SafeZoneStringFormatPtr::~SafeZoneStringFormatPtr() { 1950 fCacheEntry->delRef(); 1951 } 1952 1953 const ZoneStringFormat* 1954 SafeZoneStringFormatPtr::get() const { 1955 return fCacheEntry->getZoneStringFormat(); 1956 } 1957 1958 ZSFCacheEntry::ZSFCacheEntry(const Locale &locale, ZoneStringFormat *zsf, ZSFCacheEntry *next) 1959 : fLocale(locale), fZoneStringFormat(zsf), 1960 fNext(next), fRefCount(1) 1961 { 1962 } 1963 1964 ZSFCacheEntry::~ZSFCacheEntry () { 1965 delete fZoneStringFormat; 1966 } 1967 1968 const ZoneStringFormat* 1969 ZSFCacheEntry::getZoneStringFormat(void) { 1970 return (const ZoneStringFormat*)fZoneStringFormat; 1971 } 1972 1973 void 1974 ZSFCacheEntry::delRef(void) { 1975 umtx_lock(&gZSFCacheLock); 1976 --fRefCount; 1977 umtx_unlock(&gZSFCacheLock); 1978 } 1979 1980 ZSFCache::ZSFCache(int32_t capacity) 1981 : fCapacity(capacity), fFirst(NULL) { 1982 } 1983 1984 ZSFCache::~ZSFCache() { 1985 ZSFCacheEntry *entry = fFirst; 1986 while (entry) { 1987 ZSFCacheEntry *next = entry->fNext; 1988 delete entry; 1989 entry = next; 1990 } 1991 } 1992 1993 SafeZoneStringFormatPtr* 1994 ZSFCache::get(const Locale &locale, UErrorCode &status) { 1995 SafeZoneStringFormatPtr *result = NULL; 1996 1997 // Search the cache entry list 1998 ZSFCacheEntry *entry = NULL; 1999 ZSFCacheEntry *next, *prev; 2000 2001 umtx_lock(&gZSFCacheLock); 2002 entry = fFirst; 2003 prev = NULL; 2004 while (entry) { 2005 next = entry->fNext; 2006 if (entry->fLocale == locale) { 2007 // Add reference count 2008 entry->fRefCount++; 2009 2010 // move the entry to the top 2011 if (entry != fFirst) { 2012 prev->fNext = next; 2013 entry->fNext = fFirst; 2014 fFirst = entry; 2015 } 2016 break; 2017 } 2018 prev = entry; 2019 entry = next; 2020 } 2021 umtx_unlock(&gZSFCacheLock); 2022 2023 // Create a new ZoneStringFormat 2024 if (entry == NULL) { 2025 ZoneStringFormat *zsf = new ZoneStringFormat(locale, status); 2026 if (U_FAILURE(status)) { 2027 delete zsf; 2028 return NULL; 2029 } 2030 if (zsf == NULL) { 2031 status = U_MEMORY_ALLOCATION_ERROR; 2032 return NULL; 2033 } 2034 2035 // Now add the new entry 2036 umtx_lock(&gZSFCacheLock); 2037 // Make sure no other threads already created the one for the same locale 2038 entry = fFirst; 2039 prev = NULL; 2040 while (entry) { 2041 next = entry->fNext; 2042 if (entry->fLocale == locale) { 2043 // Add reference count 2044 entry->fRefCount++; 2045 2046 // move the entry to the top 2047 if (entry != fFirst) { 2048 prev->fNext = next; 2049 entry->fNext = fFirst; 2050 fFirst = entry; 2051 } 2052 break; 2053 } 2054 prev = entry; 2055 entry = next; 2056 } 2057 if (entry == NULL) { 2058 // Add the new one to the top 2059 next = fFirst; 2060 entry = new ZSFCacheEntry(locale, zsf, next); 2061 fFirst = entry; 2062 } else { 2063 delete zsf; 2064 } 2065 umtx_unlock(&gZSFCacheLock); 2066 } 2067 2068 result = new SafeZoneStringFormatPtr(entry); 2069 2070 // Now, delete unused cache entries beyond the capacity 2071 umtx_lock(&gZSFCacheLock); 2072 entry = fFirst; 2073 prev = NULL; 2074 int32_t idx = 1; 2075 while (entry) { 2076 next = entry->fNext; 2077 if (idx >= fCapacity && entry->fRefCount == 0) { 2078 if (entry == fFirst) { 2079 fFirst = next; 2080 } else { 2081 prev->fNext = next; 2082 } 2083 delete entry; 2084 } else { 2085 prev = entry; 2086 } 2087 entry = next; 2088 idx++; 2089 } 2090 umtx_unlock(&gZSFCacheLock); 2091 2092 return result; 2093 } 2094 2095 2096 /* 2097 * Zone String Formatter String Pool Implementation 2098 * 2099 * String pool for (UChar *) strings. Avoids having repeated copies of the same string. 2100 */ 2101 2102 static const int32_t POOL_CHUNK_SIZE = 2000; 2103 struct ZSFStringPoolChunk: public UMemory { 2104 ZSFStringPoolChunk *fNext; // Ptr to next pool chunk 2105 int32_t fLimit; // Index to start of unused area at end of fStrings 2106 UChar fStrings[POOL_CHUNK_SIZE]; // Strings array 2107 ZSFStringPoolChunk(); 2108 }; 2109 2110 ZSFStringPoolChunk::ZSFStringPoolChunk() { 2111 fNext = NULL; 2112 fLimit = 0; 2113 } 2114 2115 ZSFStringPool::ZSFStringPool(UErrorCode &status) { 2116 fChunks = NULL; 2117 fHash = NULL; 2118 if (U_FAILURE(status)) { 2119 return; 2120 } 2121 fChunks = new ZSFStringPoolChunk; 2122 if (fChunks == NULL) { 2123 status = U_MEMORY_ALLOCATION_ERROR; 2124 return; 2125 } 2126 2127 fHash = uhash_open(uhash_hashUChars /* keyHash */, 2128 uhash_compareUChars /* keyComp */, 2129 uhash_compareUChars /* valueComp */, 2130 &status); 2131 if (U_FAILURE(status)) { 2132 return; 2133 } 2134 } 2135 2136 2137 ZSFStringPool::~ZSFStringPool() { 2138 if (fHash != NULL) { 2139 uhash_close(fHash); 2140 fHash = NULL; 2141 } 2142 2143 while (fChunks != NULL) { 2144 ZSFStringPoolChunk *nextChunk = fChunks->fNext; 2145 delete fChunks; 2146 fChunks = nextChunk; 2147 } 2148 } 2149 2150 static const UChar EmptyString = 0; 2151 2152 const UChar *ZSFStringPool::get(const UChar *s, UErrorCode &status) { 2153 const UChar *pooledString; 2154 if (U_FAILURE(status)) { 2155 return &EmptyString; 2156 } 2157 2158 pooledString = static_cast<UChar *>(uhash_get(fHash, s)); 2159 if (pooledString != NULL) { 2160 return pooledString; 2161 } 2162 2163 int32_t length = u_strlen(s); 2164 int32_t remainingLength = POOL_CHUNK_SIZE - fChunks->fLimit; 2165 if (remainingLength <= length) { 2166 U_ASSERT(length < POOL_CHUNK_SIZE); 2167 if (length >= POOL_CHUNK_SIZE) { 2168 status = U_INTERNAL_PROGRAM_ERROR; 2169 return &EmptyString; 2170 } 2171 ZSFStringPoolChunk *oldChunk = fChunks; 2172 fChunks = new ZSFStringPoolChunk; 2173 if (fChunks == NULL) { 2174 status = U_MEMORY_ALLOCATION_ERROR; 2175 return &EmptyString; 2176 } 2177 fChunks->fNext = oldChunk; 2178 } 2179 2180 UChar *destString = &fChunks->fStrings[fChunks->fLimit]; 2181 u_strcpy(destString, s); 2182 fChunks->fLimit += (length + 1); 2183 uhash_put(fHash, destString, destString, &status); 2184 return destString; 2185 } 2186 2187 2188 // 2189 // ZSFStringPool::adopt() Put a string into the hash, but do not copy the string data 2190 // into the pool's storage. Used for strings from resource bundles, 2191 // which will perisist for the life of the zone string formatter, and 2192 // therefore can be used directly without copying. 2193 const UChar *ZSFStringPool::adopt(const UChar * s, UErrorCode &status) { 2194 const UChar *pooledString; 2195 if (U_FAILURE(status)) { 2196 return &EmptyString; 2197 } 2198 if (s != NULL) { 2199 pooledString = static_cast<UChar *>(uhash_get(fHash, s)); 2200 if (pooledString == NULL) { 2201 UChar *ncs = const_cast<UChar *>(s); 2202 uhash_put(fHash, ncs, ncs, &status); 2203 } 2204 } 2205 return s; 2206 } 2207 2208 2209 const UChar *ZSFStringPool::get(const UnicodeString &s, UErrorCode &status) { 2210 UnicodeString &nonConstStr = const_cast<UnicodeString &>(s); 2211 return this->get(nonConstStr.getTerminatedBuffer(), status); 2212 } 2213 2214 /* 2215 * freeze(). Close the hash table that maps to the pooled strings. 2216 * After freezing, the pool can not be searched or added to, 2217 * but all existing references to pooled strings remain valid. 2218 * 2219 * The main purpose is to recover the storage used for the hash. 2220 */ 2221 void ZSFStringPool::freeze() { 2222 uhash_close(fHash); 2223 fHash = NULL; 2224 } 2225 2226 U_NAMESPACE_END 2227 2228 #endif /* #if !UCONFIG_NO_FORMATTING */ 2229