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