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