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