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