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