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