1/* 2******************************************************************************* 3* Copyright (C) 2007-2009, 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 31/** 32 * global ZoneStringFormatCache stuffs 33 */ 34static UMTX gZSFCacheLock = NULL; 35static U_NAMESPACE_QUALIFIER ZSFCache *gZoneStringFormatCache = NULL; 36 37U_CDECL_BEGIN 38/** 39 * ZoneStringFormatCache cleanup callback func 40 */ 41static UBool U_CALLCONV zoneStringFormat_cleanup(void) 42{ 43 umtx_destroy(&gZSFCacheLock); 44 if (gZoneStringFormatCache != NULL) { 45 delete gZoneStringFormatCache; 46 gZoneStringFormatCache = NULL; 47 } 48 gZoneStringFormatCache = NULL; 49 return TRUE; 50} 51 52/** 53 * Deleter for ZoneStringInfo 54 */ 55static void U_CALLCONV 56deleteZoneStringInfo(void *obj) { 57 delete (U_NAMESPACE_QUALIFIER ZoneStringInfo*)obj; 58} 59 60/** 61 * Deleter for ZoneStrings 62 */ 63static void U_CALLCONV 64deleteZoneStrings(void *obj) { 65 delete (U_NAMESPACE_QUALIFIER ZoneStrings*)obj; 66} 67U_CDECL_END 68 69U_NAMESPACE_BEGIN 70 71#define ZID_KEY_MAX 128 72 73static const char gCountriesTag[] = "Countries"; 74static const char gZoneStringsTag[] = "zoneStrings"; 75static const char gShortGenericTag[] = "sg"; 76static const char gShortStandardTag[] = "ss"; 77static const char gShortDaylightTag[] = "sd"; 78static const char gLongGenericTag[] = "lg"; 79static const char gLongStandardTag[] = "ls"; 80static const char gLongDaylightTag[] = "ld"; 81static const char gExemplarCityTag[] = "ec"; 82static const char gCommonlyUsedTag[] = "cu"; 83static const char gFallbackFormatTag[] = "fallbackFormat"; 84static const char gRegionFormatTag[] = "regionFormat"; 85 86#define MZID_PREFIX_LEN 5 87static const char gMetazoneIdPrefix[] = "meta:"; 88 89#define MAX_METAZONES_PER_ZONE 10 90 91static const UChar gDefFallbackPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})" 92static const UChar gDefRegionPattern[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}" 93static const UChar gCommonlyUsedTrue[] = {0x31, 0x00}; // "1" 94 95static const double kDstCheckRange = (double)184*U_MILLIS_PER_DAY; 96 97static int32_t 98getTimeZoneTranslationTypeIndex(TimeZoneTranslationType type) { 99 int32_t typeIdx = 0; 100 switch (type) { 101 case LOCATION: 102 typeIdx = ZSIDX_LOCATION; 103 break; 104 case GENERIC_LONG: 105 typeIdx = ZSIDX_LONG_GENERIC; 106 break; 107 case GENERIC_SHORT: 108 typeIdx = ZSIDX_SHORT_GENERIC; 109 break; 110 case STANDARD_LONG: 111 typeIdx = ZSIDX_LONG_STANDARD; 112 break; 113 case STANDARD_SHORT: 114 typeIdx = ZSIDX_SHORT_STANDARD; 115 break; 116 case DAYLIGHT_LONG: 117 typeIdx = ZSIDX_LONG_DAYLIGHT; 118 break; 119 case DAYLIGHT_SHORT: 120 typeIdx = ZSIDX_SHORT_DAYLIGHT; 121 break; 122 } 123 return typeIdx; 124} 125 126static int32_t 127getTimeZoneTranslationType(TimeZoneTranslationTypeIndex typeIdx) { 128 int32_t type = 0; 129 switch (typeIdx) { 130 case ZSIDX_LOCATION: 131 type = LOCATION; 132 break; 133 case ZSIDX_LONG_GENERIC: 134 type = GENERIC_LONG; 135 break; 136 case ZSIDX_SHORT_GENERIC: 137 type = GENERIC_SHORT; 138 break; 139 case ZSIDX_LONG_STANDARD: 140 type = STANDARD_LONG; 141 break; 142 case ZSIDX_SHORT_STANDARD: 143 type = STANDARD_SHORT; 144 break; 145 case ZSIDX_LONG_DAYLIGHT: 146 type = DAYLIGHT_LONG; 147 break; 148 case ZSIDX_COUNT: 149 case ZSIDX_SHORT_DAYLIGHT: 150 type = DAYLIGHT_SHORT; 151 break; 152 default: 153 break; 154 } 155 return type; 156} 157 158#define DEFAULT_CHARACTERNODE_CAPACITY 1 159 160// ---------------------------------------------------------------------------- 161void CharacterNode::clear() { 162 uprv_memset(this, 0, sizeof(*this)); 163} 164 165void CharacterNode::deleteValues() { 166 if (fValues == NULL) { 167 // Do nothing. 168 } else if (!fHasValuesVector) { 169 deleteZoneStringInfo(fValues); 170 } else { 171 delete (UVector *)fValues; 172 } 173} 174 175void 176CharacterNode::addValue(void *value, UErrorCode &status) { 177 if (U_FAILURE(status)) { 178 deleteZoneStringInfo(value); 179 return; 180 } 181 if (fValues == NULL) { 182 fValues = value; 183 } else { 184 // At least one value already. 185 if (!fHasValuesVector) { 186 // There is only one value so far, and not in a vector yet. 187 // Create a vector and add the old value. 188 UVector *values = new UVector(deleteZoneStringInfo, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status); 189 if (U_FAILURE(status)) { 190 deleteZoneStringInfo(value); 191 return; 192 } 193 values->addElement(fValues, status); 194 fValues = values; 195 fHasValuesVector = TRUE; 196 } 197 // Add the new value. 198 ((UVector *)fValues)->addElement(value, status); 199 } 200} 201 202//---------------------------------------------------------------------------- 203// Virtual destructor to avoid warning 204TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){ 205} 206 207// ---------------------------------------------------------------------------- 208TextTrieMap::TextTrieMap(UBool ignoreCase) 209: fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0) { 210} 211 212TextTrieMap::~TextTrieMap() { 213 int32_t index; 214 for (index = 0; index < fNodesCount; ++index) { 215 fNodes[index].deleteValues(); 216 } 217 uprv_free(fNodes); 218} 219 220void 221TextTrieMap::put(const UnicodeString &key, void *value, UErrorCode &status) { 222 if (fNodes == NULL) { 223 fNodesCapacity = 512; 224 fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode)); 225 fNodes[0].clear(); // Init root node. 226 fNodesCount = 1; 227 } 228 229 UnicodeString foldedKey; 230 const UChar *keyBuffer; 231 int32_t keyLength; 232 if (fIgnoreCase) { 233 // Ok to use fastCopyFrom() because we discard the copy when we return. 234 foldedKey.fastCopyFrom(key).foldCase(); 235 keyBuffer = foldedKey.getBuffer(); 236 keyLength = foldedKey.length(); 237 } else { 238 keyBuffer = key.getBuffer(); 239 keyLength = key.length(); 240 } 241 242 CharacterNode *node = fNodes; 243 int32_t index; 244 for (index = 0; index < keyLength; ++index) { 245 node = addChildNode(node, keyBuffer[index], status); 246 } 247 node->addValue(value, status); 248} 249 250UBool 251TextTrieMap::growNodes() { 252 if (fNodesCapacity == 0xffff) { 253 return FALSE; // We use 16-bit node indexes. 254 } 255 int32_t newCapacity = fNodesCapacity * 2; 256 if (newCapacity > 0xffff) { 257 newCapacity = 0xffff; 258 } 259 CharacterNode *newNodes = (CharacterNode *)uprv_malloc(newCapacity * sizeof(CharacterNode)); 260 if (newNodes == NULL) { 261 return FALSE; 262 } 263 uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode)); 264 uprv_free(fNodes); 265 fNodes = newNodes; 266 fNodesCapacity = newCapacity; 267 return TRUE; 268} 269 270CharacterNode* 271TextTrieMap::addChildNode(CharacterNode *parent, UChar c, UErrorCode &status) { 272 if (U_FAILURE(status)) { 273 return NULL; 274 } 275 // Linear search of the sorted list of children. 276 uint16_t prevIndex = 0; 277 uint16_t nodeIndex = parent->fFirstChild; 278 while (nodeIndex > 0) { 279 CharacterNode *current = fNodes + nodeIndex; 280 UChar childCharacter = current->fCharacter; 281 if (childCharacter == c) { 282 return current; 283 } else if (childCharacter > c) { 284 break; 285 } 286 prevIndex = nodeIndex; 287 nodeIndex = current->fNextSibling; 288 } 289 290 // Ensure capacity. Grow fNodes[] if needed. 291 if (fNodesCount == fNodesCapacity) { 292 int32_t parentIndex = (parent - fNodes); 293 if (!growNodes()) { 294 status = U_MEMORY_ALLOCATION_ERROR; 295 return NULL; 296 } 297 parent = fNodes + parentIndex; 298 } 299 300 // Insert a new child node with c in sorted order. 301 CharacterNode *node = fNodes + fNodesCount; 302 node->clear(); 303 node->fCharacter = c; 304 node->fNextSibling = nodeIndex; 305 if (prevIndex == 0) { 306 parent->fFirstChild = (uint16_t)fNodesCount; 307 } else { 308 fNodes[prevIndex].fNextSibling = (uint16_t)fNodesCount; 309 } 310 ++fNodesCount; 311 return node; 312} 313 314CharacterNode* 315TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const { 316 // Linear search of the sorted list of children. 317 uint16_t nodeIndex = parent->fFirstChild; 318 while (nodeIndex > 0) { 319 CharacterNode *current = fNodes + nodeIndex; 320 UChar childCharacter = current->fCharacter; 321 if (childCharacter == c) { 322 return current; 323 } else if (childCharacter > c) { 324 break; 325 } 326 nodeIndex = current->fNextSibling; 327 } 328 return NULL; 329} 330 331void 332TextTrieMap::search(const UnicodeString &text, int32_t start, 333 TextTrieMapSearchResultHandler *handler, UErrorCode &status) const { 334 if (fNodes == NULL) { 335 return; 336 } 337 search(fNodes, text, start, start, handler, status); 338} 339 340void 341TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start, 342 int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const { 343 if (U_FAILURE(status)) { 344 return; 345 } 346 if (node->hasValues()) { 347 if (!handler->handleMatch(index - start, node, status)) { 348 return; 349 } 350 if (U_FAILURE(status)) { 351 return; 352 } 353 } 354 UChar32 c = text.char32At(index); 355 if (fIgnoreCase) { 356 // size of character may grow after fold operation 357 UnicodeString tmp(c); 358 tmp.foldCase(); 359 int32_t tmpidx = 0; 360 while (tmpidx < tmp.length()) { 361 c = tmp.char32At(tmpidx); 362 node = getChildNode(node, c); 363 if (node == NULL) { 364 break; 365 } 366 tmpidx = tmp.moveIndex32(tmpidx, 1); 367 } 368 } else { 369 node = getChildNode(node, c); 370 } 371 if (node != NULL) { 372 search(node, text, start, index+1, handler, status); 373 } 374} 375 376// ---------------------------------------------------------------------------- 377ZoneStringInfo::ZoneStringInfo(const UnicodeString &id, const UnicodeString &str, 378 TimeZoneTranslationType type) 379: fId(id), fStr(str), fType(type) { 380} 381 382ZoneStringInfo::~ZoneStringInfo() { 383} 384// ---------------------------------------------------------------------------- 385ZoneStringSearchResultHandler::ZoneStringSearchResultHandler(UErrorCode &status) 386: fResults(status) 387{ 388 clear(); 389} 390 391ZoneStringSearchResultHandler::~ZoneStringSearchResultHandler() { 392 clear(); 393} 394 395UBool 396ZoneStringSearchResultHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) { 397 if (U_FAILURE(status)) { 398 return FALSE; 399 } 400 if (node->hasValues()) { 401 int32_t valuesCount = node->countValues(); 402 for (int32_t i = 0; i < valuesCount; i++) { 403 ZoneStringInfo *zsinfo = (ZoneStringInfo*)node->getValue(i); 404 if (zsinfo == NULL) { 405 break; 406 } 407 // Update the results 408 UBool foundType = FALSE; 409 for (int32_t j = 0; j < fResults.size(); j++) { 410 ZoneStringInfo *tmp = (ZoneStringInfo*)fResults.elementAt(j); 411 if (zsinfo->fType == tmp->fType) { 412 int32_t lenidx = getTimeZoneTranslationTypeIndex(tmp->fType); 413 if (matchLength > fMatchLen[lenidx]) { 414 // Same type, longer match 415 fResults.setElementAt(zsinfo, j); 416 fMatchLen[lenidx] = matchLength; 417 } 418 foundType = TRUE; 419 break; 420 } 421 } 422 if (!foundType) { 423 // not found in the current list 424 fResults.addElement(zsinfo, status); 425 fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)] = matchLength; 426 } 427 } 428 } 429 return TRUE; 430} 431 432int32_t 433ZoneStringSearchResultHandler::countMatches(void) { 434 return fResults.size(); 435} 436 437const ZoneStringInfo* 438ZoneStringSearchResultHandler::getMatch(int32_t index, int32_t &matchLength) { 439 ZoneStringInfo *zsinfo = NULL; 440 if (index < fResults.size()) { 441 zsinfo = (ZoneStringInfo*)fResults.elementAt(index); 442 matchLength = fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)]; 443 } 444 return zsinfo; 445} 446 447void 448ZoneStringSearchResultHandler::clear(void) { 449 fResults.removeAllElements(); 450 for (int32_t i = 0; i < (int32_t)(sizeof(fMatchLen)/sizeof(fMatchLen[0])); i++) { 451 fMatchLen[i] = 0; 452 } 453} 454// ---------------------------------------------------------------------------- 455ZoneStringFormat::ZoneStringFormat(const UnicodeString* const* strings, 456 int32_t rowCount, int32_t columnCount, UErrorCode &status) 457: fLocale(""), 458 fTzidToStrings(uhash_compareUnicodeString, NULL, status), 459 fMzidToStrings(uhash_compareUnicodeString, NULL, status), 460 fZoneStringsTrie(TRUE) 461{ 462 if (U_FAILURE(status)) { 463 return; 464 } 465 fLocale.setToBogus(); 466 if (strings == NULL || columnCount <= 0 || rowCount <= 0) { 467 status = U_ILLEGAL_ARGUMENT_ERROR; 468 return; 469 } 470 471 fTzidToStrings.setValueDeleter(deleteZoneStrings); 472 473 for (int32_t row = 0; row < rowCount; row++) { 474 if (strings[row][0].isEmpty()) { 475 continue; 476 } 477 UnicodeString *names = new UnicodeString[ZSIDX_COUNT]; 478 for (int32_t col = 1; col < columnCount; col++) { 479 if (!strings[row][col].isEmpty()) { 480 int32_t typeIdx = -1; 481 switch (col) { 482 case 1: 483 typeIdx = ZSIDX_LONG_STANDARD; 484 break; 485 case 2: 486 typeIdx = ZSIDX_SHORT_STANDARD; 487 break; 488 case 3: 489 typeIdx = ZSIDX_LONG_DAYLIGHT; 490 break; 491 case 4: 492 typeIdx = ZSIDX_SHORT_DAYLIGHT; 493 break; 494 case 5: 495 typeIdx = ZSIDX_LOCATION; 496 break; 497 case 6: 498 typeIdx = ZSIDX_LONG_GENERIC; 499 break; 500 case 7: 501 typeIdx = ZSIDX_SHORT_GENERIC; 502 break; 503 } 504 if (typeIdx != -1) { 505 names[typeIdx].setTo(strings[row][col]); 506 507 // Put the name into the trie 508 int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeIdx); 509 ZoneStringInfo *zsinf = new ZoneStringInfo(strings[row][0], strings[row][col], (TimeZoneTranslationType)type); 510 fZoneStringsTrie.put(strings[row][col], zsinf, status); 511 if (U_FAILURE(status)) { 512 delete zsinf; 513 goto error_cleanup; 514 } 515 } 516 } 517 } 518 ZoneStrings *zstrings = new ZoneStrings(names, ZSIDX_COUNT, TRUE, NULL, 0, 0); 519 fTzidToStrings.put(strings[row][0], zstrings, status); 520 if (U_FAILURE(status)) { 521 delete zstrings; 522 goto error_cleanup; 523 } 524 } 525 return; 526 527error_cleanup: 528 return; 529} 530 531ZoneStringFormat::ZoneStringFormat(const Locale &locale, UErrorCode &status) 532: fLocale(locale), 533 fTzidToStrings(uhash_compareUnicodeString, NULL, status), 534 fMzidToStrings(uhash_compareUnicodeString, NULL, status), 535 fZoneStringsTrie(TRUE) 536{ 537 if (U_FAILURE(status)) { 538 return; 539 } 540 fTzidToStrings.setValueDeleter(deleteZoneStrings); 541 fMzidToStrings.setValueDeleter(deleteZoneStrings); 542 543 UResourceBundle *zoneStringsArray = ures_open(NULL, locale.getName(), &status); 544 zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status); 545 if (U_FAILURE(status)) { 546 // If no locale bundles are available, zoneStrings will be null. 547 // We still want to go through the rest of zone strings initialization, 548 // because generic location format is generated from tzid for the case. 549 // The rest of code should work even zoneStrings is null. 550 status = U_ZERO_ERROR; 551 ures_close(zoneStringsArray); 552 zoneStringsArray = NULL; 553 } 554 555 StringEnumeration *tzids = NULL; 556 MessageFormat *fallbackFmt = NULL; 557 MessageFormat *regionFmt = NULL; 558 559 UResourceBundle *zoneItem = NULL; 560 UResourceBundle *metazoneItem = NULL; 561 562 char zidkey[ZID_KEY_MAX]; 563 const UChar *zstrarray[ZSIDX_COUNT]; 564 const UChar *mzstrarray[ZSIDX_COUNT]; 565 UnicodeString mzPartialLoc[MAX_METAZONES_PER_ZONE][4]; 566 567 UnicodeString region; 568 getRegion(region); 569 570 fallbackFmt = getFallbackFormat(locale, status); 571 if (U_FAILURE(status)) { 572 goto error_cleanup; 573 } 574 regionFmt = getRegionFormat(locale, status); 575 if (U_FAILURE(status)) { 576 goto error_cleanup; 577 } 578 579 tzids = TimeZone::createEnumeration(); 580 const char *tzid; 581 while ((tzid = tzids->next(NULL, status))) { 582 if (U_FAILURE(status)) { 583 goto error_cleanup; 584 } 585 // Skip non-canonical IDs 586 UnicodeString utzid(tzid, -1, US_INV); 587 UnicodeString canonicalID; 588 TimeZone::getCanonicalID(utzid, canonicalID, status); 589 if (U_FAILURE(status)) { 590 // Ignore unknown ID - we should not get here, but just in case. 591 status = U_ZERO_ERROR; 592 continue; 593 } 594 if (utzid != canonicalID) { 595 continue; 596 } 597 598 uprv_strcpy(zidkey, tzid); 599 600 // Replace '/' with ':' 601 char *pCity = NULL; 602 char *p = zidkey; 603 while (*p) { 604 if (*p == '/') { 605 *p = ':'; 606 pCity = p + 1; 607 } 608 p++; 609 } 610 611 if (zoneStringsArray != NULL) { 612 zoneItem = ures_getByKeyWithFallback(zoneStringsArray, zidkey, zoneItem, &status); 613 if (U_FAILURE(status)) { 614 // If failed to open the zone item, create only location string 615 ures_close(zoneItem); 616 zoneItem = NULL; 617 status = U_ZERO_ERROR; 618 } 619 } 620 zstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(zoneItem, gLongStandardTag); 621 zstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(zoneItem, gShortStandardTag); 622 zstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(zoneItem, gLongDaylightTag); 623 zstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(zoneItem, gShortDaylightTag); 624 zstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(zoneItem, gLongGenericTag); 625 zstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(zoneItem, gShortGenericTag); 626 627 // Compose location format string 628 UnicodeString location; 629 UnicodeString country; 630 UnicodeString city; 631 UnicodeString countryCode; 632 ZoneMeta::getCanonicalCountry(utzid, countryCode); 633 if (!countryCode.isEmpty()) { 634 const UChar* tmpCity = getZoneStringFromBundle(zoneItem, gExemplarCityTag); 635 if (tmpCity != NULL) { 636 city.setTo(TRUE, tmpCity, -1); 637 } else { 638 city.setTo(UnicodeString(pCity, -1, US_INV)); 639 // Replace '_' with ' ' 640 for (int32_t i = 0; i < city.length(); i++) { 641 if (city.charAt(i) == (UChar)0x5F /*'_'*/) { 642 city.setCharAt(i, (UChar)0x20 /*' '*/); 643 } 644 } 645 } 646 getLocalizedCountry(countryCode, locale, country); 647 UnicodeString singleCountry; 648 ZoneMeta::getSingleCountry(utzid, singleCountry); 649 FieldPosition fpos; 650 if (singleCountry.isEmpty()) { 651 Formattable params [] = { 652 Formattable(city), 653 Formattable(country) 654 }; 655 fallbackFmt->format(params, 2, location, fpos, status); 656 } else { 657 // If the zone is only one zone in the country, do not add city 658 Formattable params [] = { 659 Formattable(country) 660 }; 661 regionFmt->format(params, 1, location, fpos, status); 662 } 663 if (U_FAILURE(status)) { 664 goto error_cleanup; 665 } 666 667 // Workaround for reducing UMR warning in Purify. 668 // Append NULL before calling getTerminatedBuffer() 669 int32_t locLen = location.length(); 670 location.append((UChar)0).truncate(locLen); 671 672 zstrarray[ZSIDX_LOCATION] = location.getTerminatedBuffer(); 673 } else { 674 if (uprv_strlen(tzid) > 4 && uprv_strncmp(tzid, "Etc/", 4) == 0) { 675 // "Etc/xxx" is not associated with a specific location, so localized 676 // GMT format is always used as generic location format. 677 zstrarray[ZSIDX_LOCATION] = NULL; 678 } else { 679 // When a new time zone ID, which is actually associated with a specific 680 // location, is added in tzdata, but the current CLDR data does not have 681 // the information yet, ICU creates a generic location string based on 682 // the ID. This implementation supports canonical time zone round trip 683 // with format pattern "VVVV". See #6602 for the details. 684 UnicodeString loc(utzid); 685 int32_t slashIdx = loc.lastIndexOf((UChar)0x2f); 686 if (slashIdx == -1) { 687 // A time zone ID without slash in the tz database is not 688 // associated with a specific location. For instances, 689 // MET, CET, EET and WET fall into this category. 690 // In this case, we still use GMT format as fallback. 691 zstrarray[ZSIDX_LOCATION] = NULL; 692 } else { 693 FieldPosition fpos; 694 Formattable params[] = { 695 Formattable(loc) 696 }; 697 regionFmt->format(params, 1, location, fpos, status); 698 if (U_FAILURE(status)) { 699 goto error_cleanup; 700 } 701 // Workaround for reducing UMR warning in Purify. 702 // Append NULL before calling getTerminatedBuffer() 703 int32_t locLen = location.length(); 704 location.append((UChar)0).truncate(locLen); 705 706 zstrarray[ZSIDX_LOCATION] = location.getTerminatedBuffer(); 707 } 708 } 709 } 710 711 UBool commonlyUsed = isCommonlyUsed(zoneItem); 712 713 // Resolve metazones used by this zone 714 int32_t mzPartialLocIdx = 0; 715 const UVector *metazoneMappings = ZoneMeta::getMetazoneMappings(utzid); 716 if (metazoneMappings != NULL) { 717 for (int32_t i = 0; i < metazoneMappings->size(); i++) { 718 const OlsonToMetaMappingEntry *mzmap = (const OlsonToMetaMappingEntry*)metazoneMappings->elementAt(i); 719 UnicodeString mzid(mzmap->mzid); 720 const ZoneStrings *mzStrings = (const ZoneStrings*)fMzidToStrings.get(mzid); 721 if (mzStrings == NULL) { 722 // If the metazone strings are not yet processed, do it now. 723 char mzidkey[ZID_KEY_MAX]; 724 uprv_strcpy(mzidkey, gMetazoneIdPrefix); 725 u_UCharsToChars(mzmap->mzid, mzidkey + MZID_PREFIX_LEN, u_strlen(mzmap->mzid) + 1); 726 metazoneItem = ures_getByKeyWithFallback(zoneStringsArray, mzidkey, metazoneItem, &status); 727 if (U_FAILURE(status)) { 728 // No resources available for this metazone 729 // Resource bundle will be cleaned up after end of the loop. 730 status = U_ZERO_ERROR; 731 continue; 732 } 733 UBool mzCommonlyUsed = isCommonlyUsed(metazoneItem); 734 mzstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(metazoneItem, gLongStandardTag); 735 mzstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(metazoneItem, gShortStandardTag); 736 mzstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(metazoneItem, gLongDaylightTag); 737 mzstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(metazoneItem, gShortDaylightTag); 738 mzstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(metazoneItem, gLongGenericTag); 739 mzstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(metazoneItem, gShortGenericTag); 740 mzstrarray[ZSIDX_LOCATION] = NULL; 741 742 int32_t lastNonNullIdx = ZSIDX_COUNT - 1; 743 while (lastNonNullIdx >= 0) { 744 if (mzstrarray[lastNonNullIdx] != NULL) { 745 break; 746 } 747 lastNonNullIdx--; 748 } 749 UnicodeString *strings_mz = NULL; 750 ZoneStrings *tmp_mzStrings = NULL; 751 if (lastNonNullIdx >= 0) { 752 // Create UnicodeString array and put strings to the zone string trie 753 strings_mz = new UnicodeString[lastNonNullIdx + 1]; 754 755 UnicodeString preferredIdForLocale; 756 ZoneMeta::getZoneIdByMetazone(mzid, region, preferredIdForLocale); 757 758 for (int32_t typeidx = 0; typeidx <= lastNonNullIdx; typeidx++) { 759 if (mzstrarray[typeidx] != NULL) { 760 strings_mz[typeidx].setTo(TRUE, mzstrarray[typeidx], -1); 761 762 // Add a metazone string to the zone string trie 763 int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeidx); 764 ZoneStringInfo *zsinfo = new ZoneStringInfo(preferredIdForLocale, strings_mz[typeidx], (TimeZoneTranslationType)type); 765 fZoneStringsTrie.put(strings_mz[typeidx], zsinfo, status); 766 if (U_FAILURE(status)) { 767 delete []strings_mz; 768 goto error_cleanup; 769 } 770 } 771 } 772 tmp_mzStrings = new ZoneStrings(strings_mz, lastNonNullIdx + 1, mzCommonlyUsed, NULL, 0, 0); 773 } else { 774 // Create ZoneStrings with empty contents 775 tmp_mzStrings = new ZoneStrings(NULL, 0, FALSE, NULL, 0, 0); 776 } 777 778 fMzidToStrings.put(mzid, tmp_mzStrings, status); 779 if (U_FAILURE(status)) { 780 goto error_cleanup; 781 } 782 783 mzStrings = tmp_mzStrings; 784 } 785 786 // Compose generic partial location format 787 UnicodeString lg; 788 UnicodeString sg; 789 790 mzStrings->getString(ZSIDX_LONG_GENERIC, lg); 791 mzStrings->getString(ZSIDX_SHORT_GENERIC, sg); 792 793 if (!lg.isEmpty() || !sg.isEmpty()) { 794 UBool addMzPartialLocationNames = TRUE; 795 for (int32_t j = 0; j < mzPartialLocIdx; j++) { 796 if (mzPartialLoc[j][0] == mzid) { 797 // already processed 798 addMzPartialLocationNames = FALSE; 799 break; 800 } 801 } 802 if (addMzPartialLocationNames) { 803 UnicodeString *locationPart = NULL; 804 // Check if the zone is the preferred zone for the territory associated with the zone 805 UnicodeString preferredID; 806 ZoneMeta::getZoneIdByMetazone(mzid, countryCode, preferredID); 807 if (utzid == preferredID) { 808 // Use country for the location 809 locationPart = &country; 810 } else { 811 // Use city for the location 812 locationPart = &city; 813 } 814 // Reset the partial location string array 815 mzPartialLoc[mzPartialLocIdx][0].setTo(mzid); 816 mzPartialLoc[mzPartialLocIdx][1].remove(); 817 mzPartialLoc[mzPartialLocIdx][2].remove(); 818 mzPartialLoc[mzPartialLocIdx][3].remove(); 819 820 if (locationPart != NULL) { 821 FieldPosition fpos; 822 if (!lg.isEmpty()) { 823 Formattable params [] = { 824 Formattable(*locationPart), 825 Formattable(lg) 826 }; 827 fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][1], fpos, status); 828 } 829 if (!sg.isEmpty()) { 830 Formattable params [] = { 831 Formattable(*locationPart), 832 Formattable(sg) 833 }; 834 fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][2], fpos, status); 835 if (mzStrings->isShortFormatCommonlyUsed()) { 836 mzPartialLoc[mzPartialLocIdx][3].setTo(TRUE, gCommonlyUsedTrue, -1); 837 } 838 } 839 if (U_FAILURE(status)) { 840 goto error_cleanup; 841 } 842 } 843 mzPartialLocIdx++; 844 } 845 } 846 } 847 } 848 // Collected names for a zone 849 850 // Create UnicodeString array for localized zone strings 851 int32_t lastIdx = ZSIDX_COUNT - 1; 852 while (lastIdx >= 0) { 853 if (zstrarray[lastIdx] != NULL) { 854 break; 855 } 856 lastIdx--; 857 } 858 UnicodeString *strings = NULL; 859 int32_t stringsCount = lastIdx + 1; 860 861 if (stringsCount > 0) { 862 strings = new UnicodeString[stringsCount]; 863 for (int32_t i = 0; i < stringsCount; i++) { 864 if (zstrarray[i] != NULL) { 865 strings[i].setTo(zstrarray[i], -1); 866 867 // Add names to the trie 868 int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)i); 869 ZoneStringInfo *zsinfo = new ZoneStringInfo(utzid, strings[i], (TimeZoneTranslationType)type); 870 fZoneStringsTrie.put(strings[i], zsinfo, status); 871 if (U_FAILURE(status)) { 872 delete zsinfo; 873 delete[] strings; 874 goto error_cleanup; 875 } 876 } 877 } 878 } 879 880 // Create UnicodeString array for generic partial location strings 881 UnicodeString **genericPartialLocationNames = NULL; 882 int32_t genericPartialRowCount = mzPartialLocIdx; 883 int32_t genericPartialColCount = 4; 884 885 if (genericPartialRowCount != 0) { 886 genericPartialLocationNames = (UnicodeString**)uprv_malloc(genericPartialRowCount * sizeof(UnicodeString*)); 887 if (genericPartialLocationNames == NULL) { 888 status = U_MEMORY_ALLOCATION_ERROR; 889 delete[] strings; 890 goto error_cleanup; 891 } 892 for (int32_t i = 0; i < genericPartialRowCount; i++) { 893 genericPartialLocationNames[i] = new UnicodeString[genericPartialColCount]; 894 for (int32_t j = 0; j < genericPartialColCount; j++) { 895 genericPartialLocationNames[i][j].setTo(mzPartialLoc[i][j]); 896 // Add names to the trie 897 if ((j == 1 || j == 2) &&!genericPartialLocationNames[i][j].isEmpty()) { 898 ZoneStringInfo *zsinfo; 899 TimeZoneTranslationType type = (j == 1) ? GENERIC_LONG : GENERIC_SHORT; 900 zsinfo = new ZoneStringInfo(utzid, genericPartialLocationNames[i][j], type); 901 fZoneStringsTrie.put(genericPartialLocationNames[i][j], zsinfo, status); 902 if (U_FAILURE(status)) { 903 delete[] genericPartialLocationNames[i]; 904 uprv_free(genericPartialLocationNames); 905 delete[] strings; 906 goto error_cleanup; 907 } 908 } 909 } 910 } 911 } 912 913 // Finally, create ZoneStrings instance and put it into the tzidToStinrgs map 914 ZoneStrings *zstrings = new ZoneStrings(strings, stringsCount, commonlyUsed, 915 genericPartialLocationNames, genericPartialRowCount, genericPartialColCount); 916 917 fTzidToStrings.put(utzid, zstrings, status); 918 if (U_FAILURE(status)) { 919 delete zstrings; 920 goto error_cleanup; 921 } 922 } 923 924error_cleanup: 925 if (fallbackFmt != NULL) { 926 delete fallbackFmt; 927 } 928 if (regionFmt != NULL) { 929 delete regionFmt; 930 } 931 if (tzids != NULL) { 932 delete tzids; 933 } 934 ures_close(zoneItem); 935 ures_close(metazoneItem); 936 ures_close(zoneStringsArray); 937} 938 939ZoneStringFormat::~ZoneStringFormat() { 940} 941 942SafeZoneStringFormatPtr* 943ZoneStringFormat::getZoneStringFormat(const Locale& locale, UErrorCode &status) { 944 umtx_lock(&gZSFCacheLock); 945 if (gZoneStringFormatCache == NULL) { 946 gZoneStringFormatCache = new ZSFCache(10 /* capacity */); 947 ucln_i18n_registerCleanup(UCLN_I18N_ZSFORMAT, zoneStringFormat_cleanup); 948 } 949 umtx_unlock(&gZSFCacheLock); 950 951 return gZoneStringFormatCache->get(locale, status); 952} 953 954 955UnicodeString** 956ZoneStringFormat::createZoneStringsArray(UDate date, int32_t &rowCount, int32_t &colCount, UErrorCode &status) const { 957 if (U_FAILURE(status)) { 958 return NULL; 959 } 960 UnicodeString **result = NULL; 961 rowCount = 0; 962 colCount = 0; 963 964 // Collect canonical time zone IDs 965 UVector canonicalIDs(uhash_deleteUnicodeString, uhash_compareUnicodeString, status); 966 if (U_FAILURE(status)) { 967 return NULL; 968 } 969 StringEnumeration *tzids = TimeZone::createEnumeration(); 970 const UChar *tzid; 971 while ((tzid = tzids->unext(NULL, status))) { 972 if (U_FAILURE(status)) { 973 delete tzids; 974 return NULL; 975 } 976 UnicodeString utzid(tzid); 977 UnicodeString canonicalID; 978 TimeZone::getCanonicalID(UnicodeString(tzid), canonicalID, status); 979 if (U_FAILURE(status)) { 980 // Ignore unknown ID - we should not get here, but just in case. 981 status = U_ZERO_ERROR; 982 continue; 983 } 984 if (utzid == canonicalID) { 985 canonicalIDs.addElement(new UnicodeString(utzid), status); 986 if (U_FAILURE(status)) { 987 delete tzids; 988 return NULL; 989 } 990 } 991 } 992 delete tzids; 993 994 // Allocate array 995 result = (UnicodeString**)uprv_malloc(canonicalIDs.size() * sizeof(UnicodeString*)); 996 if (result == NULL) { 997 status = U_MEMORY_ALLOCATION_ERROR; 998 return NULL; 999 } 1000 for (int32_t i = 0; i < canonicalIDs.size(); i++) { 1001 result[i] = new UnicodeString[8]; 1002 UnicodeString *id = (UnicodeString*)canonicalIDs.elementAt(i); 1003 result[i][0].setTo(*id); 1004 getLongStandard(*id, date, result[i][1]); 1005 getShortStandard(*id, date, FALSE, result[i][2]); 1006 getLongDaylight(*id, date, result[i][3]); 1007 getShortDaylight(*id, date, FALSE, result[i][4]); 1008 getGenericLocation(*id, result[i][5]); 1009 getLongGenericNonLocation(*id, date, result[i][6]); 1010 getShortGenericNonLocation(*id, date, FALSE, result[i][7]); 1011 } 1012 1013 rowCount = canonicalIDs.size(); 1014 colCount = 8; 1015 return result; 1016} 1017 1018UnicodeString& 1019ZoneStringFormat::getSpecificLongString(const Calendar &cal, UnicodeString &result, 1020 UErrorCode &status) const { 1021 result.remove(); 1022 if (U_FAILURE(status)) { 1023 return result; 1024 } 1025 UnicodeString tzid; 1026 cal.getTimeZone().getID(tzid); 1027 UDate date = cal.getTime(status); 1028 if (cal.get(UCAL_DST_OFFSET, status) == 0) { 1029 return getString(tzid, ZSIDX_LONG_STANDARD, date, FALSE /*not used*/, result); 1030 } else { 1031 return getString(tzid, ZSIDX_LONG_DAYLIGHT, date, FALSE /*not used*/, result); 1032 } 1033} 1034 1035UnicodeString& 1036ZoneStringFormat::getSpecificShortString(const Calendar &cal, UBool commonlyUsedOnly, 1037 UnicodeString &result, UErrorCode &status) const { 1038 result.remove(); 1039 if (U_FAILURE(status)) { 1040 return result; 1041 } 1042 UnicodeString tzid; 1043 cal.getTimeZone().getID(tzid); 1044 UDate date = cal.getTime(status); 1045 if (cal.get(UCAL_DST_OFFSET, status) == 0) { 1046 return getString(tzid, ZSIDX_SHORT_STANDARD, date, commonlyUsedOnly, result); 1047 } else { 1048 return getString(tzid, ZSIDX_SHORT_DAYLIGHT, date, commonlyUsedOnly, result); 1049 } 1050} 1051 1052UnicodeString& 1053ZoneStringFormat::getGenericLongString(const Calendar &cal, UnicodeString &result, 1054 UErrorCode &status) const { 1055 return getGenericString(cal, FALSE /*long*/, FALSE /* not used */, result, status); 1056} 1057 1058UnicodeString& 1059ZoneStringFormat::getGenericShortString(const Calendar &cal, UBool commonlyUsedOnly, 1060 UnicodeString &result, UErrorCode &status) const { 1061 return getGenericString(cal, TRUE /*short*/, commonlyUsedOnly, result, status); 1062} 1063 1064UnicodeString& 1065ZoneStringFormat::getGenericLocationString(const Calendar &cal, UnicodeString &result, 1066 UErrorCode &status) const { 1067 UnicodeString tzid; 1068 cal.getTimeZone().getID(tzid); 1069 UDate date = cal.getTime(status); 1070 return getString(tzid, ZSIDX_LOCATION, date, FALSE /*not used*/, result); 1071} 1072 1073const ZoneStringInfo* 1074ZoneStringFormat::findSpecificLong(const UnicodeString &text, int32_t start, 1075 int32_t &matchLength, UErrorCode &status) const { 1076 return find(text, start, STANDARD_LONG | DAYLIGHT_LONG, matchLength, status); 1077} 1078 1079const ZoneStringInfo* 1080ZoneStringFormat::findSpecificShort(const UnicodeString &text, int32_t start, 1081 int32_t &matchLength, UErrorCode &status) const { 1082 return find(text, start, STANDARD_SHORT | DAYLIGHT_SHORT, matchLength, status); 1083} 1084 1085const ZoneStringInfo* 1086ZoneStringFormat::findGenericLong(const UnicodeString &text, int32_t start, 1087 int32_t &matchLength, UErrorCode &status) const { 1088 return find(text, start, GENERIC_LONG | STANDARD_LONG | LOCATION, matchLength, status); 1089} 1090 1091const ZoneStringInfo* 1092ZoneStringFormat::findGenericShort(const UnicodeString &text, int32_t start, 1093 int32_t &matchLength, UErrorCode &status) const { 1094 return find(text, start, GENERIC_SHORT | STANDARD_SHORT | LOCATION, matchLength, status); 1095} 1096 1097const ZoneStringInfo* 1098ZoneStringFormat::findGenericLocation(const UnicodeString &text, int32_t start, 1099 int32_t &matchLength, UErrorCode &status) const { 1100 return find(text, start, LOCATION, matchLength, status); 1101} 1102 1103UnicodeString& 1104ZoneStringFormat::getString(const UnicodeString &tzid, TimeZoneTranslationTypeIndex typeIdx, UDate date, 1105 UBool commonlyUsedOnly, UnicodeString& result) const { 1106 result.remove(); 1107 1108 // ICU's own array does not have entries for aliases 1109 UnicodeString canonicalID; 1110 UErrorCode status = U_ZERO_ERROR; 1111 TimeZone::getCanonicalID(tzid, canonicalID, status); 1112 if (U_FAILURE(status)) { 1113 // Unknown ID, but users might have their own data. 1114 canonicalID.setTo(tzid); 1115 } 1116 1117 if (fTzidToStrings.count() > 0) { 1118 ZoneStrings *zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID); 1119 if (zstrings != NULL) { 1120 switch (typeIdx) { 1121 case ZSIDX_LONG_STANDARD: 1122 case ZSIDX_LONG_DAYLIGHT: 1123 case ZSIDX_LONG_GENERIC: 1124 case ZSIDX_LOCATION: 1125 zstrings->getString(typeIdx, result); 1126 break; 1127 case ZSIDX_SHORT_STANDARD: 1128 case ZSIDX_SHORT_DAYLIGHT: 1129 case ZSIDX_COUNT: //added to avoid warning 1130 case ZSIDX_SHORT_GENERIC: 1131 if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) { 1132 zstrings->getString(typeIdx, result); 1133 } 1134 break; 1135 default: 1136 break; 1137 } 1138 } 1139 } 1140 if (result.isEmpty() && fMzidToStrings.count() > 0 && typeIdx != ZSIDX_LOCATION) { 1141 // Try metazone 1142 UnicodeString mzid; 1143 ZoneMeta::getMetazoneID(canonicalID, date, mzid); 1144 if (!mzid.isEmpty()) { 1145 ZoneStrings *mzstrings = (ZoneStrings*)fMzidToStrings.get(mzid); 1146 if (mzstrings != NULL) { 1147 switch (typeIdx) { 1148 case ZSIDX_LONG_STANDARD: 1149 case ZSIDX_LONG_DAYLIGHT: 1150 case ZSIDX_LONG_GENERIC: 1151 case ZSIDX_LOCATION: 1152 mzstrings->getString(typeIdx, result); 1153 break; 1154 case ZSIDX_SHORT_STANDARD: 1155 case ZSIDX_SHORT_DAYLIGHT: 1156 case ZSIDX_COUNT: //added to avoid warning 1157 case ZSIDX_SHORT_GENERIC: 1158 if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) { 1159 mzstrings->getString(typeIdx, result); 1160 } 1161 break; 1162 default: 1163 break; 1164 } 1165 } 1166 } 1167 } 1168 return result; 1169} 1170 1171UnicodeString& 1172ZoneStringFormat::getGenericString(const Calendar &cal, UBool isShort, UBool commonlyUsedOnly, 1173 UnicodeString &result, UErrorCode &status) const { 1174 result.remove(); 1175 UDate time = cal.getTime(status); 1176 if (U_FAILURE(status)) { 1177 return result; 1178 } 1179 const TimeZone &tz = cal.getTimeZone(); 1180 UnicodeString tzid; 1181 tz.getID(tzid); 1182 1183 // ICU's own array does not have entries for aliases 1184 UnicodeString canonicalID; 1185 TimeZone::getCanonicalID(tzid, canonicalID, status); 1186 if (U_FAILURE(status)) { 1187 // Unknown ID, but users might have their own data. 1188 status = U_ZERO_ERROR; 1189 canonicalID.setTo(tzid); 1190 } 1191 1192 ZoneStrings *zstrings = NULL; 1193 if (fTzidToStrings.count() > 0) { 1194 zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID); 1195 if (zstrings != NULL) { 1196 if (isShort) { 1197 if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) { 1198 zstrings->getString(ZSIDX_SHORT_GENERIC, result); 1199 } 1200 } else { 1201 zstrings->getString(ZSIDX_LONG_GENERIC, result); 1202 } 1203 } 1204 } 1205 if (result.isEmpty() && fMzidToStrings.count() > 0) { 1206 // try metazone 1207 int32_t raw, sav; 1208 UnicodeString mzid; 1209 ZoneMeta::getMetazoneID(canonicalID, time, mzid); 1210 if (!mzid.isEmpty()) { 1211 UBool useStandard = FALSE; 1212 sav = cal.get(UCAL_DST_OFFSET, status); 1213 if (U_FAILURE(status)) { 1214 return result; 1215 } 1216 if (sav == 0) { 1217 useStandard = TRUE; 1218 // Check if the zone actually uses daylight saving time around the time 1219 TimeZone *tmptz = tz.clone(); 1220 BasicTimeZone *btz = NULL; 1221 if (tmptz->getDynamicClassID() == OlsonTimeZone::getStaticClassID() 1222 || tmptz->getDynamicClassID() == SimpleTimeZone::getStaticClassID() 1223 || tmptz->getDynamicClassID() == RuleBasedTimeZone::getStaticClassID() 1224 || tmptz->getDynamicClassID() == VTimeZone::getStaticClassID()) { 1225 btz = (BasicTimeZone*)tmptz; 1226 } 1227 1228 if (btz != NULL) { 1229 TimeZoneTransition before; 1230 UBool beforTrs = btz->getPreviousTransition(time, TRUE, before); 1231 if (beforTrs 1232 && (time - before.getTime() < kDstCheckRange) 1233 && before.getFrom()->getDSTSavings() != 0) { 1234 useStandard = FALSE; 1235 } else { 1236 TimeZoneTransition after; 1237 UBool afterTrs = btz->getNextTransition(time, FALSE, after); 1238 if (afterTrs 1239 && (after.getTime() - time < kDstCheckRange) 1240 && after.getTo()->getDSTSavings() != 0) { 1241 useStandard = FALSE; 1242 } 1243 } 1244 } else { 1245 // If not BasicTimeZone... only if the instance is not an ICU's implementation. 1246 // We may get a wrong answer in edge case, but it should practically work OK. 1247 tmptz->getOffset(time - kDstCheckRange, FALSE, raw, sav, status); 1248 if (sav != 0) { 1249 useStandard = FALSE; 1250 } else { 1251 tmptz->getOffset(time + kDstCheckRange, FALSE, raw, sav, status); 1252 if (sav != 0){ 1253 useStandard = FALSE; 1254 } 1255 } 1256 if (U_FAILURE(status)) { 1257 delete tmptz; 1258 result.remove(); 1259 return result; 1260 } 1261 } 1262 delete tmptz; 1263 } 1264 if (useStandard) { 1265 getString(canonicalID, (isShort ? ZSIDX_SHORT_STANDARD : ZSIDX_LONG_STANDARD), 1266 time, commonlyUsedOnly, result); 1267 1268 // Note: 1269 // In CLDR 1.5.1, a same localization is used for both generic and standard 1270 // for some metazones in some locales. This is actually data bugs and should 1271 // be resolved in later versions of CLDR. For now, we check if the standard 1272 // name is different from its generic name below. 1273 if (!result.isEmpty()) { 1274 UnicodeString genericNonLocation; 1275 getString(canonicalID, (isShort ? ZSIDX_SHORT_GENERIC : ZSIDX_LONG_GENERIC), 1276 time, commonlyUsedOnly, genericNonLocation); 1277 if (!genericNonLocation.isEmpty() && result == genericNonLocation) { 1278 result.remove(); 1279 } 1280 } 1281 } 1282 if (result.isEmpty()) { 1283 ZoneStrings *mzstrings = (ZoneStrings*)fMzidToStrings.get(mzid); 1284 if (mzstrings != NULL) { 1285 if (isShort) { 1286 if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) { 1287 mzstrings->getString(ZSIDX_SHORT_GENERIC, result); 1288 } 1289 } else { 1290 mzstrings->getString(ZSIDX_LONG_GENERIC, result); 1291 } 1292 } 1293 if (!result.isEmpty()) { 1294 // Check if the offsets at the given time matches the preferred zone's offsets 1295 UnicodeString preferredId; 1296 UnicodeString region; 1297 ZoneMeta::getZoneIdByMetazone(mzid, getRegion(region), preferredId); 1298 if (canonicalID != preferredId) { 1299 // Check if the offsets at the given time are identical with the preferred zone 1300 raw = cal.get(UCAL_ZONE_OFFSET, status); 1301 if (U_FAILURE(status)) { 1302 result.remove(); 1303 return result; 1304 } 1305 TimeZone *preferredZone = TimeZone::createTimeZone(preferredId); 1306 int32_t prfRaw, prfSav; 1307 // Check offset in preferred time zone with wall time. 1308 // With getOffset(time, false, preferredOffsets), 1309 // you may get incorrect results because of time overlap at DST->STD 1310 // transition. 1311 preferredZone->getOffset(time + raw + sav, TRUE, prfRaw, prfSav, status); 1312 delete preferredZone; 1313 1314 if (U_FAILURE(status)) { 1315 result.remove(); 1316 return result; 1317 } 1318 if ((raw != prfRaw || sav != prfSav) && zstrings != NULL) { 1319 // Use generic partial location string as fallback 1320 zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result); 1321 } 1322 } 1323 } 1324 } 1325 } 1326 } 1327 if (result.isEmpty()) { 1328 // Use location format as the final fallback 1329 getString(canonicalID, ZSIDX_LOCATION, time, FALSE /*not used*/, result); 1330 } 1331 1332 return result; 1333} 1334 1335UnicodeString& 1336ZoneStringFormat::getGenericPartialLocationString(const UnicodeString &tzid, UBool isShort, 1337 UDate date, UBool commonlyUsedOnly, UnicodeString &result) const { 1338 result.remove(); 1339 if (fTzidToStrings.count() <= 0) { 1340 return result; 1341 } 1342 1343 UnicodeString canonicalID; 1344 UErrorCode status = U_ZERO_ERROR; 1345 TimeZone::getCanonicalID(tzid, canonicalID, status); 1346 if (U_FAILURE(status)) { 1347 // Unknown ID, so no corresponding meta data. 1348 return result; 1349 } 1350 1351 UnicodeString mzid; 1352 ZoneMeta::getMetazoneID(canonicalID, date, mzid); 1353 1354 if (!mzid.isEmpty()) { 1355 ZoneStrings *zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID); 1356 if (zstrings != NULL) { 1357 zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result); 1358 } 1359 } 1360 return result; 1361} 1362 1363const ZoneStringInfo* 1364ZoneStringFormat::find(const UnicodeString &text, int32_t start, int32_t types, 1365 int32_t &matchLength, UErrorCode &status) const { 1366 matchLength = 0; 1367 if (U_FAILURE(status)) { 1368 return NULL; 1369 } 1370 if (fZoneStringsTrie.isEmpty()) { 1371 return NULL; 1372 } 1373 const ZoneStringInfo *result = NULL; 1374 const ZoneStringInfo *fallback = NULL; 1375 int32_t fallbackMatchLen = 0; 1376 1377 ZoneStringSearchResultHandler handler(status); 1378 fZoneStringsTrie.search(text, start, (TextTrieMapSearchResultHandler*)&handler, status); 1379 if (U_SUCCESS(status)) { 1380 int32_t numMatches = handler.countMatches(); 1381 for (int32_t i = 0; i < numMatches; i++) { 1382 int32_t tmpMatchLen; 1383 const ZoneStringInfo *tmp = handler.getMatch(i, tmpMatchLen); 1384 if ((types & tmp->fType) != 0) { 1385 if (result == NULL || matchLength < tmpMatchLen) { 1386 result = tmp; 1387 matchLength = tmpMatchLen; 1388 } else if (matchLength == tmpMatchLen) { 1389 // Tie breaker - there are some examples that a 1390 // long standard name is identical with a location 1391 // name - for example, "Uruguay Time". In this case, 1392 // we interpret it as generic, not specific. 1393 if (tmp->isGeneric() && !result->isGeneric()) { 1394 result = tmp; 1395 } 1396 } 1397 } else if (result == NULL) { 1398 if (fallback == NULL || fallbackMatchLen < tmpMatchLen) { 1399 fallback = tmp; 1400 fallbackMatchLen = tmpMatchLen; 1401 } else if (fallbackMatchLen == tmpMatchLen) { 1402 if (tmp->isGeneric() && !fallback->isGeneric()) { 1403 fallback = tmp; 1404 } 1405 } 1406 } 1407 } 1408 if (result == NULL && fallback != NULL) { 1409 result = fallback; 1410 matchLength = fallbackMatchLen; 1411 } 1412 } 1413 return result; 1414} 1415 1416 1417UnicodeString& 1418ZoneStringFormat::getRegion(UnicodeString ®ion) const { 1419 const char* country = fLocale.getCountry(); 1420 // TODO: Utilize addLikelySubtag in Locale to resolve default region 1421 // when the implementation is ready. 1422 region.setTo(UnicodeString(country, -1, US_INV)); 1423 return region; 1424} 1425 1426MessageFormat* 1427ZoneStringFormat::getFallbackFormat(const Locale &locale, UErrorCode &status) { 1428 if (U_FAILURE(status)) { 1429 return NULL; 1430 } 1431 UnicodeString pattern(TRUE, gDefFallbackPattern, -1); 1432 UResourceBundle *zoneStringsArray = ures_open(NULL, locale.getName(), &status); 1433 zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status); 1434 int32_t len; 1435 const UChar *flbkfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gFallbackFormatTag, &len, &status); 1436 if (U_SUCCESS(status)) { 1437 pattern.setTo(flbkfmt); 1438 } else { 1439 status = U_ZERO_ERROR; 1440 } 1441 ures_close(zoneStringsArray); 1442 1443 MessageFormat *fallbackFmt = new MessageFormat(pattern, status); 1444 return fallbackFmt; 1445} 1446 1447MessageFormat* 1448ZoneStringFormat::getRegionFormat(const Locale& locale, UErrorCode &status) { 1449 if (U_FAILURE(status)) { 1450 return NULL; 1451 } 1452 UnicodeString pattern(TRUE, gDefRegionPattern, -1); 1453 UResourceBundle *zoneStringsArray = ures_open(NULL, locale.getName(), &status); 1454 zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status); 1455 int32_t len; 1456 const UChar *regionfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gRegionFormatTag, &len, &status); 1457 if (U_SUCCESS(status)) { 1458 pattern.setTo(regionfmt); 1459 } else { 1460 status = U_ZERO_ERROR; 1461 } 1462 ures_close(zoneStringsArray); 1463 1464 MessageFormat *regionFmt = new MessageFormat(pattern, status); 1465 return regionFmt; 1466} 1467 1468const UChar* 1469ZoneStringFormat::getZoneStringFromBundle(const UResourceBundle *zoneitem, const char *key) { 1470 const UChar *str = NULL; 1471 if (zoneitem != NULL) { 1472 UErrorCode status = U_ZERO_ERROR; 1473 int32_t len; 1474 str = ures_getStringByKeyWithFallback(zoneitem, key, &len, &status); 1475 if (U_FAILURE(status)) { 1476 str = NULL; 1477 } 1478 } 1479 return str; 1480} 1481 1482UBool 1483ZoneStringFormat::isCommonlyUsed(const UResourceBundle *zoneitem) { 1484 if (zoneitem == NULL) { 1485 return TRUE; 1486 } 1487 1488 UBool commonlyUsed = FALSE; 1489 UErrorCode status = U_ZERO_ERROR; 1490 UResourceBundle *cuRes = ures_getByKey(zoneitem, gCommonlyUsedTag, NULL, &status); 1491 int32_t cuValue = ures_getInt(cuRes, &status); 1492 if (U_SUCCESS(status)) { 1493 if (cuValue == 1) { 1494 commonlyUsed = TRUE; 1495 } 1496 } 1497 ures_close(cuRes); 1498 return commonlyUsed; 1499} 1500 1501UnicodeString& 1502ZoneStringFormat::getLocalizedCountry(const UnicodeString &countryCode, const Locale &locale, UnicodeString &displayCountry) { 1503 // We do not want to use display country names only from the target language bundle 1504 // Note: we should do this in better way. 1505 displayCountry.remove(); 1506 int32_t ccLen = countryCode.length(); 1507 if (ccLen > 0 && ccLen < ULOC_COUNTRY_CAPACITY) { 1508 UErrorCode status = U_ZERO_ERROR; 1509 UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status); 1510 if (U_SUCCESS(status)) { 1511 const char *bundleLocStr = ures_getLocale(localeBundle, &status); 1512 if (U_SUCCESS(status) && uprv_strlen(bundleLocStr) > 0) { 1513 Locale bundleLoc(bundleLocStr); 1514 if (uprv_strcmp(bundleLocStr, "root") != 0 && uprv_strcmp(bundleLoc.getLanguage(), locale.getLanguage()) == 0) { 1515 // Create a fake locale strings 1516 char tmpLocStr[ULOC_COUNTRY_CAPACITY + 3]; 1517 uprv_strcpy(tmpLocStr, "xx_"); 1518 u_UCharsToChars(countryCode.getBuffer(), &tmpLocStr[3], ccLen); 1519 tmpLocStr[3 + ccLen] = 0; 1520 1521 Locale tmpLoc(tmpLocStr); 1522 tmpLoc.getDisplayCountry(locale, displayCountry); 1523 } 1524 } 1525 } 1526 ures_close(localeBundle); 1527 } 1528 if (displayCountry.isEmpty()) { 1529 // Use the country code as the fallback 1530 displayCountry.setTo(countryCode); 1531 } 1532 return displayCountry; 1533} 1534 1535// ---------------------------------------------------------------------------- 1536/* 1537 * This constructor adopts the input UnicodeString arrays. 1538 */ 1539ZoneStrings::ZoneStrings(UnicodeString *strings, int32_t stringsCount, UBool commonlyUsed, 1540 UnicodeString **genericPartialLocationStrings, int32_t genericRowCount, int32_t genericColCount) 1541: fStrings(strings), fStringsCount(stringsCount), fIsCommonlyUsed(commonlyUsed), 1542 fGenericPartialLocationStrings(genericPartialLocationStrings), 1543 fGenericPartialLocationRowCount(genericRowCount), fGenericPartialLocationColCount(genericColCount) { 1544} 1545 1546ZoneStrings::~ZoneStrings() { 1547 if (fStrings != NULL) { 1548 delete[] fStrings; 1549 } 1550 if (fGenericPartialLocationStrings != NULL) { 1551 for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) { 1552 delete[] fGenericPartialLocationStrings[i]; 1553 } 1554 uprv_free(fGenericPartialLocationStrings); 1555 } 1556} 1557 1558 1559UnicodeString& 1560ZoneStrings::getString(int32_t typeIdx, UnicodeString &result) const { 1561 if (typeIdx >= 0 && typeIdx < fStringsCount) { 1562 result.setTo(fStrings[typeIdx]); 1563 } else { 1564 result.remove(); 1565 } 1566 return result; 1567} 1568 1569UnicodeString& 1570ZoneStrings::getGenericPartialLocationString(const UnicodeString &mzid, UBool isShort, 1571 UBool commonlyUsedOnly, UnicodeString &result) const { 1572 UBool isSet = FALSE; 1573 if (fGenericPartialLocationColCount >= 2) { 1574 for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) { 1575 if (fGenericPartialLocationStrings[i][0] == mzid) { 1576 if (isShort) { 1577 if (fGenericPartialLocationColCount >= 3) { 1578 if (!commonlyUsedOnly || 1579 fGenericPartialLocationColCount == 3 || fGenericPartialLocationStrings[i][3].length() != 0) { 1580 result.setTo(fGenericPartialLocationStrings[i][2]); 1581 isSet = TRUE; 1582 } 1583 } 1584 } else { 1585 result.setTo(fGenericPartialLocationStrings[i][1]); 1586 isSet = TRUE; 1587 } 1588 break; 1589 } 1590 } 1591 } 1592 if (!isSet) { 1593 result.remove(); 1594 } 1595 return result; 1596} 1597 1598// -------------------------------------------------------------- 1599SafeZoneStringFormatPtr::SafeZoneStringFormatPtr(ZSFCacheEntry *cacheEntry) 1600: fCacheEntry(cacheEntry) { 1601} 1602 1603SafeZoneStringFormatPtr::~SafeZoneStringFormatPtr() { 1604 fCacheEntry->delRef(); 1605} 1606 1607const ZoneStringFormat* 1608SafeZoneStringFormatPtr::get() const { 1609 return fCacheEntry->getZoneStringFormat(); 1610} 1611 1612ZSFCacheEntry::ZSFCacheEntry(const Locale &locale, ZoneStringFormat *zsf, ZSFCacheEntry *next) 1613: fLocale(locale), fZoneStringFormat(zsf), 1614 fNext(next), fRefCount(1) 1615{ 1616} 1617 1618ZSFCacheEntry::~ZSFCacheEntry () { 1619 delete fZoneStringFormat; 1620} 1621 1622const ZoneStringFormat* 1623ZSFCacheEntry::getZoneStringFormat(void) { 1624 return (const ZoneStringFormat*)fZoneStringFormat; 1625} 1626 1627void 1628ZSFCacheEntry::delRef(void) { 1629 umtx_lock(&gZSFCacheLock); 1630 --fRefCount; 1631 umtx_unlock(&gZSFCacheLock); 1632} 1633 1634ZSFCache::ZSFCache(int32_t capacity) 1635: fCapacity(capacity), fFirst(NULL) { 1636} 1637 1638ZSFCache::~ZSFCache() { 1639 ZSFCacheEntry *entry = fFirst; 1640 while (entry) { 1641 ZSFCacheEntry *next = entry->fNext; 1642 delete entry; 1643 entry = next; 1644 } 1645} 1646 1647SafeZoneStringFormatPtr* 1648ZSFCache::get(const Locale &locale, UErrorCode &status) { 1649 SafeZoneStringFormatPtr *result = NULL; 1650 1651 // Search the cache entry list 1652 ZSFCacheEntry *entry = NULL; 1653 ZSFCacheEntry *next, *prev; 1654 1655 umtx_lock(&gZSFCacheLock); 1656 entry = fFirst; 1657 prev = NULL; 1658 while (entry) { 1659 next = entry->fNext; 1660 if (entry->fLocale == locale) { 1661 // Add reference count 1662 entry->fRefCount++; 1663 1664 // move the entry to the top 1665 if (entry != fFirst) { 1666 prev->fNext = next; 1667 entry->fNext = fFirst; 1668 fFirst = entry; 1669 } 1670 break; 1671 } 1672 prev = entry; 1673 entry = next; 1674 } 1675 umtx_unlock(&gZSFCacheLock); 1676 1677 // Create a new ZoneStringFormat 1678 if (entry == NULL) { 1679 ZoneStringFormat *zsf = new ZoneStringFormat(locale, status); 1680 if (U_FAILURE(status)) { 1681 delete zsf; 1682 return NULL; 1683 } 1684 if (zsf == NULL) { 1685 status = U_MEMORY_ALLOCATION_ERROR; 1686 return NULL; 1687 } 1688 // Now add the new entry 1689 umtx_lock(&gZSFCacheLock); 1690 // Make sure no other threads already created the one for the same locale 1691 entry = fFirst; 1692 prev = NULL; 1693 while (entry) { 1694 next = entry->fNext; 1695 if (entry->fLocale == locale) { 1696 // Add reference count 1697 entry->fRefCount++; 1698 1699 // move the entry to the top 1700 if (entry != fFirst) { 1701 prev->fNext = next; 1702 entry->fNext = fFirst; 1703 fFirst = entry; 1704 } 1705 break; 1706 } 1707 prev = entry; 1708 entry = next; 1709 } 1710 if (entry == NULL) { 1711 // Add the new one to the top 1712 next = fFirst; 1713 entry = new ZSFCacheEntry(locale, zsf, next); 1714 fFirst = entry; 1715 } else { 1716 delete zsf; 1717 } 1718 umtx_unlock(&gZSFCacheLock); 1719 } 1720 1721 result = new SafeZoneStringFormatPtr(entry); 1722 1723 // Now, delete unused cache entries beyond the capacity 1724 umtx_lock(&gZSFCacheLock); 1725 entry = fFirst; 1726 prev = NULL; 1727 int32_t idx = 1; 1728 while (entry) { 1729 next = entry->fNext; 1730 if (idx >= fCapacity && entry->fRefCount == 0) { 1731 if (entry == fFirst) { 1732 fFirst = next; 1733 } else { 1734 prev->fNext = next; 1735 } 1736 delete entry; 1737 } else { 1738 prev = entry; 1739 } 1740 entry = next; 1741 idx++; 1742 } 1743 umtx_unlock(&gZSFCacheLock); 1744 1745 return result; 1746} 1747 1748U_NAMESPACE_END 1749 1750#endif /* #if !UCONFIG_NO_FORMATTING */ 1751