OLD | NEW |
(Empty) | |
| 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_ZER
O, RIGHT_CURLY_BRACKET, SPACE, LOW_S, 0}; |
| 65 static const UChar DEFAULT_PATTERN_FOR_MINUTE[] = {LEFT_CURLY_BRACKET, DIGIT_ZER
O, 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& s
tatus) |
| 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], fTimeUnitToCountToPatt
erns[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->fTimeUnitToCo
untToPatterns[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*>(forma
tObj); |
| 206 if (amount != NULL){ |
| 207 Hashtable* countToPattern = fTimeUnitToCountToPatterns[amount->getTi
meUnitField()]; |
| 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: " << re
sult << "\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, resultTimeUni
t, 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[timeUnitFiel
d]; |
| 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, fLocal
e, 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(Mess
ageFormat*)); |
| 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**)countToPattern
s->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* l
ocaleName, |
| 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, searchPlura
lCount, &ptLength, &status); |
| 625 if (U_SUCCESS(status)) { |
| 626 //found |
| 627 MessageFormat* messageFormat = new MessageFormat(pattern, fLocale, e
rr); |
| 628 if (U_SUCCESS(err)) { |
| 629 if (fNumberFormat != NULL) { |
| 630 messageFormat->setFormat(0, *fNumberFormat); |
| 631 } |
| 632 MessageFormat** formatters = (MessageFormat**)countToPatterns->g
et(srcPluralCount); |
| 633 if (formatters == NULL) { |
| 634 formatters = (MessageFormat**)uprv_malloc(kTotal*sizeof(Mess
ageFormat*)); |
| 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 fall
ing back |
| 674 uprv_strcat(pLocale, "_"); |
| 675 searchInLocaleChain(style, gUnitsTag, pLocale, srcTimeUnitField, srcPlur
alCount, |
| 676 searchPluralCount, countToPatterns, err); |
| 677 if (countToPatterns != NULL) { |
| 678 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(s
rcPluralCount); |
| 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, fLocal
e, err); |
| 690 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MINUTE ) { |
| 691 messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_MINUTE, fLocal
e, 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(s
rcPluralCount); |
| 708 if (formatters == NULL) { |
| 709 formatters = (MessageFormat**)uprv_malloc(kTotal*sizeof(MessageF
ormat*)); |
| 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, srcPluralC
ount, |
| 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.pointe
r; |
| 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.pointe
r; |
| 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 |
OLD | NEW |