tmutfmt.cpp (32427B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2008-2015, Google, International Business Machines Corporation 6 * and others. All Rights Reserved. 7 ******************************************************************************* 8 */ 9 10 #include "unicode/tmutfmt.h" 11 12 #if !UCONFIG_NO_FORMATTING 13 14 #include <utility> 15 16 #include "unicode/decimfmt.h" 17 #include "unicode/localpointer.h" 18 #include "plurrule_impl.h" 19 #include "uvector.h" 20 #include "charstr.h" 21 #include "cmemory.h" 22 #include "cstring.h" 23 #include "hash.h" 24 #include "ulocimp.h" 25 #include "uresimp.h" 26 #include "ureslocs.h" 27 #include "unicode/msgfmt.h" 28 #include "uassert.h" 29 30 #define LEFT_CURLY_BRACKET ((char16_t)0x007B) 31 #define RIGHT_CURLY_BRACKET ((char16_t)0x007D) 32 #define SPACE ((char16_t)0x0020) 33 #define DIGIT_ZERO ((char16_t)0x0030) 34 #define LOW_S ((char16_t)0x0073) 35 #define LOW_M ((char16_t)0x006D) 36 #define LOW_I ((char16_t)0x0069) 37 #define LOW_N ((char16_t)0x006E) 38 #define LOW_H ((char16_t)0x0068) 39 #define LOW_W ((char16_t)0x0077) 40 #define LOW_D ((char16_t)0x0064) 41 #define LOW_Y ((char16_t)0x0079) 42 #define LOW_Z ((char16_t)0x007A) 43 #define LOW_E ((char16_t)0x0065) 44 #define LOW_R ((char16_t)0x0072) 45 #define LOW_O ((char16_t)0x006F) 46 #define LOW_N ((char16_t)0x006E) 47 #define LOW_T ((char16_t)0x0074) 48 49 50 //TODO: define in compile time 51 //#define TMUTFMT_DEBUG 1 52 53 #ifdef TMUTFMT_DEBUG 54 #include <iostream> 55 #endif 56 57 U_NAMESPACE_BEGIN 58 59 60 61 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat) 62 63 static const char gUnitsTag[] = "units"; 64 static const char gShortUnitsTag[] = "unitsShort"; 65 static const char gTimeUnitYear[] = "year"; 66 static const char gTimeUnitMonth[] = "month"; 67 static const char gTimeUnitDay[] = "day"; 68 static const char gTimeUnitWeek[] = "week"; 69 static const char gTimeUnitHour[] = "hour"; 70 static const char gTimeUnitMinute[] = "minute"; 71 static const char gTimeUnitSecond[] = "second"; 72 static const char gPluralCountOther[] = "other"; 73 74 static const char16_t DEFAULT_PATTERN_FOR_SECOND[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_S, 0}; 75 static const char16_t DEFAULT_PATTERN_FOR_MINUTE[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, LOW_I, LOW_N, 0}; 76 static const char16_t DEFAULT_PATTERN_FOR_HOUR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_H, 0}; 77 static const char16_t DEFAULT_PATTERN_FOR_WEEK[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_W, 0}; 78 static const char16_t DEFAULT_PATTERN_FOR_DAY[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_D, 0}; 79 static const char16_t DEFAULT_PATTERN_FOR_MONTH[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, 0}; 80 static const char16_t DEFAULT_PATTERN_FOR_YEAR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_Y, 0}; 81 82 static const char16_t PLURAL_COUNT_ZERO[] = {LOW_Z, LOW_E, LOW_R, LOW_O, 0}; 83 static const char16_t PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0}; 84 static const char16_t PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0}; 85 86 TimeUnitFormat::TimeUnitFormat(UErrorCode& status) { 87 initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE, nullptr, status); 88 create(UTMUTFMT_FULL_STYLE, status); 89 } 90 91 92 TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) { 93 initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, nullptr, status); 94 create(UTMUTFMT_FULL_STYLE, status); 95 } 96 97 98 TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) { 99 switch (style) { 100 case UTMUTFMT_FULL_STYLE: 101 initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, nullptr, status); 102 break; 103 case UTMUTFMT_ABBREVIATED_STYLE: 104 initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, nullptr, status); 105 break; 106 default: 107 initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, nullptr, status); 108 break; 109 } 110 create(style, status); 111 } 112 113 TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other) 114 : MeasureFormat(other), 115 fStyle(other.fStyle) 116 { 117 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 118 i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 119 i = static_cast<TimeUnit::UTimeUnitFields>(i + 1)) { 120 UErrorCode status = U_ZERO_ERROR; 121 fTimeUnitToCountToPatterns[i] = initHash(status); 122 if (U_SUCCESS(status)) { 123 copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status); 124 } else { 125 delete fTimeUnitToCountToPatterns[i]; 126 fTimeUnitToCountToPatterns[i] = nullptr; 127 } 128 } 129 } 130 131 132 TimeUnitFormat::~TimeUnitFormat() { 133 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 134 i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 135 i = static_cast<TimeUnit::UTimeUnitFields>(i + 1)) { 136 deleteHash(fTimeUnitToCountToPatterns[i]); 137 fTimeUnitToCountToPatterns[i] = nullptr; 138 } 139 } 140 141 142 TimeUnitFormat* 143 TimeUnitFormat::clone() const { 144 return new TimeUnitFormat(*this); 145 } 146 147 148 TimeUnitFormat& 149 TimeUnitFormat::operator=(const TimeUnitFormat& other) { 150 if (this == &other) { 151 return *this; 152 } 153 MeasureFormat::operator=(other); 154 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 155 i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 156 i = static_cast<TimeUnit::UTimeUnitFields>(i + 1)) { 157 deleteHash(fTimeUnitToCountToPatterns[i]); 158 fTimeUnitToCountToPatterns[i] = nullptr; 159 } 160 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 161 i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 162 i = static_cast<TimeUnit::UTimeUnitFields>(i + 1)) { 163 UErrorCode status = U_ZERO_ERROR; 164 fTimeUnitToCountToPatterns[i] = initHash(status); 165 if (U_SUCCESS(status)) { 166 copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status); 167 } else { 168 delete fTimeUnitToCountToPatterns[i]; 169 fTimeUnitToCountToPatterns[i] = nullptr; 170 } 171 } 172 fStyle = other.fStyle; 173 return *this; 174 } 175 176 void 177 TimeUnitFormat::parseObject(const UnicodeString& source, 178 Formattable& result, 179 ParsePosition& pos) const { 180 Formattable resultNumber(0.0); 181 UBool withNumberFormat = false; 182 TimeUnit::UTimeUnitFields resultTimeUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT; 183 int32_t oldPos = pos.getIndex(); 184 int32_t newPos = -1; 185 int32_t longestParseDistance = 0; 186 UnicodeString* countOfLongestMatch = nullptr; 187 #ifdef TMUTFMT_DEBUG 188 char res[1000]; 189 source.extract(0, source.length(), res, "UTF-8"); 190 std::cout << "parse source: " << res << "\n"; 191 #endif 192 // parse by iterating through all available patterns 193 // and looking for the longest match. 194 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 195 i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 196 i = static_cast<TimeUnit::UTimeUnitFields>(i + 1)) { 197 Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i]; 198 int32_t elemPos = UHASH_FIRST; 199 const UHashElement* elem = nullptr; 200 while ((elem = countToPatterns->nextElement(elemPos)) != nullptr){ 201 const UHashTok keyTok = elem->key; 202 UnicodeString* count = static_cast<UnicodeString*>(keyTok.pointer); 203 #ifdef TMUTFMT_DEBUG 204 count->extract(0, count->length(), res, "UTF-8"); 205 std::cout << "parse plural count: " << res << "\n"; 206 #endif 207 const UHashTok valueTok = elem->value; 208 // the value is a pair of MessageFormat* 209 MessageFormat** patterns = static_cast<MessageFormat**>(valueTok.pointer); 210 for (UTimeUnitFormatStyle style = UTMUTFMT_FULL_STYLE; style < UTMUTFMT_FORMAT_STYLE_COUNT; 211 style = static_cast<UTimeUnitFormatStyle>(style + 1)) { 212 MessageFormat* pattern = patterns[style]; 213 pos.setErrorIndex(-1); 214 pos.setIndex(oldPos); 215 // see if we can parse 216 Formattable parsed; 217 pattern->parseObject(source, parsed, pos); 218 if (pos.getErrorIndex() != -1 || pos.getIndex() == oldPos) { 219 continue; 220 } 221 #ifdef TMUTFMT_DEBUG 222 std::cout << "parsed.getType: " << parsed.getType() << "\n"; 223 #endif 224 Formattable tmpNumber(0.0); 225 if (pattern->getArgTypeCount() != 0) { 226 Formattable& temp = parsed[0]; 227 if (temp.getType() == Formattable::kString) { 228 UnicodeString tmpString; 229 UErrorCode pStatus = U_ZERO_ERROR; 230 getNumberFormatInternal().parse(temp.getString(tmpString), tmpNumber, pStatus); 231 if (U_FAILURE(pStatus)) { 232 continue; 233 } 234 } else if (temp.isNumeric()) { 235 tmpNumber = temp; 236 } else { 237 continue; 238 } 239 } 240 int32_t parseDistance = pos.getIndex() - oldPos; 241 if (parseDistance > longestParseDistance) { 242 if (pattern->getArgTypeCount() != 0) { 243 resultNumber = tmpNumber; 244 withNumberFormat = true; 245 } else { 246 withNumberFormat = false; 247 } 248 resultTimeUnit = i; 249 newPos = pos.getIndex(); 250 longestParseDistance = parseDistance; 251 countOfLongestMatch = count; 252 } 253 } 254 } 255 } 256 /* After find the longest match, parse the number. 257 * Result number could be null for the pattern without number pattern. 258 * such as unit pattern in Arabic. 259 * When result number is null, use plural rule to set the number. 260 */ 261 if (withNumberFormat == false && longestParseDistance != 0) { 262 // set the number using plurrual count 263 if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ZERO, 4)) { 264 resultNumber = Formattable(0.0); 265 } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ONE, 3)) { 266 resultNumber = Formattable(1.0); 267 } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_TWO, 3)) { 268 resultNumber = Formattable(2.0); 269 } else { 270 // should not happen. 271 // TODO: how to handle? 272 resultNumber = Formattable(3.0); 273 } 274 } 275 if (longestParseDistance == 0) { 276 pos.setIndex(oldPos); 277 pos.setErrorIndex(0); 278 } else { 279 UErrorCode status = U_ZERO_ERROR; 280 LocalPointer<TimeUnitAmount> tmutamt(new TimeUnitAmount(resultNumber, resultTimeUnit, status), status); 281 if (U_SUCCESS(status)) { 282 result.adoptObject(tmutamt.orphan()); 283 pos.setIndex(newPos); 284 pos.setErrorIndex(-1); 285 } else { 286 pos.setIndex(oldPos); 287 pos.setErrorIndex(0); 288 } 289 } 290 } 291 292 void 293 TimeUnitFormat::create(UTimeUnitFormatStyle style, UErrorCode& status) { 294 // fTimeUnitToCountToPatterns[] must have its elements initialized to nullptr first 295 // before checking for failure status. 296 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 297 i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 298 i = static_cast<TimeUnit::UTimeUnitFields>(i + 1)) { 299 fTimeUnitToCountToPatterns[i] = nullptr; 300 } 301 302 if (U_FAILURE(status)) { 303 return; 304 } 305 if (style < UTMUTFMT_FULL_STYLE || style >= UTMUTFMT_FORMAT_STYLE_COUNT) { 306 status = U_ILLEGAL_ARGUMENT_ERROR; 307 return; 308 } 309 fStyle = style; 310 311 //TODO: format() and parseObj() are const member functions, 312 //so, can not do lazy initialization in C++. 313 //setup has to be done in constructors. 314 //and here, the behavior is not consistent with Java. 315 //In Java, create an empty instance does not setup locale as 316 //default locale. If it followed by setNumberFormat(), 317 //in format(), the locale will set up as the locale in fNumberFormat. 318 //But in C++, this sets the locale as the default locale. 319 setup(status); 320 } 321 322 void 323 TimeUnitFormat::setup(UErrorCode& err) { 324 initDataMembers(err); 325 326 UVector pluralCounts(nullptr, uhash_compareUnicodeString, 6, err); 327 LocalPointer<StringEnumeration> keywords(getPluralRules().getKeywords(err), err); 328 if (U_FAILURE(err)) { 329 return; 330 } 331 UnicodeString* pluralCount; 332 while ((pluralCount = const_cast<UnicodeString*>(keywords->snext(err))) != nullptr) { 333 pluralCounts.addElement(pluralCount, err); 334 } 335 readFromCurrentLocale(UTMUTFMT_FULL_STYLE, gUnitsTag, pluralCounts, err); 336 checkConsistency(UTMUTFMT_FULL_STYLE, gUnitsTag, err); 337 readFromCurrentLocale(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, pluralCounts, err); 338 checkConsistency(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, err); 339 } 340 341 342 void 343 TimeUnitFormat::initDataMembers(UErrorCode& err){ 344 if (U_FAILURE(err)) { 345 return; 346 } 347 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 348 i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 349 i = static_cast<TimeUnit::UTimeUnitFields>(i + 1)) { 350 deleteHash(fTimeUnitToCountToPatterns[i]); 351 fTimeUnitToCountToPatterns[i] = nullptr; 352 } 353 } 354 355 struct TimeUnitFormatReadSink : public ResourceSink { 356 TimeUnitFormat *timeUnitFormatObj; 357 const UVector &pluralCounts; 358 UTimeUnitFormatStyle style; 359 UBool beenHere; 360 361 TimeUnitFormatReadSink(TimeUnitFormat *timeUnitFormatObj, 362 const UVector &pluralCounts, UTimeUnitFormatStyle style) : 363 timeUnitFormatObj(timeUnitFormatObj), pluralCounts(pluralCounts), 364 style(style), beenHere(false){} 365 366 virtual ~TimeUnitFormatReadSink(); 367 368 virtual void put(const char *key, ResourceValue &value, UBool, UErrorCode &errorCode) override { 369 // Skip all put() calls except the first one -- discard all fallback data. 370 if (beenHere) { 371 return; 372 } else { 373 beenHere = true; 374 } 375 376 ResourceTable units = value.getTable(errorCode); 377 if (U_FAILURE(errorCode)) { return; } 378 379 for (int32_t i = 0; units.getKeyAndValue(i, key, value); ++i) { 380 const char* timeUnitName = key; 381 if (timeUnitName == nullptr) { 382 continue; 383 } 384 385 TimeUnit::UTimeUnitFields timeUnitField = TimeUnit::UTIMEUNIT_FIELD_COUNT; 386 if ( uprv_strcmp(timeUnitName, gTimeUnitYear) == 0 ) { 387 timeUnitField = TimeUnit::UTIMEUNIT_YEAR; 388 } else if ( uprv_strcmp(timeUnitName, gTimeUnitMonth) == 0 ) { 389 timeUnitField = TimeUnit::UTIMEUNIT_MONTH; 390 } else if ( uprv_strcmp(timeUnitName, gTimeUnitDay) == 0 ) { 391 timeUnitField = TimeUnit::UTIMEUNIT_DAY; 392 } else if ( uprv_strcmp(timeUnitName, gTimeUnitHour) == 0 ) { 393 timeUnitField = TimeUnit::UTIMEUNIT_HOUR; 394 } else if ( uprv_strcmp(timeUnitName, gTimeUnitMinute) == 0 ) { 395 timeUnitField = TimeUnit::UTIMEUNIT_MINUTE; 396 } else if ( uprv_strcmp(timeUnitName, gTimeUnitSecond) == 0 ) { 397 timeUnitField = TimeUnit::UTIMEUNIT_SECOND; 398 } else if ( uprv_strcmp(timeUnitName, gTimeUnitWeek) == 0 ) { 399 timeUnitField = TimeUnit::UTIMEUNIT_WEEK; 400 } else { 401 continue; 402 } 403 LocalPointer<Hashtable> localCountToPatterns; 404 Hashtable *countToPatterns = 405 timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField]; 406 if (countToPatterns == nullptr) { 407 localCountToPatterns.adoptInsteadAndCheckErrorCode( 408 timeUnitFormatObj->initHash(errorCode), errorCode); 409 countToPatterns = localCountToPatterns.getAlias(); 410 if (U_FAILURE(errorCode)) { 411 return; 412 } 413 } 414 415 ResourceTable countsToPatternTable = value.getTable(errorCode); 416 if (U_FAILURE(errorCode)) { 417 continue; 418 } 419 for (int32_t j = 0; countsToPatternTable.getKeyAndValue(j, key, value); ++j) { 420 errorCode = U_ZERO_ERROR; 421 UnicodeString pattern = value.getUnicodeString(errorCode); 422 if (U_FAILURE(errorCode)) { 423 continue; 424 } 425 UnicodeString pluralCountUniStr(key, -1, US_INV); 426 if (!pluralCounts.contains(&pluralCountUniStr)) { 427 continue; 428 } 429 LocalPointer<MessageFormat> messageFormat(new MessageFormat( 430 pattern, timeUnitFormatObj->getLocale(errorCode), errorCode), errorCode); 431 if (U_FAILURE(errorCode)) { 432 return; 433 } 434 MessageFormat** formatters = 435 static_cast<MessageFormat**>(countToPatterns->get(pluralCountUniStr)); 436 if (formatters == nullptr) { 437 LocalMemory<MessageFormat *> localFormatters( 438 static_cast<MessageFormat**>(uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT * sizeof(MessageFormat*)))); 439 if (localFormatters.isNull()) { 440 errorCode = U_MEMORY_ALLOCATION_ERROR; 441 return; 442 } 443 localFormatters[UTMUTFMT_FULL_STYLE] = nullptr; 444 localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = nullptr; 445 countToPatterns->put(pluralCountUniStr, localFormatters.getAlias(), errorCode); 446 if (U_FAILURE(errorCode)) { 447 return; 448 } 449 formatters = localFormatters.orphan(); 450 } 451 formatters[style] = messageFormat.orphan(); 452 } 453 454 if (timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] == nullptr) { 455 timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] = localCountToPatterns.orphan(); 456 } 457 } 458 } 459 460 }; 461 462 TimeUnitFormatReadSink::~TimeUnitFormatReadSink() {} 463 464 void 465 TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key, 466 const UVector& pluralCounts, UErrorCode& err) { 467 if (U_FAILURE(err)) { 468 return; 469 } 470 // fill timeUnitToCountToPatterns from resource file 471 // err is used to indicate wrong status except missing resource. 472 // status is an error code used in resource lookup. 473 // status does not affect "err". 474 UErrorCode status = U_ZERO_ERROR; 475 LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_UNIT, getLocaleID(status), &status)); 476 477 LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, nullptr, &status)); 478 ures_getByKey(unitsRes.getAlias(), "duration", unitsRes.getAlias(), &status); 479 if (U_FAILURE(status)) { 480 return; 481 } 482 483 TimeUnitFormatReadSink sink(this, pluralCounts, style); 484 ures_getAllItemsWithFallback(unitsRes.getAlias(), "", sink, status); 485 } 486 487 void 488 TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style, const char* key, UErrorCode& err) { 489 if (U_FAILURE(err)) { 490 return; 491 } 492 // there should be patterns for each plural rule in each time unit. 493 // For each time unit, 494 // for each plural rule, following is unit pattern fall-back rule: 495 // ( for example: "one" hour ) 496 // look for its unit pattern in its locale tree. 497 // if pattern is not found in its own locale, such as de_DE, 498 // look for the pattern in its parent, such as de, 499 // keep looking till found or till root. 500 // if the pattern is not found in root either, 501 // fallback to plural count "other", 502 // look for the pattern of "other" in the locale tree: 503 // "de_DE" to "de" to "root". 504 // If not found, fall back to value of 505 // static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h". 506 // 507 // Following is consistency check to create pattern for each 508 // plural rule in each time unit using above fall-back rule. 509 // 510 LocalPointer<StringEnumeration> keywords( 511 getPluralRules().getKeywords(err), err); 512 const UnicodeString* pluralCount; 513 while (U_SUCCESS(err) && (pluralCount = keywords->snext(err)) != nullptr) { 514 for (int32_t i = 0; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; ++i) { 515 // for each time unit, 516 // get all the patterns for each plural rule in this locale. 517 Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i]; 518 if ( countToPatterns == nullptr ) { 519 fTimeUnitToCountToPatterns[i] = countToPatterns = initHash(err); 520 if (U_FAILURE(err)) { 521 return; 522 } 523 } 524 MessageFormat** formatters = static_cast<MessageFormat**>(countToPatterns->get(*pluralCount)); 525 if( formatters == nullptr || formatters[style] == nullptr ) { 526 // look through parents 527 const char* localeName = getLocaleID(err); 528 CharString pluralCountChars; 529 pluralCountChars.appendInvariantChars(*pluralCount, err); 530 searchInLocaleChain(style, key, localeName, 531 static_cast<TimeUnit::UTimeUnitFields>(i), 532 *pluralCount, pluralCountChars.data(), 533 countToPatterns, err); 534 } 535 // TODO: what to do with U_FAILURE(err) at this point. 536 // As is, the outer loop continues to run, but does nothing. 537 } 538 } 539 } 540 541 542 543 // srcPluralCount is the original plural count on which the pattern is 544 // searched for. 545 // searchPluralCount is the fallback plural count. 546 // For example, to search for pattern for ""one" hour", 547 // "one" is the srcPluralCount, 548 // if the pattern is not found even in root, fallback to 549 // using patterns of plural count "other", 550 // then, "other" is the searchPluralCount. 551 void 552 TimeUnitFormat::searchInLocaleChain(UTimeUnitFormatStyle style, const char* key, const char* localeName, 553 TimeUnit::UTimeUnitFields srcTimeUnitField, 554 const UnicodeString& srcPluralCount, 555 const char* searchPluralCount, 556 Hashtable* countToPatterns, 557 UErrorCode& err) { 558 if (U_FAILURE(err)) { 559 return; 560 } 561 UErrorCode status = U_ZERO_ERROR; 562 CharString parentLocale(localeName, status); 563 U_ASSERT(countToPatterns != nullptr); 564 for (;;) { 565 parentLocale = ulocimp_getParent(parentLocale.data(), status); 566 // look for pattern for srcPluralCount in locale tree 567 LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_UNIT, parentLocale.data(), &status)); 568 LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, nullptr, &status)); 569 const char* timeUnitName = getTimeUnitName(srcTimeUnitField, status); 570 LocalUResourceBundlePointer countsToPatternRB(ures_getByKey(unitsRes.getAlias(), timeUnitName, nullptr, &status)); 571 const char16_t* pattern; 572 int32_t ptLength; 573 pattern = ures_getStringByKeyWithFallback(countsToPatternRB.getAlias(), searchPluralCount, &ptLength, &status); 574 if (U_SUCCESS(status)) { 575 //found 576 LocalPointer<MessageFormat> messageFormat( 577 new MessageFormat(UnicodeString(true, pattern, ptLength), getLocale(err), err), err); 578 if (U_FAILURE(err)) { 579 return; 580 } 581 MessageFormat** formatters = static_cast<MessageFormat**>(countToPatterns->get(srcPluralCount)); 582 if (formatters == nullptr) { 583 LocalMemory<MessageFormat *> localFormatters( 584 static_cast<MessageFormat**>(uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT * sizeof(MessageFormat*)))); 585 formatters = localFormatters.getAlias(); 586 localFormatters[UTMUTFMT_FULL_STYLE] = nullptr; 587 localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = nullptr; 588 countToPatterns->put(srcPluralCount, localFormatters.orphan(), err); 589 if (U_FAILURE(err)) { 590 return; 591 } 592 } 593 //delete formatters[style]; 594 formatters[style] = messageFormat.orphan(); 595 return; 596 } 597 status = U_ZERO_ERROR; 598 if (parentLocale.isEmpty()) { 599 break; 600 } 601 } 602 603 // if no unitsShort resource was found even after fallback to root locale 604 // then search the units resource fallback from the current level to root 605 if ( parentLocale.isEmpty() && uprv_strcmp(key, gShortUnitsTag) == 0) { 606 #ifdef TMUTFMT_DEBUG 607 std::cout << "loop into searchInLocaleChain since Short-Long-Alternative \n"; 608 #endif 609 CharString pLocale(localeName, -1, err); 610 // Add an underscore at the tail of locale name, 611 // so that searchInLocaleChain will check the current locale before falling back 612 pLocale.append('_', err); 613 searchInLocaleChain(style, gUnitsTag, pLocale.data(), srcTimeUnitField, srcPluralCount, 614 searchPluralCount, countToPatterns, err); 615 if (U_FAILURE(err)) { 616 return; 617 } 618 MessageFormat** formatters = static_cast<MessageFormat**>(countToPatterns->get(srcPluralCount)); 619 if (formatters != nullptr && formatters[style] != nullptr) { 620 return; 621 } 622 } 623 624 // if not found the pattern for this plural count at all, 625 // fall-back to plural count "other" 626 if ( uprv_strcmp(searchPluralCount, gPluralCountOther) == 0 ) { 627 // set default fall back the same as the resource in root 628 LocalPointer<MessageFormat> messageFormat; 629 const char16_t *pattern = nullptr; 630 if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_SECOND ) { 631 pattern = DEFAULT_PATTERN_FOR_SECOND; 632 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MINUTE ) { 633 pattern = DEFAULT_PATTERN_FOR_MINUTE; 634 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_HOUR ) { 635 pattern = DEFAULT_PATTERN_FOR_HOUR; 636 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_WEEK ) { 637 pattern = DEFAULT_PATTERN_FOR_WEEK; 638 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_DAY ) { 639 pattern = DEFAULT_PATTERN_FOR_DAY; 640 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MONTH ) { 641 pattern = DEFAULT_PATTERN_FOR_MONTH; 642 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_YEAR ) { 643 pattern = DEFAULT_PATTERN_FOR_YEAR; 644 } 645 if (pattern != nullptr) { 646 messageFormat.adoptInsteadAndCheckErrorCode( 647 new MessageFormat(UnicodeString(true, pattern, -1), getLocale(err), err), err); 648 } 649 if (U_FAILURE(err)) { 650 return; 651 } 652 MessageFormat** formatters = static_cast<MessageFormat**>(countToPatterns->get(srcPluralCount)); 653 if (formatters == nullptr) { 654 LocalMemory<MessageFormat *> localFormatters ( 655 static_cast<MessageFormat**>(uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT * sizeof(MessageFormat*)))); 656 if (localFormatters.isNull()) { 657 err = U_MEMORY_ALLOCATION_ERROR; 658 return; 659 } 660 formatters = localFormatters.getAlias(); 661 formatters[UTMUTFMT_FULL_STYLE] = nullptr; 662 formatters[UTMUTFMT_ABBREVIATED_STYLE] = nullptr; 663 countToPatterns->put(srcPluralCount, localFormatters.orphan(), err); 664 } 665 if (U_SUCCESS(err)) { 666 //delete formatters[style]; 667 formatters[style] = messageFormat.orphan(); 668 } 669 } else { 670 // fall back to rule "other", and search in parents 671 searchInLocaleChain(style, key, localeName, srcTimeUnitField, srcPluralCount, 672 gPluralCountOther, countToPatterns, err); 673 } 674 } 675 676 void 677 TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) { 678 if (setMeasureFormatLocale(locale, status)) { 679 setup(status); 680 } 681 } 682 683 684 void 685 TimeUnitFormat::setNumberFormat(const NumberFormat& format, UErrorCode& status){ 686 if (U_FAILURE(status)) { 687 return; 688 } 689 adoptNumberFormat(format.clone(), status); 690 } 691 692 693 void 694 TimeUnitFormat::deleteHash(Hashtable* htable) { 695 int32_t pos = UHASH_FIRST; 696 const UHashElement* element = nullptr; 697 if ( htable ) { 698 while ( (element = htable->nextElement(pos)) != nullptr ) { 699 const UHashTok valueTok = element->value; 700 const MessageFormat** value = static_cast<const MessageFormat**>(valueTok.pointer); 701 delete value[UTMUTFMT_FULL_STYLE]; 702 delete value[UTMUTFMT_ABBREVIATED_STYLE]; 703 //delete[] value; 704 uprv_free(value); 705 } 706 } 707 delete htable; 708 } 709 710 711 void 712 TimeUnitFormat::copyHash(const Hashtable* source, Hashtable* target, UErrorCode& status) { 713 if ( U_FAILURE(status) ) { 714 return; 715 } 716 int32_t pos = UHASH_FIRST; 717 const UHashElement* element = nullptr; 718 if ( source ) { 719 while ( (element = source->nextElement(pos)) != nullptr ) { 720 const UHashTok keyTok = element->key; 721 const UnicodeString* key = static_cast<UnicodeString*>(keyTok.pointer); 722 const UHashTok valueTok = element->value; 723 const MessageFormat** value = static_cast<const MessageFormat**>(valueTok.pointer); 724 MessageFormat** newVal = static_cast<MessageFormat**>(uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT * sizeof(MessageFormat*))); 725 newVal[0] = value[0]->clone(); 726 newVal[1] = value[1]->clone(); 727 target->put(UnicodeString(*key), newVal, status); 728 if ( U_FAILURE(status) ) { 729 delete newVal[0]; 730 delete newVal[1]; 731 uprv_free(newVal); 732 return; 733 } 734 } 735 } 736 } 737 738 739 U_CDECL_BEGIN 740 741 /** 742 * set hash table value comparator 743 * 744 * @param val1 one value in comparison 745 * @param val2 the other value in comparison 746 * @return true if 2 values are the same, false otherwise 747 */ 748 static UBool U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2); 749 750 static UBool 751 U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2) { 752 const MessageFormat** pattern1 = (const MessageFormat**)val1.pointer; 753 const MessageFormat** pattern2 = (const MessageFormat**)val2.pointer; 754 return *pattern1[0] == *pattern2[0] && *pattern1[1] == *pattern2[1]; 755 } 756 757 U_CDECL_END 758 759 Hashtable* 760 TimeUnitFormat::initHash(UErrorCode& status) { 761 if ( U_FAILURE(status) ) { 762 return nullptr; 763 } 764 Hashtable* hTable; 765 if ( (hTable = new Hashtable(true, status)) == nullptr ) { 766 status = U_MEMORY_ALLOCATION_ERROR; 767 return nullptr; 768 } 769 if ( U_FAILURE(status) ) { 770 delete hTable; 771 return nullptr; 772 } 773 hTable->setValueComparator(tmutfmtHashTableValueComparator); 774 return hTable; 775 } 776 777 778 const char* 779 TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField, 780 UErrorCode& status) { 781 if (U_FAILURE(status)) { 782 return nullptr; 783 } 784 switch (unitField) { 785 case TimeUnit::UTIMEUNIT_YEAR: 786 return gTimeUnitYear; 787 case TimeUnit::UTIMEUNIT_MONTH: 788 return gTimeUnitMonth; 789 case TimeUnit::UTIMEUNIT_DAY: 790 return gTimeUnitDay; 791 case TimeUnit::UTIMEUNIT_WEEK: 792 return gTimeUnitWeek; 793 case TimeUnit::UTIMEUNIT_HOUR: 794 return gTimeUnitHour; 795 case TimeUnit::UTIMEUNIT_MINUTE: 796 return gTimeUnitMinute; 797 case TimeUnit::UTIMEUNIT_SECOND: 798 return gTimeUnitSecond; 799 default: 800 status = U_ILLEGAL_ARGUMENT_ERROR; 801 return nullptr; 802 } 803 } 804 805 U_NAMESPACE_END 806 807 #endif