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