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