OLD | NEW |
(Empty) | |
| 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, 0x7
B, 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_CH
ARACTERNODE_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 t
he |
| 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 ti
me 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, UErro
rCode &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(CharacterN
ode)); |
| 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 firs
t call to search(). |
| 373 static UMTX TextTrieMutex; |
| 374 |
| 375 // buildTrie() - The Trie node structure is needed. Create it from the data tha
t was |
| 376 // saved at the time the ZoneStringFormatter was created. The Tri
e is only |
| 377 // needed for parsing operations, which are less common than forma
tting, |
| 378 // and the Trie is big, which is why its creation is deferred unti
l 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 c
onstructor. |
| 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) c
onst { |
| 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 star
t, |
| 412 int32_t index, TextTrieMapSearchResultHandler *handler, UError
Code &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 CharacterN
ode *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)] = matc
hLength; |
| 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 ZoneStrin
gFormat structures. |
| 530 static UMTX ZoneStringFormatMutex; |
| 531 |
| 532 |
| 533 // ---------------------------------------------------------------------------- |
| 534 ZoneStringFormat::ZoneStringFormat(const UnicodeString* const* strings, |
| 535 int32_t rowCount, int32_t columnCount, UError
Code &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 functi
on |
| 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((TimeZoneTranslati
onTypeIndex)typeIdx); |
| 602 ZoneStringInfo *zsinf = new ZoneStringInfo(strings[row][0], |
| 603 strings[row][col]
, |
| 604 (TimeZoneTranslat
ionType)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], stat
us)); |
| 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 functi
on |
| 650 &status); |
| 651 fMzidToStrings = uhash_open(uhash_hashUChars, // key hash function |
| 652 uhash_compareUChars, // key comparison function |
| 653 NULL, // Value comparison functi
on |
| 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 tzi
d 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(fTzidToS
trings, 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(fZoneI
tem, gLongStandardTag); |
| 775 zstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(fZoneI
tem, gShortStandardTag); |
| 776 zstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(fZoneI
tem, gLongDaylightTag); |
| 777 zstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(fZoneI
tem, gShortDaylightTag); |
| 778 zstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(fZoneI
tem, gLongGenericTag); |
| 779 zstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(fZoneI
tem, 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, s
tatus); |
| 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, sta
tus); |
| 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 l
ocation, 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 as
sociated 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 lo
cation string based on |
| 831 // the ID. This implementation supports canonic
al time zone round trip |
| 832 // with format pattern "VVVV". See #6602 for th
e 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 t
z 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, f
pos, status); |
| 847 if (U_FAILURE(status)) { |
| 848 goto error_cleanup; |
| 849 } |
| 850 zstrarray[ZSIDX_LOCATION] = location.get
TerminatedBuffer(); |
| 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(fMzidToStr
ings, mzid.getTerminatedBuffer()); |
| 867 if (mzStrings == NULL) { |
| 868 // If the metazone strings are not yet p
rocessed, do it now. |
| 869 char mzidkey[ZID_KEY_MAX]; |
| 870 uprv_strcpy(mzidkey, gMetazoneIdPrefix); |
| 871 u_UCharsToChars(mzmap->mzid, mzidkey + M
ZID_PREFIX_LEN, u_strlen(mzmap->mzid) + 1); |
| 872 fMetazoneItem = ures_getByKeyWithFallbac
k(fZoneStringsArray, mzidkey, fMetazoneItem, &status); |
| 873 if (U_FAILURE(status)) { |
| 874 // No resources available for th
is metazone |
| 875 // Resource bundle will be clean
ed up after end of the loop. |
| 876 status = U_ZERO_ERROR; |
| 877 continue; |
| 878 } |
| 879 UBool mzCommonlyUsed = isCommonlyUsed(fM
etazoneItem); |
| 880 mzstrarray[ZSIDX_LONG_STANDARD] = getZon
eStringFromBundle(fMetazoneItem, gLongStandardTag); |
| 881 mzstrarray[ZSIDX_SHORT_STANDARD] = getZo
neStringFromBundle(fMetazoneItem, gShortStandardTag); |
| 882 mzstrarray[ZSIDX_LONG_DAYLIGHT] = getZon
eStringFromBundle(fMetazoneItem, gLongDaylightTag); |
| 883 mzstrarray[ZSIDX_SHORT_DAYLIGHT] = getZo
neStringFromBundle(fMetazoneItem, gShortDaylightTag); |
| 884 mzstrarray[ZSIDX_LONG_GENERIC] = getZone
StringFromBundle(fMetazoneItem, gLongGenericTag); |
| 885 mzstrarray[ZSIDX_SHORT_GENERIC] = getZon
eStringFromBundle(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 an
d put strings to the zone string trie |
| 899 strings_mz = new UnicodeString[l
astNonNullIdx + 1]; |
| 900 |
| 901 UnicodeString preferredIdForLoca
le; |
| 902 ZoneMeta::getZoneIdByMetazone(mz
id, region, preferredIdForLocale); |
| 903 |
| 904 for (int32_t typeidx = 0; typeid
x <= lastNonNullIdx; typeidx++) { |
| 905 if (mzstrarray[typeidx]
!= NULL) { |
| 906 strings_mz[typei
dx].setTo(TRUE, mzstrarray[typeidx], -1); |
| 907 |
| 908 // Add a metazon
e string to the zone string trie |
| 909 int32_t type = g
etTimeZoneTranslationType((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(st
atus)) { |
| 918 delete [
]strings_mz; |
| 919 goto err
or_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 *>(fStri
ngPool.get(mzid, status)); |
| 932 uhash_put(fMzidToStrings, umzid, tmp_mzS
trings, &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 addMzPartialLocationName
s = FALSE; |
| 953 break; |
| 954 } |
| 955 } |
| 956 if (addMzPartialLocationNames) { |
| 957 UnicodeString *locationPart = NU
LL; |
| 958 // Check if the zone is the pref
erred zone for the territory associated with the zone |
| 959 UnicodeString preferredID; |
| 960 ZoneMeta::getZoneIdByMetazone(mz
id, countryCode, preferredID); |
| 961 if (utzid == preferredID) { |
| 962 // Use country for the l
ocation |
| 963 locationPart = &country; |
| 964 } else { |
| 965 // Use city for the loca
tion |
| 966 locationPart = &city; |
| 967 } |
| 968 // Reset the partial location st
ring 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 para
ms [] = { |
| 978 Formatta
ble(*locationPart), |
| 979 Formatta
ble(lg) |
| 980 }; |
| 981 fallbackFmt->for
mat(params, 2, mzPartialLoc[mzPartialLocIdx][1], fpos, status); |
| 982 } |
| 983 if (!sg.isEmpty()) { |
| 984 Formattable para
ms [] = { |
| 985 Formatta
ble(*locationPart), |
| 986 Formatta
ble(sg) |
| 987 }; |
| 988 fallbackFmt->for
mat(params, 2, mzPartialLoc[mzPartialLocIdx][2], fpos, status); |
| 989 if (mzStrings->i
sShortFormatCommonlyUsed()) { |
| 990 mzPartia
lLoc[mzPartialLocIdx][3].setTo(TRUE, gCommonlyUsedTrue, -1); |
| 991 } |
| 992 } |
| 993 if (U_FAILURE(status)) { |
| 994 goto error_clean
up; |
| 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 = getTimeZoneTranslationTyp
e((TimeZoneTranslationTypeIndex)i); |
| 1023 ZoneStringInfo *zsinfo = new ZoneStringI
nfo(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 strin
gs |
| 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(genericPar
tialRowCount * 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 UnicodeStri
ng[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) &&!genericPartial
LocationNames[i][j].isEmpty()) { |
| 1057 ZoneStringInfo *zsinfo; |
| 1058 TimeZoneTranslationType type = (
j == 1) ? GENERIC_LONG : GENERIC_SHORT; |
| 1059 zsinfo = new ZoneStringInfo(utzi
d, genericPartialLocationNames[i][j], type, |
| 1060
fStringPool, status); |
| 1061 fZoneStringsTrie.put(genericPart
ialLocationNames[i][j], zsinfo, fStringPool, status); |
| 1062 if (U_FAILURE(status)) { |
| 1063 delete[] genericPartialL
ocationNames[i]; |
| 1064 uprv_free(genericPartial
LocationNames); |
| 1065 delete[] strings; |
| 1066 goto error_cleanup; |
| 1067 } |
| 1068 } |
| 1069 } |
| 1070 } |
| 1071 } |
| 1072 |
| 1073 // Finally, create ZoneStrings instance and put it into the tzid
ToStinrgs map |
| 1074 ZoneStrings *zstrings = new ZoneStrings(strings, stringsCount, c
ommonlyUsed, |
| 1075
genericPartialLocationNames, genericPartialRowCount, |
| 1076
genericPartialColCount, fStringPool, status); |
| 1077 |
| 1078 UChar *uutzid = const_cast<UChar *>(fStringPool.get(utzid, statu
s)); |
| 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 tzi
d 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(UnicodeSt
ring*)); |
| 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 &resu
lt, |
| 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*/, re
sult); |
| 1267 } else { |
| 1268 return getString(tzid, ZSIDX_LONG_DAYLIGHT, date, FALSE /*not used*/, re
sult); |
| 1269 } |
| 1270 } |
| 1271 |
| 1272 UnicodeString& |
| 1273 ZoneStringFormat::getSpecificShortString(const Calendar &cal, UBool commonlyUsed
Only, |
| 1274 UnicodeString &result, UErrorCode &stat
us) 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, res
ult); |
| 1284 } else { |
| 1285 return getString(tzid, ZSIDX_SHORT_DAYLIGHT, date, commonlyUsedOnly, res
ult); |
| 1286 } |
| 1287 } |
| 1288 |
| 1289 UnicodeString& |
| 1290 ZoneStringFormat::getGenericLongString(const Calendar &cal, UnicodeString &resul
t, |
| 1291 UErrorCode &status) const { |
| 1292 return getGenericString(cal, FALSE /*long*/, FALSE /* not used */, result, s
tatus); |
| 1293 } |
| 1294 |
| 1295 UnicodeString& |
| 1296 ZoneStringFormat::getGenericShortString(const Calendar &cal, UBool commonlyUsedO
nly, |
| 1297 UnicodeString &result, UErrorCode &statu
s) const { |
| 1298 return getGenericString(cal, TRUE /*short*/, commonlyUsedOnly, result, statu
s); |
| 1299 } |
| 1300 |
| 1301 UnicodeString& |
| 1302 ZoneStringFormat::getGenericLocationString(const Calendar &cal, UnicodeString &r
esult, |
| 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) con
st { |
| 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) co
nst { |
| 1319 return find(text, start, STANDARD_SHORT | DAYLIGHT_SHORT, matchLength, statu
s); |
| 1320 } |
| 1321 |
| 1322 const ZoneStringInfo* |
| 1323 ZoneStringFormat::findGenericLong(const UnicodeString &text, int32_t start, |
| 1324 int32_t &matchLength, UErrorCode &status) cons
t { |
| 1325 return find(text, start, GENERIC_LONG | STANDARD_LONG | LOCATION, matchLengt
h, status); |
| 1326 } |
| 1327 |
| 1328 const ZoneStringInfo* |
| 1329 ZoneStringFormat::findGenericShort(const UnicodeString &text, int32_t start, |
| 1330 int32_t &matchLength, UErrorCode &status) con
st { |
| 1331 return find(text, start, GENERIC_SHORT | STANDARD_SHORT | LOCATION, matchLen
gth, 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, TimeZoneTranslationTypeIn
dex 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, canonica
lID.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, mzi
d.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->isShortFormatCommonl
yUsed()) { |
| 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 com
monlyUsedOnly, |
| 1415 UnicodeString &result, UErrorCode &status) co
nst { |
| 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.getTermin
atedBuffer()); |
| 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 t
he 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, befo
re); |
| 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, aft
er); |
| 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 IC
U's implementation. |
| 1494 // We may get a wrong answer in edge case, but it should pra
ctically work OK. |
| 1495 tmptz->getOffset(time - kDstCheckRange, FALSE, raw, sav, sta
tus); |
| 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_L
ONG_STANDARD), |
| 1514 time, commonlyUsedOnly, result); |
| 1515 |
| 1516 // Note: |
| 1517 // In CLDR 1.5.1, a same localization is used for both generic a
nd standard |
| 1518 // for some metazones in some locales. This is actually data bu
gs 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 : ZSID
X_LONG_GENERIC), |
| 1524 time, commonlyUsedOnly, genericNonLocation); |
| 1525 if (!genericNonLocation.isEmpty() && result == genericNonLoc
ation) { |
| 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->isShortFormatCommonl
yUsed()) { |
| 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 prefer
red zone's offsets |
| 1543 UnicodeString preferredId; |
| 1544 UnicodeString region; |
| 1545 ZoneMeta::getZoneIdByMetazone(mzid, getRegion(region), prefe
rredId); |
| 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(prefe
rredId); |
| 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, isSh
ort, 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, UBo
ol isShort, |
| 1585 UDate date, UBool commonlyUsed
Only, 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, canonica
lID.getTerminatedBuffer()); |
| 1610 if (zstrings != NULL) { |
| 1611 zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUse
dOnly, 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 type
s, |
| 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*)&handl
er, 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, gZoneStringsT
ag, zoneStringsArray, &status); |
| 1731 int32_t len; |
| 1732 const UChar *flbkfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gFa
llbackFormatTag, &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, gZoneStringsT
ag, zoneStringsArray, &status); |
| 1752 int32_t len; |
| 1753 const UChar *regionfmt = ures_getStringByKeyWithFallback(zoneStringsArray, g
RegionFormatTag, &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, &st
atus); |
| 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 Lo
cale &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(), &statu
s); |
| 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], ccLe
n); |
| 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 **) * stringsC
ount); |
| 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 ***) * genericRowCou
nt); |
| 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 **) * genericColC
ount); |
| 1879 if (fGenericPartialLocationStrings[i] == NULL) { |
| 1880 status = U_MEMORY_ALLOCATION_ERROR; |
| 1881 continue; // Continue so that fGenericPartialLocationStrings w
ill 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 is
Short, |
| 1916 UBool commonlyUsedOnly, UnicodeString &r
esult) 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, ZSFCac
heEntry *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 local
e |
| 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 s
ame string. |
| 2100 */ |
| 2101 |
| 2102 static const int32_t POOL_CHUNK_SIZE = 2000; |
| 2103 struct ZSFStringPoolChunk: public UMemory { |
| 2104 ZSFStringPoolChunk *fNext; // Ptr to next pool chun
k |
| 2105 int32_t fLimit; // Index to start of unu
sed 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 str
ing data |
| 2190 // into the pool's storage. Used for strings from res
ource 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 */ |
OLD | NEW |