OLD | NEW |
1 /* | 1 /* |
2 ********************************************************************** | 2 ********************************************************************** |
3 * Copyright (c) 2004-2011, International Business Machines | 3 * Copyright (c) 2004-2014, International Business Machines |
4 * Corporation and others. All Rights Reserved. | 4 * Corporation and others. All Rights Reserved. |
5 ********************************************************************** | 5 ********************************************************************** |
6 * Author: Alan Liu | 6 * Author: Alan Liu |
7 * Created: April 20, 2004 | 7 * Created: April 20, 2004 |
8 * Since: ICU 3.0 | 8 * Since: ICU 3.0 |
9 ********************************************************************** | 9 ********************************************************************** |
10 */ | 10 */ |
| 11 #include "utypeinfo.h" // for 'typeid' to work |
11 #include "unicode/utypes.h" | 12 #include "unicode/utypes.h" |
12 | 13 |
13 #if !UCONFIG_NO_FORMATTING | 14 #if !UCONFIG_NO_FORMATTING |
14 | 15 |
15 #include "unicode/measfmt.h" | 16 #include "unicode/measfmt.h" |
| 17 #include "unicode/numfmt.h" |
16 #include "currfmt.h" | 18 #include "currfmt.h" |
| 19 #include "unicode/localpointer.h" |
| 20 #include "simplepatternformatter.h" |
| 21 #include "quantityformatter.h" |
| 22 #include "unicode/plurrule.h" |
| 23 #include "unicode/decimfmt.h" |
| 24 #include "uresimp.h" |
| 25 #include "unicode/ures.h" |
| 26 #include "ureslocs.h" |
| 27 #include "cstring.h" |
| 28 #include "mutex.h" |
| 29 #include "ucln_in.h" |
| 30 #include "unicode/listformatter.h" |
| 31 #include "charstr.h" |
| 32 #include "unicode/putil.h" |
| 33 #include "unicode/smpdtfmt.h" |
| 34 #include "uassert.h" |
| 35 |
| 36 #include "sharednumberformat.h" |
| 37 #include "sharedpluralrules.h" |
| 38 #include "unifiedcache.h" |
| 39 |
| 40 #define MEAS_UNIT_COUNT 121 |
| 41 #define WIDTH_INDEX_COUNT (UMEASFMT_WIDTH_NARROW + 1) |
17 | 42 |
18 U_NAMESPACE_BEGIN | 43 U_NAMESPACE_BEGIN |
19 | 44 |
20 MeasureFormat::MeasureFormat() {} | 45 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat) |
21 | 46 |
22 MeasureFormat::~MeasureFormat() {} | 47 // Used to format durations like 5:47 or 21:35:42. |
| 48 class NumericDateFormatters : public UMemory { |
| 49 public: |
| 50 // Formats like H:mm |
| 51 SimpleDateFormat hourMinute; |
| 52 |
| 53 // formats like M:ss |
| 54 SimpleDateFormat minuteSecond; |
| 55 |
| 56 // formats like H:mm:ss |
| 57 SimpleDateFormat hourMinuteSecond; |
| 58 |
| 59 // Constructor that takes the actual patterns for hour-minute, |
| 60 // minute-second, and hour-minute-second respectively. |
| 61 NumericDateFormatters( |
| 62 const UnicodeString &hm, |
| 63 const UnicodeString &ms, |
| 64 const UnicodeString &hms, |
| 65 UErrorCode &status) : |
| 66 hourMinute(hm, status), |
| 67 minuteSecond(ms, status), |
| 68 hourMinuteSecond(hms, status) { |
| 69 const TimeZone *gmt = TimeZone::getGMT(); |
| 70 hourMinute.setTimeZone(*gmt); |
| 71 minuteSecond.setTimeZone(*gmt); |
| 72 hourMinuteSecond.setTimeZone(*gmt); |
| 73 } |
| 74 private: |
| 75 NumericDateFormatters(const NumericDateFormatters &other); |
| 76 NumericDateFormatters &operator=(const NumericDateFormatters &other); |
| 77 }; |
| 78 |
| 79 // Instances contain all MeasureFormat specific data for a particular locale. |
| 80 // This data is cached. It is never copied, but is shared via shared pointers. |
| 81 class MeasureFormatCacheData : public SharedObject { |
| 82 public: |
| 83 QuantityFormatter formatters[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT]; |
| 84 SimplePatternFormatter perFormatters[WIDTH_INDEX_COUNT]; |
| 85 |
| 86 MeasureFormatCacheData(); |
| 87 void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) { |
| 88 delete currencyFormats[widthIndex]; |
| 89 currencyFormats[widthIndex] = nfToAdopt; |
| 90 } |
| 91 const NumberFormat *getCurrencyFormat(int32_t widthIndex) const { |
| 92 return currencyFormats[widthIndex]; |
| 93 } |
| 94 void adoptIntegerFormat(NumberFormat *nfToAdopt) { |
| 95 delete integerFormat; |
| 96 integerFormat = nfToAdopt; |
| 97 } |
| 98 const NumberFormat *getIntegerFormat() const { |
| 99 return integerFormat; |
| 100 } |
| 101 void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) { |
| 102 delete numericDateFormatters; |
| 103 numericDateFormatters = formattersToAdopt; |
| 104 } |
| 105 const NumericDateFormatters *getNumericDateFormatters() const { |
| 106 return numericDateFormatters; |
| 107 } |
| 108 void adoptPerUnitFormatter( |
| 109 int32_t index, |
| 110 int32_t widthIndex, |
| 111 SimplePatternFormatter *formatterToAdopt) { |
| 112 delete perUnitFormatters[index][widthIndex]; |
| 113 perUnitFormatters[index][widthIndex] = formatterToAdopt; |
| 114 } |
| 115 const SimplePatternFormatter * const * getPerUnitFormattersByIndex( |
| 116 int32_t index) const { |
| 117 return perUnitFormatters[index]; |
| 118 } |
| 119 virtual ~MeasureFormatCacheData(); |
| 120 private: |
| 121 NumberFormat *currencyFormats[WIDTH_INDEX_COUNT]; |
| 122 NumberFormat *integerFormat; |
| 123 NumericDateFormatters *numericDateFormatters; |
| 124 SimplePatternFormatter *perUnitFormatters[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT
]; |
| 125 MeasureFormatCacheData(const MeasureFormatCacheData &other); |
| 126 MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other); |
| 127 }; |
| 128 |
| 129 MeasureFormatCacheData::MeasureFormatCacheData() { |
| 130 for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) { |
| 131 currencyFormats[i] = NULL; |
| 132 } |
| 133 for (int32_t i = 0; i < MEAS_UNIT_COUNT; ++i) { |
| 134 for (int32_t j = 0; j < WIDTH_INDEX_COUNT; ++j) { |
| 135 perUnitFormatters[i][j] = NULL; |
| 136 } |
| 137 } |
| 138 integerFormat = NULL; |
| 139 numericDateFormatters = NULL; |
| 140 } |
| 141 |
| 142 MeasureFormatCacheData::~MeasureFormatCacheData() { |
| 143 for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) { |
| 144 delete currencyFormats[i]; |
| 145 } |
| 146 for (int32_t i = 0; i < MEAS_UNIT_COUNT; ++i) { |
| 147 for (int32_t j = 0; j < WIDTH_INDEX_COUNT; ++j) { |
| 148 delete perUnitFormatters[i][j]; |
| 149 } |
| 150 } |
| 151 delete integerFormat; |
| 152 delete numericDateFormatters; |
| 153 } |
| 154 |
| 155 static int32_t widthToIndex(UMeasureFormatWidth width) { |
| 156 if (width >= WIDTH_INDEX_COUNT) { |
| 157 return WIDTH_INDEX_COUNT - 1; |
| 158 } |
| 159 return width; |
| 160 } |
| 161 |
| 162 static UBool isCurrency(const MeasureUnit &unit) { |
| 163 return (uprv_strcmp(unit.getType(), "currency") == 0); |
| 164 } |
| 165 |
| 166 static UBool getString( |
| 167 const UResourceBundle *resource, |
| 168 UnicodeString &result, |
| 169 UErrorCode &status) { |
| 170 int32_t len = 0; |
| 171 const UChar *resStr = ures_getString(resource, &len, &status); |
| 172 if (U_FAILURE(status)) { |
| 173 return FALSE; |
| 174 } |
| 175 result.setTo(TRUE, resStr, len); |
| 176 return TRUE; |
| 177 } |
| 178 |
| 179 |
| 180 static UBool loadMeasureUnitData( |
| 181 const UResourceBundle *resource, |
| 182 MeasureFormatCacheData &cacheData, |
| 183 UErrorCode &status) { |
| 184 if (U_FAILURE(status)) { |
| 185 return FALSE; |
| 186 } |
| 187 static const char *widthPath[] = {"units", "unitsShort", "unitsNarrow"}; |
| 188 MeasureUnit *units = NULL; |
| 189 int32_t unitCount = MeasureUnit::getAvailable(units, 0, status); |
| 190 while (status == U_BUFFER_OVERFLOW_ERROR) { |
| 191 status = U_ZERO_ERROR; |
| 192 delete [] units; |
| 193 units = new MeasureUnit[unitCount]; |
| 194 if (units == NULL) { |
| 195 status = U_MEMORY_ALLOCATION_ERROR; |
| 196 return FALSE; |
| 197 } |
| 198 unitCount = MeasureUnit::getAvailable(units, unitCount, status); |
| 199 } |
| 200 for (int32_t currentWidth = 0; currentWidth < WIDTH_INDEX_COUNT; ++currentWi
dth) { |
| 201 // Be sure status is clear since next resource bundle lookup may fail. |
| 202 if (U_FAILURE(status)) { |
| 203 delete [] units; |
| 204 return FALSE; |
| 205 } |
| 206 LocalUResourceBundlePointer widthBundle( |
| 207 ures_getByKeyWithFallback( |
| 208 resource, widthPath[currentWidth], NULL, &status)); |
| 209 // We may not have data for all widths in all locales. |
| 210 if (status == U_MISSING_RESOURCE_ERROR) { |
| 211 status = U_ZERO_ERROR; |
| 212 continue; |
| 213 } |
| 214 { |
| 215 // compound per |
| 216 LocalUResourceBundlePointer compoundPerBundle( |
| 217 ures_getByKeyWithFallback( |
| 218 widthBundle.getAlias(), |
| 219 "compound/per", |
| 220 NULL, |
| 221 &status)); |
| 222 if (U_FAILURE(status)) { |
| 223 status = U_ZERO_ERROR; |
| 224 } else { |
| 225 UnicodeString perPattern; |
| 226 getString(compoundPerBundle.getAlias(), perPattern, status); |
| 227 cacheData.perFormatters[currentWidth].compile(perPattern, status
); |
| 228 } |
| 229 } |
| 230 for (int32_t currentUnit = 0; currentUnit < unitCount; ++currentUnit) { |
| 231 // Be sure status is clear next lookup may fail. |
| 232 if (U_FAILURE(status)) { |
| 233 delete [] units; |
| 234 return FALSE; |
| 235 } |
| 236 if (isCurrency(units[currentUnit])) { |
| 237 continue; |
| 238 } |
| 239 CharString pathBuffer; |
| 240 pathBuffer.append(units[currentUnit].getType(), status) |
| 241 .append("/", status) |
| 242 .append(units[currentUnit].getSubtype(), status); |
| 243 LocalUResourceBundlePointer unitBundle( |
| 244 ures_getByKeyWithFallback( |
| 245 widthBundle.getAlias(), |
| 246 pathBuffer.data(), |
| 247 NULL, |
| 248 &status)); |
| 249 // We may not have data for all units in all widths |
| 250 if (status == U_MISSING_RESOURCE_ERROR) { |
| 251 status = U_ZERO_ERROR; |
| 252 continue; |
| 253 } |
| 254 // We must have the unit bundle to proceed |
| 255 if (U_FAILURE(status)) { |
| 256 delete [] units; |
| 257 return FALSE; |
| 258 } |
| 259 int32_t size = ures_getSize(unitBundle.getAlias()); |
| 260 for (int32_t plIndex = 0; plIndex < size; ++plIndex) { |
| 261 LocalUResourceBundlePointer pluralBundle( |
| 262 ures_getByIndex( |
| 263 unitBundle.getAlias(), plIndex, NULL, &status)); |
| 264 if (U_FAILURE(status)) { |
| 265 delete [] units; |
| 266 return FALSE; |
| 267 } |
| 268 const char * resKey = ures_getKey(pluralBundle.getAlias()); |
| 269 if (uprv_strcmp(resKey, "dnam") == 0) { |
| 270 continue; // skip display name & per pattern (new in CLDR 26
/ ICU 54) for now, not part of plurals |
| 271 } |
| 272 if (uprv_strcmp(resKey, "per") == 0) { |
| 273 UnicodeString perPattern; |
| 274 getString(pluralBundle.getAlias(), perPattern, status); |
| 275 cacheData.adoptPerUnitFormatter( |
| 276 units[currentUnit].getIndex(), |
| 277 currentWidth, |
| 278 new SimplePatternFormatter(perPattern)); |
| 279 continue; |
| 280 } |
| 281 UnicodeString rawPattern; |
| 282 getString(pluralBundle.getAlias(), rawPattern, status); |
| 283 cacheData.formatters[units[currentUnit].getIndex()][currentWidth
].add( |
| 284 resKey, |
| 285 rawPattern, |
| 286 status); |
| 287 } |
| 288 } |
| 289 } |
| 290 delete [] units; |
| 291 return U_SUCCESS(status); |
| 292 } |
| 293 |
| 294 static UnicodeString loadNumericDateFormatterPattern( |
| 295 const UResourceBundle *resource, |
| 296 const char *pattern, |
| 297 UErrorCode &status) { |
| 298 UnicodeString result; |
| 299 if (U_FAILURE(status)) { |
| 300 return result; |
| 301 } |
| 302 CharString chs; |
| 303 chs.append("durationUnits", status) |
| 304 .append("/", status).append(pattern, status); |
| 305 LocalUResourceBundlePointer patternBundle( |
| 306 ures_getByKeyWithFallback( |
| 307 resource, |
| 308 chs.data(), |
| 309 NULL, |
| 310 &status)); |
| 311 if (U_FAILURE(status)) { |
| 312 return result; |
| 313 } |
| 314 getString(patternBundle.getAlias(), result, status); |
| 315 // Replace 'h' with 'H' |
| 316 int32_t len = result.length(); |
| 317 UChar *buffer = result.getBuffer(len); |
| 318 for (int32_t i = 0; i < len; ++i) { |
| 319 if (buffer[i] == 0x68) { // 'h' |
| 320 buffer[i] = 0x48; // 'H' |
| 321 } |
| 322 } |
| 323 result.releaseBuffer(len); |
| 324 return result; |
| 325 } |
| 326 |
| 327 static NumericDateFormatters *loadNumericDateFormatters( |
| 328 const UResourceBundle *resource, |
| 329 UErrorCode &status) { |
| 330 if (U_FAILURE(status)) { |
| 331 return NULL; |
| 332 } |
| 333 NumericDateFormatters *result = new NumericDateFormatters( |
| 334 loadNumericDateFormatterPattern(resource, "hm", status), |
| 335 loadNumericDateFormatterPattern(resource, "ms", status), |
| 336 loadNumericDateFormatterPattern(resource, "hms", status), |
| 337 status); |
| 338 if (U_FAILURE(status)) { |
| 339 delete result; |
| 340 return NULL; |
| 341 } |
| 342 return result; |
| 343 } |
| 344 |
| 345 template<> U_I18N_API |
| 346 const MeasureFormatCacheData *LocaleCacheKey<MeasureFormatCacheData>::createObje
ct( |
| 347 const void * /*unused*/, UErrorCode &status) const { |
| 348 const char *localeId = fLoc.getName(); |
| 349 LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status)); |
| 350 LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, localeId,
&status)); |
| 351 static UNumberFormatStyle currencyStyles[] = { |
| 352 UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY}; |
| 353 if (U_FAILURE(status)) { |
| 354 return NULL; |
| 355 } |
| 356 LocalPointer<MeasureFormatCacheData> result(new MeasureFormatCacheData()); |
| 357 if (result.isNull()) { |
| 358 status = U_MEMORY_ALLOCATION_ERROR; |
| 359 return NULL; |
| 360 } |
| 361 if (!loadMeasureUnitData( |
| 362 unitsBundle.getAlias(), |
| 363 *result, |
| 364 status)) { |
| 365 return NULL; |
| 366 } |
| 367 result->adoptNumericDateFormatters(loadNumericDateFormatters( |
| 368 topLevel.getAlias(), status)); |
| 369 if (U_FAILURE(status)) { |
| 370 return NULL; |
| 371 } |
| 372 |
| 373 for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { |
| 374 result->adoptCurrencyFormat(i, NumberFormat::createInstance( |
| 375 localeId, currencyStyles[i], status)); |
| 376 if (U_FAILURE(status)) { |
| 377 return NULL; |
| 378 } |
| 379 } |
| 380 NumberFormat *inf = NumberFormat::createInstance( |
| 381 localeId, UNUM_DECIMAL, status); |
| 382 if (U_FAILURE(status)) { |
| 383 return NULL; |
| 384 } |
| 385 inf->setMaximumFractionDigits(0); |
| 386 DecimalFormat *decfmt = dynamic_cast<DecimalFormat *>(inf); |
| 387 if (decfmt != NULL) { |
| 388 decfmt->setRoundingMode(DecimalFormat::kRoundDown); |
| 389 } |
| 390 result->adoptIntegerFormat(inf); |
| 391 result->addRef(); |
| 392 return result.orphan(); |
| 393 } |
| 394 |
| 395 static UBool isTimeUnit(const MeasureUnit &mu, const char *tu) { |
| 396 return uprv_strcmp(mu.getType(), "duration") == 0 && |
| 397 uprv_strcmp(mu.getSubtype(), tu) == 0; |
| 398 } |
| 399 |
| 400 // Converts a composite measure into hours-minutes-seconds and stores at hms |
| 401 // array. [0] is hours; [1] is minutes; [2] is seconds. Returns a bit map of |
| 402 // units found: 1=hours, 2=minutes, 4=seconds. For example, if measures |
| 403 // contains hours-minutes, this function would return 3. |
| 404 // |
| 405 // If measures cannot be converted into hours, minutes, seconds or if amounts |
| 406 // are negative, or if hours, minutes, seconds are out of order, returns 0. |
| 407 static int32_t toHMS( |
| 408 const Measure *measures, |
| 409 int32_t measureCount, |
| 410 Formattable *hms, |
| 411 UErrorCode &status) { |
| 412 if (U_FAILURE(status)) { |
| 413 return 0; |
| 414 } |
| 415 int32_t result = 0; |
| 416 if (U_FAILURE(status)) { |
| 417 return 0; |
| 418 } |
| 419 // We use copy constructor to ensure that both sides of equality operator |
| 420 // are instances of MeasureUnit base class and not a subclass. Otherwise, |
| 421 // operator== will immediately return false. |
| 422 for (int32_t i = 0; i < measureCount; ++i) { |
| 423 if (isTimeUnit(measures[i].getUnit(), "hour")) { |
| 424 // hour must come first |
| 425 if (result >= 1) { |
| 426 return 0; |
| 427 } |
| 428 hms[0] = measures[i].getNumber(); |
| 429 if (hms[0].getDouble() < 0.0) { |
| 430 return 0; |
| 431 } |
| 432 result |= 1; |
| 433 } else if (isTimeUnit(measures[i].getUnit(), "minute")) { |
| 434 // minute must come after hour |
| 435 if (result >= 2) { |
| 436 return 0; |
| 437 } |
| 438 hms[1] = measures[i].getNumber(); |
| 439 if (hms[1].getDouble() < 0.0) { |
| 440 return 0; |
| 441 } |
| 442 result |= 2; |
| 443 } else if (isTimeUnit(measures[i].getUnit(), "second")) { |
| 444 // second must come after hour and minute |
| 445 if (result >= 4) { |
| 446 return 0; |
| 447 } |
| 448 hms[2] = measures[i].getNumber(); |
| 449 if (hms[2].getDouble() < 0.0) { |
| 450 return 0; |
| 451 } |
| 452 result |= 4; |
| 453 } else { |
| 454 return 0; |
| 455 } |
| 456 } |
| 457 return result; |
| 458 } |
| 459 |
| 460 |
| 461 MeasureFormat::MeasureFormat( |
| 462 const Locale &locale, UMeasureFormatWidth w, UErrorCode &status) |
| 463 : cache(NULL), |
| 464 numberFormat(NULL), |
| 465 pluralRules(NULL), |
| 466 width(w), |
| 467 listFormatter(NULL) { |
| 468 initMeasureFormat(locale, w, NULL, status); |
| 469 } |
| 470 |
| 471 MeasureFormat::MeasureFormat( |
| 472 const Locale &locale, |
| 473 UMeasureFormatWidth w, |
| 474 NumberFormat *nfToAdopt, |
| 475 UErrorCode &status) |
| 476 : cache(NULL), |
| 477 numberFormat(NULL), |
| 478 pluralRules(NULL), |
| 479 width(w), |
| 480 listFormatter(NULL) { |
| 481 initMeasureFormat(locale, w, nfToAdopt, status); |
| 482 } |
| 483 |
| 484 MeasureFormat::MeasureFormat(const MeasureFormat &other) : |
| 485 Format(other), |
| 486 cache(other.cache), |
| 487 numberFormat(other.numberFormat), |
| 488 pluralRules(other.pluralRules), |
| 489 width(other.width), |
| 490 listFormatter(NULL) { |
| 491 cache->addRef(); |
| 492 numberFormat->addRef(); |
| 493 pluralRules->addRef(); |
| 494 listFormatter = new ListFormatter(*other.listFormatter); |
| 495 } |
| 496 |
| 497 MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) { |
| 498 if (this == &other) { |
| 499 return *this; |
| 500 } |
| 501 Format::operator=(other); |
| 502 SharedObject::copyPtr(other.cache, cache); |
| 503 SharedObject::copyPtr(other.numberFormat, numberFormat); |
| 504 SharedObject::copyPtr(other.pluralRules, pluralRules); |
| 505 width = other.width; |
| 506 delete listFormatter; |
| 507 listFormatter = new ListFormatter(*other.listFormatter); |
| 508 return *this; |
| 509 } |
| 510 |
| 511 MeasureFormat::MeasureFormat() : |
| 512 cache(NULL), |
| 513 numberFormat(NULL), |
| 514 pluralRules(NULL), |
| 515 width(UMEASFMT_WIDTH_WIDE), |
| 516 listFormatter(NULL) { |
| 517 } |
| 518 |
| 519 MeasureFormat::~MeasureFormat() { |
| 520 if (cache != NULL) { |
| 521 cache->removeRef(); |
| 522 } |
| 523 if (numberFormat != NULL) { |
| 524 numberFormat->removeRef(); |
| 525 } |
| 526 if (pluralRules != NULL) { |
| 527 pluralRules->removeRef(); |
| 528 } |
| 529 delete listFormatter; |
| 530 } |
| 531 |
| 532 UBool MeasureFormat::operator==(const Format &other) const { |
| 533 if (this == &other) { // Same object, equal |
| 534 return TRUE; |
| 535 } |
| 536 if (!Format::operator==(other)) { |
| 537 return FALSE; |
| 538 } |
| 539 const MeasureFormat &rhs = static_cast<const MeasureFormat &>(other); |
| 540 |
| 541 // Note: Since the ListFormatter depends only on Locale and width, we |
| 542 // don't have to check it here. |
| 543 |
| 544 // differing widths aren't equivalent |
| 545 if (width != rhs.width) { |
| 546 return FALSE; |
| 547 } |
| 548 // Width the same check locales. |
| 549 // We don't need to check locales if both objects have same cache. |
| 550 if (cache != rhs.cache) { |
| 551 UErrorCode status = U_ZERO_ERROR; |
| 552 const char *localeId = getLocaleID(status); |
| 553 const char *rhsLocaleId = rhs.getLocaleID(status); |
| 554 if (U_FAILURE(status)) { |
| 555 // On failure, assume not equal |
| 556 return FALSE; |
| 557 } |
| 558 if (uprv_strcmp(localeId, rhsLocaleId) != 0) { |
| 559 return FALSE; |
| 560 } |
| 561 } |
| 562 // Locales same, check NumberFormat if shared data differs. |
| 563 return ( |
| 564 numberFormat == rhs.numberFormat || |
| 565 **numberFormat == **rhs.numberFormat); |
| 566 } |
| 567 |
| 568 Format *MeasureFormat::clone() const { |
| 569 return new MeasureFormat(*this); |
| 570 } |
| 571 |
| 572 UnicodeString &MeasureFormat::format( |
| 573 const Formattable &obj, |
| 574 UnicodeString &appendTo, |
| 575 FieldPosition &pos, |
| 576 UErrorCode &status) const { |
| 577 if (U_FAILURE(status)) return appendTo; |
| 578 if (obj.getType() == Formattable::kObject) { |
| 579 const UObject* formatObj = obj.getObject(); |
| 580 const Measure* amount = dynamic_cast<const Measure*>(formatObj); |
| 581 if (amount != NULL) { |
| 582 return formatMeasure( |
| 583 *amount, **numberFormat, appendTo, pos, status); |
| 584 } |
| 585 } |
| 586 status = U_ILLEGAL_ARGUMENT_ERROR; |
| 587 return appendTo; |
| 588 } |
| 589 |
| 590 void MeasureFormat::parseObject( |
| 591 const UnicodeString & /*source*/, |
| 592 Formattable & /*result*/, |
| 593 ParsePosition& /*pos*/) const { |
| 594 return; |
| 595 } |
| 596 |
| 597 UnicodeString &MeasureFormat::formatMeasuresPer( |
| 598 const Measure *measures, |
| 599 int32_t measureCount, |
| 600 const MeasureUnit &perUnit, |
| 601 UnicodeString &appendTo, |
| 602 FieldPosition &pos, |
| 603 UErrorCode &status) const { |
| 604 FieldPosition fpos(pos.getField()); |
| 605 UnicodeString measuresString; |
| 606 int32_t offset = withPerUnit( |
| 607 formatMeasures( |
| 608 measures, measureCount, measuresString, fpos, status), |
| 609 perUnit, |
| 610 appendTo, |
| 611 status); |
| 612 if (U_FAILURE(status)) { |
| 613 return appendTo; |
| 614 } |
| 615 if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) { |
| 616 pos.setBeginIndex(fpos.getBeginIndex() + offset); |
| 617 pos.setEndIndex(fpos.getEndIndex() + offset); |
| 618 } |
| 619 return appendTo; |
| 620 } |
| 621 |
| 622 UnicodeString &MeasureFormat::formatMeasures( |
| 623 const Measure *measures, |
| 624 int32_t measureCount, |
| 625 UnicodeString &appendTo, |
| 626 FieldPosition &pos, |
| 627 UErrorCode &status) const { |
| 628 if (U_FAILURE(status)) { |
| 629 return appendTo; |
| 630 } |
| 631 if (measureCount == 0) { |
| 632 return appendTo; |
| 633 } |
| 634 if (measureCount == 1) { |
| 635 return formatMeasure(measures[0], **numberFormat, appendTo, pos, status)
; |
| 636 } |
| 637 if (width == UMEASFMT_WIDTH_NUMERIC) { |
| 638 Formattable hms[3]; |
| 639 int32_t bitMap = toHMS(measures, measureCount, hms, status); |
| 640 if (bitMap > 0) { |
| 641 return formatNumeric(hms, bitMap, appendTo, status); |
| 642 } |
| 643 } |
| 644 if (pos.getField() != FieldPosition::DONT_CARE) { |
| 645 return formatMeasuresSlowTrack( |
| 646 measures, measureCount, appendTo, pos, status); |
| 647 } |
| 648 UnicodeString *results = new UnicodeString[measureCount]; |
| 649 if (results == NULL) { |
| 650 status = U_MEMORY_ALLOCATION_ERROR; |
| 651 return appendTo; |
| 652 } |
| 653 for (int32_t i = 0; i < measureCount; ++i) { |
| 654 const NumberFormat *nf = cache->getIntegerFormat(); |
| 655 if (i == measureCount - 1) { |
| 656 nf = numberFormat->get(); |
| 657 } |
| 658 formatMeasure( |
| 659 measures[i], |
| 660 *nf, |
| 661 results[i], |
| 662 pos, |
| 663 status); |
| 664 } |
| 665 listFormatter->format(results, measureCount, appendTo, status); |
| 666 delete [] results; |
| 667 return appendTo; |
| 668 } |
| 669 |
| 670 void MeasureFormat::initMeasureFormat( |
| 671 const Locale &locale, |
| 672 UMeasureFormatWidth w, |
| 673 NumberFormat *nfToAdopt, |
| 674 UErrorCode &status) { |
| 675 static const char *listStyles[] = {"unit", "unit-short", "unit-narrow"}; |
| 676 LocalPointer<NumberFormat> nf(nfToAdopt); |
| 677 if (U_FAILURE(status)) { |
| 678 return; |
| 679 } |
| 680 const char *name = locale.getName(); |
| 681 setLocaleIDs(name, name); |
| 682 |
| 683 UnifiedCache::getByLocale(locale, cache, status); |
| 684 if (U_FAILURE(status)) { |
| 685 return; |
| 686 } |
| 687 |
| 688 const SharedPluralRules *pr = PluralRules::createSharedInstance( |
| 689 locale, UPLURAL_TYPE_CARDINAL, status); |
| 690 if (U_FAILURE(status)) { |
| 691 return; |
| 692 } |
| 693 SharedObject::copyPtr(pr, pluralRules); |
| 694 pr->removeRef(); |
| 695 if (nf.isNull()) { |
| 696 const SharedNumberFormat *shared = NumberFormat::createSharedInstance( |
| 697 locale, UNUM_DECIMAL, status); |
| 698 if (U_FAILURE(status)) { |
| 699 return; |
| 700 } |
| 701 SharedObject::copyPtr(shared, numberFormat); |
| 702 shared->removeRef(); |
| 703 } else { |
| 704 adoptNumberFormat(nf.orphan(), status); |
| 705 if (U_FAILURE(status)) { |
| 706 return; |
| 707 } |
| 708 } |
| 709 width = w; |
| 710 delete listFormatter; |
| 711 listFormatter = ListFormatter::createInstance( |
| 712 locale, |
| 713 listStyles[widthToIndex(width)], |
| 714 status); |
| 715 } |
| 716 |
| 717 void MeasureFormat::adoptNumberFormat( |
| 718 NumberFormat *nfToAdopt, UErrorCode &status) { |
| 719 LocalPointer<NumberFormat> nf(nfToAdopt); |
| 720 if (U_FAILURE(status)) { |
| 721 return; |
| 722 } |
| 723 SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias()); |
| 724 if (shared == NULL) { |
| 725 status = U_MEMORY_ALLOCATION_ERROR; |
| 726 return; |
| 727 } |
| 728 nf.orphan(); |
| 729 SharedObject::copyPtr(shared, numberFormat); |
| 730 } |
| 731 |
| 732 UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &st
atus) { |
| 733 if (U_FAILURE(status) || locale == getLocale(status)) { |
| 734 return FALSE; |
| 735 } |
| 736 initMeasureFormat(locale, width, NULL, status); |
| 737 return U_SUCCESS(status); |
| 738 } |
| 739 |
| 740 const NumberFormat &MeasureFormat::getNumberFormat() const { |
| 741 return **numberFormat; |
| 742 } |
| 743 |
| 744 const PluralRules &MeasureFormat::getPluralRules() const { |
| 745 return **pluralRules; |
| 746 } |
| 747 |
| 748 Locale MeasureFormat::getLocale(UErrorCode &status) const { |
| 749 return Format::getLocale(ULOC_VALID_LOCALE, status); |
| 750 } |
| 751 |
| 752 const char *MeasureFormat::getLocaleID(UErrorCode &status) const { |
| 753 return Format::getLocaleID(ULOC_VALID_LOCALE, status); |
| 754 } |
| 755 |
| 756 UnicodeString &MeasureFormat::formatMeasure( |
| 757 const Measure &measure, |
| 758 const NumberFormat &nf, |
| 759 UnicodeString &appendTo, |
| 760 FieldPosition &pos, |
| 761 UErrorCode &status) const { |
| 762 if (U_FAILURE(status)) { |
| 763 return appendTo; |
| 764 } |
| 765 const Formattable& amtNumber = measure.getNumber(); |
| 766 const MeasureUnit& amtUnit = measure.getUnit(); |
| 767 if (isCurrency(amtUnit)) { |
| 768 UChar isoCode[4]; |
| 769 u_charsToUChars(amtUnit.getSubtype(), isoCode, 4); |
| 770 return cache->getCurrencyFormat(widthToIndex(width))->format( |
| 771 new CurrencyAmount(amtNumber, isoCode, status), |
| 772 appendTo, |
| 773 pos, |
| 774 status); |
| 775 } |
| 776 const QuantityFormatter *quantityFormatter = getQuantityFormatter( |
| 777 amtUnit.getIndex(), widthToIndex(width), status); |
| 778 if (U_FAILURE(status)) { |
| 779 return appendTo; |
| 780 } |
| 781 return quantityFormatter->format( |
| 782 amtNumber, |
| 783 nf, |
| 784 **pluralRules, |
| 785 appendTo, |
| 786 pos, |
| 787 status); |
| 788 } |
| 789 |
| 790 // Formats hours-minutes-seconds as 5:37:23 or similar. |
| 791 UnicodeString &MeasureFormat::formatNumeric( |
| 792 const Formattable *hms, // always length 3 |
| 793 int32_t bitMap, // 1=hourset, 2=minuteset, 4=secondset |
| 794 UnicodeString &appendTo, |
| 795 UErrorCode &status) const { |
| 796 if (U_FAILURE(status)) { |
| 797 return appendTo; |
| 798 } |
| 799 UDate millis = |
| 800 (UDate) (((uprv_trunc(hms[0].getDouble(status)) * 60.0 |
| 801 + uprv_trunc(hms[1].getDouble(status))) * 60.0 |
| 802 + uprv_trunc(hms[2].getDouble(status))) * 1000.0); |
| 803 switch (bitMap) { |
| 804 case 5: // hs |
| 805 case 7: // hms |
| 806 return formatNumeric( |
| 807 millis, |
| 808 cache->getNumericDateFormatters()->hourMinuteSecond, |
| 809 UDAT_SECOND_FIELD, |
| 810 hms[2], |
| 811 appendTo, |
| 812 status); |
| 813 break; |
| 814 case 6: // ms |
| 815 return formatNumeric( |
| 816 millis, |
| 817 cache->getNumericDateFormatters()->minuteSecond, |
| 818 UDAT_SECOND_FIELD, |
| 819 hms[2], |
| 820 appendTo, |
| 821 status); |
| 822 break; |
| 823 case 3: // hm |
| 824 return formatNumeric( |
| 825 millis, |
| 826 cache->getNumericDateFormatters()->hourMinute, |
| 827 UDAT_MINUTE_FIELD, |
| 828 hms[1], |
| 829 appendTo, |
| 830 status); |
| 831 break; |
| 832 default: |
| 833 status = U_INTERNAL_PROGRAM_ERROR; |
| 834 return appendTo; |
| 835 break; |
| 836 } |
| 837 return appendTo; |
| 838 } |
| 839 |
| 840 static void appendRange( |
| 841 const UnicodeString &src, |
| 842 int32_t start, |
| 843 int32_t end, |
| 844 UnicodeString &dest) { |
| 845 dest.append(src, start, end - start); |
| 846 } |
| 847 |
| 848 static void appendRange( |
| 849 const UnicodeString &src, |
| 850 int32_t end, |
| 851 UnicodeString &dest) { |
| 852 dest.append(src, end, src.length() - end); |
| 853 } |
| 854 |
| 855 // Formats time like 5:37:23 |
| 856 UnicodeString &MeasureFormat::formatNumeric( |
| 857 UDate date, // Time since epoch 1:30:00 would be 5400000 |
| 858 const DateFormat &dateFmt, // h:mm, m:ss, or h:mm:ss |
| 859 UDateFormatField smallestField, // seconds in 5:37:23.5 |
| 860 const Formattable &smallestAmount, // 23.5 for 5:37:23.5 |
| 861 UnicodeString &appendTo, |
| 862 UErrorCode &status) const { |
| 863 if (U_FAILURE(status)) { |
| 864 return appendTo; |
| 865 } |
| 866 // Format the smallest amount with this object's NumberFormat |
| 867 UnicodeString smallestAmountFormatted; |
| 868 |
| 869 // We keep track of the integer part of smallest amount so that |
| 870 // we can replace it later so that we get '0:00:09.3' instead of |
| 871 // '0:00:9.3' |
| 872 FieldPosition intFieldPosition(UNUM_INTEGER_FIELD); |
| 873 (*numberFormat)->format( |
| 874 smallestAmount, smallestAmountFormatted, intFieldPosition, status); |
| 875 if ( |
| 876 intFieldPosition.getBeginIndex() == 0 && |
| 877 intFieldPosition.getEndIndex() == 0) { |
| 878 status = U_INTERNAL_PROGRAM_ERROR; |
| 879 return appendTo; |
| 880 } |
| 881 |
| 882 // Format time. draft becomes something like '5:30:45' |
| 883 FieldPosition smallestFieldPosition(smallestField); |
| 884 UnicodeString draft; |
| 885 dateFmt.format(date, draft, smallestFieldPosition, status); |
| 886 |
| 887 // If we find field for smallest amount replace it with the formatted |
| 888 // smallest amount from above taking care to replace the integer part |
| 889 // with what is in original time. For example, If smallest amount |
| 890 // is 9.35s and the formatted time is 0:00:09 then 9.35 becomes 09.35 |
| 891 // and replacing yields 0:00:09.35 |
| 892 if (smallestFieldPosition.getBeginIndex() != 0 || |
| 893 smallestFieldPosition.getEndIndex() != 0) { |
| 894 appendRange(draft, 0, smallestFieldPosition.getBeginIndex(), appendTo); |
| 895 appendRange( |
| 896 smallestAmountFormatted, |
| 897 0, |
| 898 intFieldPosition.getBeginIndex(), |
| 899 appendTo); |
| 900 appendRange( |
| 901 draft, |
| 902 smallestFieldPosition.getBeginIndex(), |
| 903 smallestFieldPosition.getEndIndex(), |
| 904 appendTo); |
| 905 appendRange( |
| 906 smallestAmountFormatted, |
| 907 intFieldPosition.getEndIndex(), |
| 908 appendTo); |
| 909 appendRange( |
| 910 draft, |
| 911 smallestFieldPosition.getEndIndex(), |
| 912 appendTo); |
| 913 } else { |
| 914 appendTo.append(draft); |
| 915 } |
| 916 return appendTo; |
| 917 } |
| 918 |
| 919 const QuantityFormatter *MeasureFormat::getQuantityFormatter( |
| 920 int32_t index, |
| 921 int32_t widthIndex, |
| 922 UErrorCode &status) const { |
| 923 if (U_FAILURE(status)) { |
| 924 return NULL; |
| 925 } |
| 926 const QuantityFormatter *formatters = |
| 927 cache->formatters[index]; |
| 928 if (formatters[widthIndex].isValid()) { |
| 929 return &formatters[widthIndex]; |
| 930 } |
| 931 if (formatters[UMEASFMT_WIDTH_SHORT].isValid()) { |
| 932 return &formatters[UMEASFMT_WIDTH_SHORT]; |
| 933 } |
| 934 if (formatters[UMEASFMT_WIDTH_WIDE].isValid()) { |
| 935 return &formatters[UMEASFMT_WIDTH_WIDE]; |
| 936 } |
| 937 status = U_MISSING_RESOURCE_ERROR; |
| 938 return NULL; |
| 939 } |
| 940 |
| 941 const SimplePatternFormatter *MeasureFormat::getPerUnitFormatter( |
| 942 int32_t index, |
| 943 int32_t widthIndex) const { |
| 944 const SimplePatternFormatter * const * perUnitFormatters = |
| 945 cache->getPerUnitFormattersByIndex(index); |
| 946 if (perUnitFormatters[widthIndex] != NULL) { |
| 947 return perUnitFormatters[widthIndex]; |
| 948 } |
| 949 if (perUnitFormatters[UMEASFMT_WIDTH_SHORT] != NULL) { |
| 950 return perUnitFormatters[UMEASFMT_WIDTH_SHORT]; |
| 951 } |
| 952 if (perUnitFormatters[UMEASFMT_WIDTH_WIDE] != NULL) { |
| 953 return perUnitFormatters[UMEASFMT_WIDTH_WIDE]; |
| 954 } |
| 955 return NULL; |
| 956 } |
| 957 |
| 958 const SimplePatternFormatter *MeasureFormat::getPerFormatter( |
| 959 int32_t widthIndex, |
| 960 UErrorCode &status) const { |
| 961 if (U_FAILURE(status)) { |
| 962 return NULL; |
| 963 } |
| 964 const SimplePatternFormatter * perFormatters = cache->perFormatters; |
| 965 |
| 966 if (perFormatters[widthIndex].getPlaceholderCount() == 2) { |
| 967 return &perFormatters[widthIndex]; |
| 968 } |
| 969 if (perFormatters[UMEASFMT_WIDTH_SHORT].getPlaceholderCount() == 2) { |
| 970 return &perFormatters[UMEASFMT_WIDTH_SHORT]; |
| 971 } |
| 972 if (perFormatters[UMEASFMT_WIDTH_WIDE].getPlaceholderCount() == 2) { |
| 973 return &perFormatters[UMEASFMT_WIDTH_WIDE]; |
| 974 } |
| 975 status = U_MISSING_RESOURCE_ERROR; |
| 976 return NULL; |
| 977 } |
| 978 |
| 979 static void getPerUnitString( |
| 980 const QuantityFormatter &formatter, |
| 981 UnicodeString &result) { |
| 982 result = formatter.getByVariant("one")->getPatternWithNoPlaceholders(); |
| 983 result.trim(); |
| 984 } |
| 985 |
| 986 int32_t MeasureFormat::withPerUnit( |
| 987 const UnicodeString &formatted, |
| 988 const MeasureUnit &perUnit, |
| 989 UnicodeString &appendTo, |
| 990 UErrorCode &status) const { |
| 991 int32_t offset = -1; |
| 992 if (U_FAILURE(status)) { |
| 993 return offset; |
| 994 } |
| 995 const SimplePatternFormatter *perUnitFormatter = getPerUnitFormatter( |
| 996 perUnit.getIndex(), widthToIndex(width)); |
| 997 if (perUnitFormatter != NULL) { |
| 998 const UnicodeString *params[] = {&formatted}; |
| 999 perUnitFormatter->format( |
| 1000 params, |
| 1001 UPRV_LENGTHOF(params), |
| 1002 appendTo, |
| 1003 &offset, |
| 1004 1, |
| 1005 status); |
| 1006 return offset; |
| 1007 } |
| 1008 const SimplePatternFormatter *perFormatter = getPerFormatter( |
| 1009 widthToIndex(width), status); |
| 1010 const QuantityFormatter *qf = getQuantityFormatter( |
| 1011 perUnit.getIndex(), widthToIndex(width), status); |
| 1012 if (U_FAILURE(status)) { |
| 1013 return offset; |
| 1014 } |
| 1015 UnicodeString perUnitString; |
| 1016 getPerUnitString(*qf, perUnitString); |
| 1017 const UnicodeString *params[] = {&formatted, &perUnitString}; |
| 1018 perFormatter->format( |
| 1019 params, |
| 1020 UPRV_LENGTHOF(params), |
| 1021 appendTo, |
| 1022 &offset, |
| 1023 1, |
| 1024 status); |
| 1025 return offset; |
| 1026 } |
| 1027 |
| 1028 UnicodeString &MeasureFormat::formatMeasuresSlowTrack( |
| 1029 const Measure *measures, |
| 1030 int32_t measureCount, |
| 1031 UnicodeString& appendTo, |
| 1032 FieldPosition& pos, |
| 1033 UErrorCode& status) const { |
| 1034 if (U_FAILURE(status)) { |
| 1035 return appendTo; |
| 1036 } |
| 1037 FieldPosition dontCare(FieldPosition::DONT_CARE); |
| 1038 FieldPosition fpos(pos.getField()); |
| 1039 UnicodeString *results = new UnicodeString[measureCount]; |
| 1040 int32_t fieldPositionFoundIndex = -1; |
| 1041 for (int32_t i = 0; i < measureCount; ++i) { |
| 1042 const NumberFormat *nf = cache->getIntegerFormat(); |
| 1043 if (i == measureCount - 1) { |
| 1044 nf = numberFormat->get(); |
| 1045 } |
| 1046 if (fieldPositionFoundIndex == -1) { |
| 1047 formatMeasure(measures[i], *nf, results[i], fpos, status); |
| 1048 if (U_FAILURE(status)) { |
| 1049 delete [] results; |
| 1050 return appendTo; |
| 1051 } |
| 1052 if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) { |
| 1053 fieldPositionFoundIndex = i; |
| 1054 } |
| 1055 } else { |
| 1056 formatMeasure(measures[i], *nf, results[i], dontCare, status); |
| 1057 } |
| 1058 } |
| 1059 int32_t offset; |
| 1060 listFormatter->format( |
| 1061 results, |
| 1062 measureCount, |
| 1063 appendTo, |
| 1064 fieldPositionFoundIndex, |
| 1065 offset, |
| 1066 status); |
| 1067 if (U_FAILURE(status)) { |
| 1068 delete [] results; |
| 1069 return appendTo; |
| 1070 } |
| 1071 if (offset != -1) { |
| 1072 pos.setBeginIndex(fpos.getBeginIndex() + offset); |
| 1073 pos.setEndIndex(fpos.getEndIndex() + offset); |
| 1074 } |
| 1075 delete [] results; |
| 1076 return appendTo; |
| 1077 } |
23 | 1078 |
24 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& local
e, | 1079 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& local
e, |
25 UErrorCode& ec) { | 1080 UErrorCode& ec) { |
26 CurrencyFormat* fmt = NULL; | 1081 CurrencyFormat* fmt = NULL; |
27 if (U_SUCCESS(ec)) { | 1082 if (U_SUCCESS(ec)) { |
28 fmt = new CurrencyFormat(locale, ec); | 1083 fmt = new CurrencyFormat(locale, ec); |
29 if (U_FAILURE(ec)) { | 1084 if (U_FAILURE(ec)) { |
30 delete fmt; | 1085 delete fmt; |
31 fmt = NULL; | 1086 fmt = NULL; |
32 } | 1087 } |
33 } | 1088 } |
34 return fmt; | 1089 return fmt; |
35 } | 1090 } |
36 | 1091 |
37 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) { | 1092 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) { |
38 if (U_FAILURE(ec)) { | 1093 if (U_FAILURE(ec)) { |
39 return NULL; | 1094 return NULL; |
40 } | 1095 } |
41 return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec); | 1096 return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec); |
42 } | 1097 } |
43 | 1098 |
44 U_NAMESPACE_END | 1099 U_NAMESPACE_END |
45 | 1100 |
46 #endif /* #if !UCONFIG_NO_FORMATTING */ | 1101 #endif /* #if !UCONFIG_NO_FORMATTING */ |
OLD | NEW |