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