measfmt.cpp (29050B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ********************************************************************** 5 * Copyright (c) 2004-2016, International Business Machines 6 * Corporation and others. All Rights Reserved. 7 ********************************************************************** 8 * Author: Alan Liu 9 * Created: April 20, 2004 10 * Since: ICU 3.0 11 ********************************************************************** 12 */ 13 #include "utypeinfo.h" // for 'typeid' to work 14 #include "unicode/utypes.h" 15 16 #if !UCONFIG_NO_FORMATTING 17 18 #include "unicode/measfmt.h" 19 #include "unicode/numfmt.h" 20 #include "currfmt.h" 21 #include "unicode/localpointer.h" 22 #include "resource.h" 23 #include "unicode/simpleformatter.h" 24 #include "quantityformatter.h" 25 #include "unicode/plurrule.h" 26 #include "unicode/decimfmt.h" 27 #include "uresimp.h" 28 #include "unicode/ures.h" 29 #include "unicode/ustring.h" 30 #include "ureslocs.h" 31 #include "cstring.h" 32 #include "mutex.h" 33 #include "ucln_in.h" 34 #include "unicode/listformatter.h" 35 #include "charstr.h" 36 #include "unicode/putil.h" 37 #include "unicode/smpdtfmt.h" 38 #include "uassert.h" 39 #include "unicode/numberformatter.h" 40 #include "number_longnames.h" 41 #include "number_utypes.h" 42 43 #include "sharednumberformat.h" 44 #include "sharedpluralrules.h" 45 #include "standardplural.h" 46 #include "unifiedcache.h" 47 48 49 U_NAMESPACE_BEGIN 50 51 using number::impl::UFormattedNumberData; 52 53 static constexpr int32_t WIDTH_INDEX_COUNT = UMEASFMT_WIDTH_NARROW + 1; 54 55 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat) 56 57 // Used to format durations like 5:47 or 21:35:42. 58 class NumericDateFormatters : public UMemory { 59 public: 60 // Formats like H:mm 61 UnicodeString hourMinute; 62 63 // formats like M:ss 64 UnicodeString minuteSecond; 65 66 // formats like H:mm:ss 67 UnicodeString hourMinuteSecond; 68 69 // Constructor that takes the actual patterns for hour-minute, 70 // minute-second, and hour-minute-second respectively. 71 NumericDateFormatters( 72 const UnicodeString &hm, 73 const UnicodeString &ms, 74 const UnicodeString &hms) : 75 hourMinute(hm), 76 minuteSecond(ms), 77 hourMinuteSecond(hms) { 78 } 79 private: 80 NumericDateFormatters(const NumericDateFormatters &other); 81 NumericDateFormatters &operator=(const NumericDateFormatters &other); 82 }; 83 84 static UMeasureFormatWidth getRegularWidth(UMeasureFormatWidth width) { 85 if (width >= WIDTH_INDEX_COUNT) { 86 return UMEASFMT_WIDTH_NARROW; 87 } 88 return width; 89 } 90 91 static UNumberUnitWidth getUnitWidth(UMeasureFormatWidth width) { 92 switch (width) { 93 case UMEASFMT_WIDTH_WIDE: 94 return UNUM_UNIT_WIDTH_FULL_NAME; 95 case UMEASFMT_WIDTH_NARROW: 96 case UMEASFMT_WIDTH_NUMERIC: 97 return UNUM_UNIT_WIDTH_NARROW; 98 case UMEASFMT_WIDTH_SHORT: 99 default: 100 return UNUM_UNIT_WIDTH_SHORT; 101 } 102 } 103 104 /** 105 * Instances contain all MeasureFormat specific data for a particular locale. 106 * This data is cached. It is never copied, but is shared via shared pointers. 107 * 108 * Note: We might change the cache data to have an array[WIDTH_INDEX_COUNT] of 109 * complete sets of unit & per patterns, 110 * to correspond to the resource data and its aliases. 111 * 112 * TODO: Maybe store more sparsely in general, with pointers rather than potentially-empty objects. 113 */ 114 class MeasureFormatCacheData : public SharedObject { 115 public: 116 117 /** 118 * Redirection data from root-bundle, top-level sideways aliases. 119 * - UMEASFMT_WIDTH_COUNT: initial value, just fall back to root 120 * - UMEASFMT_WIDTH_WIDE/SHORT/NARROW: sideways alias for missing data 121 */ 122 UMeasureFormatWidth widthFallback[WIDTH_INDEX_COUNT]; 123 124 MeasureFormatCacheData(); 125 virtual ~MeasureFormatCacheData(); 126 127 void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) { 128 delete currencyFormats[widthIndex]; 129 currencyFormats[widthIndex] = nfToAdopt; 130 } 131 const NumberFormat *getCurrencyFormat(UMeasureFormatWidth width) const { 132 return currencyFormats[getRegularWidth(width)]; 133 } 134 void adoptIntegerFormat(NumberFormat *nfToAdopt) { 135 delete integerFormat; 136 integerFormat = nfToAdopt; 137 } 138 const NumberFormat *getIntegerFormat() const { 139 return integerFormat; 140 } 141 void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) { 142 delete numericDateFormatters; 143 numericDateFormatters = formattersToAdopt; 144 } 145 const NumericDateFormatters *getNumericDateFormatters() const { 146 return numericDateFormatters; 147 } 148 149 private: 150 NumberFormat* currencyFormats[WIDTH_INDEX_COUNT]; 151 NumberFormat* integerFormat; 152 NumericDateFormatters* numericDateFormatters; 153 154 MeasureFormatCacheData(const MeasureFormatCacheData &other); 155 MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other); 156 }; 157 158 MeasureFormatCacheData::MeasureFormatCacheData() 159 : integerFormat(nullptr), numericDateFormatters(nullptr) { 160 for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { 161 widthFallback[i] = UMEASFMT_WIDTH_COUNT; 162 } 163 memset(currencyFormats, 0, sizeof(currencyFormats)); 164 } 165 166 MeasureFormatCacheData::~MeasureFormatCacheData() { 167 for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) { 168 delete currencyFormats[i]; 169 } 170 // Note: the contents of 'dnams' are pointers into the resource bundle 171 delete integerFormat; 172 delete numericDateFormatters; 173 } 174 175 static UBool isCurrency(const MeasureUnit &unit) { 176 return (uprv_strcmp(unit.getType(), "currency") == 0); 177 } 178 179 static UBool getString( 180 const UResourceBundle *resource, 181 UnicodeString &result, 182 UErrorCode &status) { 183 int32_t len = 0; 184 const char16_t *resStr = ures_getString(resource, &len, &status); 185 if (U_FAILURE(status)) { 186 return false; 187 } 188 result.setTo(true, resStr, len); 189 return true; 190 } 191 192 static UnicodeString loadNumericDateFormatterPattern( 193 const UResourceBundle *resource, 194 const char *pattern, 195 UErrorCode &status) { 196 UnicodeString result; 197 if (U_FAILURE(status)) { 198 return result; 199 } 200 CharString chs; 201 chs.append("durationUnits", status) 202 .append("/", status).append(pattern, status); 203 LocalUResourceBundlePointer patternBundle( 204 ures_getByKeyWithFallback( 205 resource, 206 chs.data(), 207 nullptr, 208 &status)); 209 if (U_FAILURE(status)) { 210 return result; 211 } 212 getString(patternBundle.getAlias(), result, status); 213 // Replace 'h' with 'H' 214 int32_t len = result.length(); 215 char16_t *buffer = result.getBuffer(len); 216 for (int32_t i = 0; i < len; ++i) { 217 if (buffer[i] == 0x68) { // 'h' 218 buffer[i] = 0x48; // 'H' 219 } 220 } 221 result.releaseBuffer(len); 222 return result; 223 } 224 225 static NumericDateFormatters *loadNumericDateFormatters( 226 const UResourceBundle *resource, 227 UErrorCode &status) { 228 if (U_FAILURE(status)) { 229 return nullptr; 230 } 231 NumericDateFormatters *result = new NumericDateFormatters( 232 loadNumericDateFormatterPattern(resource, "hm", status), 233 loadNumericDateFormatterPattern(resource, "ms", status), 234 loadNumericDateFormatterPattern(resource, "hms", status)); 235 if (U_FAILURE(status)) { 236 delete result; 237 return nullptr; 238 } 239 return result; 240 } 241 242 template<> 243 const MeasureFormatCacheData *LocaleCacheKey<MeasureFormatCacheData>::createObject( 244 const void * /*unused*/, UErrorCode &status) const { 245 const char *localeId = fLoc.getName(); 246 LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, localeId, &status)); 247 static UNumberFormatStyle currencyStyles[] = { 248 UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY}; 249 LocalPointer<MeasureFormatCacheData> result(new MeasureFormatCacheData(), status); 250 if (U_FAILURE(status)) { 251 return nullptr; 252 } 253 result->adoptNumericDateFormatters(loadNumericDateFormatters( 254 unitsBundle.getAlias(), status)); 255 if (U_FAILURE(status)) { 256 return nullptr; 257 } 258 259 for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { 260 // NumberFormat::createInstance can erase warning codes from status, so pass it 261 // a separate status instance 262 UErrorCode localStatus = U_ZERO_ERROR; 263 result->adoptCurrencyFormat(i, NumberFormat::createInstance( 264 localeId, currencyStyles[i], localStatus)); 265 if (localStatus != U_ZERO_ERROR) { 266 status = localStatus; 267 } 268 if (U_FAILURE(status)) { 269 return nullptr; 270 } 271 } 272 NumberFormat *inf = NumberFormat::createInstance( 273 localeId, UNUM_DECIMAL, status); 274 if (U_FAILURE(status)) { 275 return nullptr; 276 } 277 inf->setMaximumFractionDigits(0); 278 DecimalFormat *decfmt = dynamic_cast<DecimalFormat *>(inf); 279 if (decfmt != nullptr) { 280 decfmt->setRoundingMode(DecimalFormat::kRoundDown); 281 } 282 result->adoptIntegerFormat(inf); 283 result->addRef(); 284 return result.orphan(); 285 } 286 287 static UBool isTimeUnit(const MeasureUnit &mu, const char *tu) { 288 return uprv_strcmp(mu.getType(), "duration") == 0 && 289 uprv_strcmp(mu.getSubtype(), tu) == 0; 290 } 291 292 // Converts a composite measure into hours-minutes-seconds and stores at hms 293 // array. [0] is hours; [1] is minutes; [2] is seconds. Returns a bit map of 294 // units found: 1=hours, 2=minutes, 4=seconds. For example, if measures 295 // contains hours-minutes, this function would return 3. 296 // 297 // If measures cannot be converted into hours, minutes, seconds or if amounts 298 // are negative, or if hours, minutes, seconds are out of order, returns 0. 299 static int32_t toHMS( 300 const Measure *measures, 301 int32_t measureCount, 302 Formattable *hms, 303 UErrorCode &status) { 304 if (U_FAILURE(status)) { 305 return 0; 306 } 307 int32_t result = 0; 308 if (U_FAILURE(status)) { 309 return 0; 310 } 311 // We use copy constructor to ensure that both sides of equality operator 312 // are instances of MeasureUnit base class and not a subclass. Otherwise, 313 // operator== will immediately return false. 314 for (int32_t i = 0; i < measureCount; ++i) { 315 if (isTimeUnit(measures[i].getUnit(), "hour")) { 316 // hour must come first 317 if (result >= 1) { 318 return 0; 319 } 320 hms[0] = measures[i].getNumber(); 321 if (hms[0].getDouble() < 0.0) { 322 return 0; 323 } 324 result |= 1; 325 } else if (isTimeUnit(measures[i].getUnit(), "minute")) { 326 // minute must come after hour 327 if (result >= 2) { 328 return 0; 329 } 330 hms[1] = measures[i].getNumber(); 331 if (hms[1].getDouble() < 0.0) { 332 return 0; 333 } 334 result |= 2; 335 } else if (isTimeUnit(measures[i].getUnit(), "second")) { 336 // second must come after hour and minute 337 if (result >= 4) { 338 return 0; 339 } 340 hms[2] = measures[i].getNumber(); 341 if (hms[2].getDouble() < 0.0) { 342 return 0; 343 } 344 result |= 4; 345 } else { 346 return 0; 347 } 348 } 349 return result; 350 } 351 352 353 MeasureFormat::MeasureFormat( 354 const Locale &locale, UMeasureFormatWidth w, UErrorCode &status) 355 : cache(nullptr), 356 numberFormat(nullptr), 357 pluralRules(nullptr), 358 fWidth(w), 359 listFormatter(nullptr) { 360 initMeasureFormat(locale, w, nullptr, status); 361 } 362 363 MeasureFormat::MeasureFormat( 364 const Locale &locale, 365 UMeasureFormatWidth w, 366 NumberFormat *nfToAdopt, 367 UErrorCode &status) 368 : cache(nullptr), 369 numberFormat(nullptr), 370 pluralRules(nullptr), 371 fWidth(w), 372 listFormatter(nullptr) { 373 initMeasureFormat(locale, w, nfToAdopt, status); 374 } 375 376 MeasureFormat::MeasureFormat(const MeasureFormat &other) : 377 Format(other), 378 cache(other.cache), 379 numberFormat(other.numberFormat), 380 pluralRules(other.pluralRules), 381 fWidth(other.fWidth), 382 listFormatter(nullptr) { 383 cache->addRef(); 384 numberFormat->addRef(); 385 pluralRules->addRef(); 386 if (other.listFormatter != nullptr) { 387 listFormatter = new ListFormatter(*other.listFormatter); 388 } 389 } 390 391 MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) { 392 if (this == &other) { 393 return *this; 394 } 395 Format::operator=(other); 396 SharedObject::copyPtr(other.cache, cache); 397 SharedObject::copyPtr(other.numberFormat, numberFormat); 398 SharedObject::copyPtr(other.pluralRules, pluralRules); 399 fWidth = other.fWidth; 400 delete listFormatter; 401 if (other.listFormatter != nullptr) { 402 listFormatter = new ListFormatter(*other.listFormatter); 403 } else { 404 listFormatter = nullptr; 405 } 406 return *this; 407 } 408 409 MeasureFormat::MeasureFormat() : 410 cache(nullptr), 411 numberFormat(nullptr), 412 pluralRules(nullptr), 413 fWidth(UMEASFMT_WIDTH_SHORT), 414 listFormatter(nullptr) { 415 } 416 417 MeasureFormat::~MeasureFormat() { 418 if (cache != nullptr) { 419 cache->removeRef(); 420 } 421 if (numberFormat != nullptr) { 422 numberFormat->removeRef(); 423 } 424 if (pluralRules != nullptr) { 425 pluralRules->removeRef(); 426 } 427 delete listFormatter; 428 } 429 430 bool MeasureFormat::operator==(const Format &other) const { 431 if (this == &other) { // Same object, equal 432 return true; 433 } 434 if (!Format::operator==(other)) { 435 return false; 436 } 437 const MeasureFormat &rhs = static_cast<const MeasureFormat &>(other); 438 439 // Note: Since the ListFormatter depends only on Locale and width, we 440 // don't have to check it here. 441 442 // differing widths aren't equivalent 443 if (fWidth != rhs.fWidth) { 444 return false; 445 } 446 // Width the same check locales. 447 // We don't need to check locales if both objects have same cache. 448 if (cache != rhs.cache) { 449 UErrorCode status = U_ZERO_ERROR; 450 const char *localeId = getLocaleID(status); 451 const char *rhsLocaleId = rhs.getLocaleID(status); 452 if (U_FAILURE(status)) { 453 // On failure, assume not equal 454 return false; 455 } 456 if (uprv_strcmp(localeId, rhsLocaleId) != 0) { 457 return false; 458 } 459 } 460 // Locales same, check NumberFormat if shared data differs. 461 return ( 462 numberFormat == rhs.numberFormat || 463 **numberFormat == **rhs.numberFormat); 464 } 465 466 MeasureFormat *MeasureFormat::clone() const { 467 return new MeasureFormat(*this); 468 } 469 470 UnicodeString &MeasureFormat::format( 471 const Formattable &obj, 472 UnicodeString &appendTo, 473 FieldPosition &pos, 474 UErrorCode &status) const { 475 if (U_FAILURE(status)) return appendTo; 476 if (obj.getType() == Formattable::kObject) { 477 const UObject* formatObj = obj.getObject(); 478 const Measure* amount = dynamic_cast<const Measure*>(formatObj); 479 if (amount != nullptr) { 480 return formatMeasure( 481 *amount, **numberFormat, appendTo, pos, status); 482 } 483 } 484 status = U_ILLEGAL_ARGUMENT_ERROR; 485 return appendTo; 486 } 487 488 void MeasureFormat::parseObject( 489 const UnicodeString & /*source*/, 490 Formattable & /*result*/, 491 ParsePosition& /*pos*/) const { 492 } 493 494 UnicodeString &MeasureFormat::formatMeasurePerUnit( 495 const Measure &measure, 496 const MeasureUnit &perUnit, 497 UnicodeString &appendTo, 498 FieldPosition &pos, 499 UErrorCode &status) const { 500 if (U_FAILURE(status)) { 501 return appendTo; 502 } 503 const auto* df = dynamic_cast<const DecimalFormat*>(&getNumberFormatInternal()); 504 if (df == nullptr) { 505 // Don't know how to handle other types of NumberFormat 506 status = U_UNSUPPORTED_ERROR; 507 return appendTo; 508 } 509 UFormattedNumberData result; 510 if (const auto* lnf = df->toNumberFormatter(status)) { 511 result.quantity.setToDouble(measure.getNumber().getDouble(status)); 512 lnf->unit(measure.getUnit()) 513 .perUnit(perUnit) 514 .unitWidth(getUnitWidth(fWidth)) 515 .formatImpl(&result, status); 516 } 517 DecimalFormat::fieldPositionHelper(result, pos, appendTo.length(), status); 518 appendTo.append(result.toTempString(status)); 519 return appendTo; 520 } 521 522 UnicodeString &MeasureFormat::formatMeasures( 523 const Measure *measures, 524 int32_t measureCount, 525 UnicodeString &appendTo, 526 FieldPosition &pos, 527 UErrorCode &status) const { 528 if (U_FAILURE(status)) { 529 return appendTo; 530 } 531 if (measureCount == 0) { 532 return appendTo; 533 } 534 if (measureCount == 1) { 535 return formatMeasure(measures[0], **numberFormat, appendTo, pos, status); 536 } 537 if (fWidth == UMEASFMT_WIDTH_NUMERIC) { 538 Formattable hms[3]; 539 int32_t bitMap = toHMS(measures, measureCount, hms, status); 540 if (bitMap > 0) { 541 return formatNumeric(hms, bitMap, appendTo, status); 542 } 543 } 544 if (pos.getField() != FieldPosition::DONT_CARE) { 545 return formatMeasuresSlowTrack( 546 measures, measureCount, appendTo, pos, status); 547 } 548 UnicodeString *results = new UnicodeString[measureCount]; 549 if (results == nullptr) { 550 status = U_MEMORY_ALLOCATION_ERROR; 551 return appendTo; 552 } 553 for (int32_t i = 0; i < measureCount; ++i) { 554 const NumberFormat *nf = cache->getIntegerFormat(); 555 if (i == measureCount - 1) { 556 nf = numberFormat->get(); 557 } 558 formatMeasure( 559 measures[i], 560 *nf, 561 results[i], 562 pos, 563 status); 564 } 565 listFormatter->format(results, measureCount, appendTo, status); 566 delete [] results; 567 return appendTo; 568 } 569 570 UnicodeString MeasureFormat::getUnitDisplayName(const MeasureUnit& unit, UErrorCode& status) const { 571 return number::impl::LongNameHandler::getUnitDisplayName( 572 getLocale(status), 573 unit, 574 getUnitWidth(fWidth), 575 status); 576 } 577 578 void MeasureFormat::initMeasureFormat( 579 const Locale &locale, 580 UMeasureFormatWidth w, 581 NumberFormat *nfToAdopt, 582 UErrorCode &status) { 583 static const UListFormatterWidth listWidths[] = { 584 ULISTFMT_WIDTH_WIDE, 585 ULISTFMT_WIDTH_SHORT, 586 ULISTFMT_WIDTH_NARROW}; 587 LocalPointer<NumberFormat> nf(nfToAdopt); 588 if (U_FAILURE(status)) { 589 return; 590 } 591 const char *name = locale.getName(); 592 setLocaleIDs(name, name); 593 594 UnifiedCache::getByLocale(locale, cache, status); 595 if (U_FAILURE(status)) { 596 return; 597 } 598 599 const SharedPluralRules *pr = PluralRules::createSharedInstance( 600 locale, UPLURAL_TYPE_CARDINAL, status); 601 if (U_FAILURE(status)) { 602 return; 603 } 604 SharedObject::copyPtr(pr, pluralRules); 605 pr->removeRef(); 606 if (nf.isNull()) { 607 // TODO: Clean this up 608 const SharedNumberFormat *shared = NumberFormat::createSharedInstance( 609 locale, UNUM_DECIMAL, status); 610 if (U_FAILURE(status)) { 611 return; 612 } 613 SharedObject::copyPtr(shared, numberFormat); 614 shared->removeRef(); 615 } else { 616 adoptNumberFormat(nf.orphan(), status); 617 if (U_FAILURE(status)) { 618 return; 619 } 620 } 621 fWidth = w; 622 delete listFormatter; 623 listFormatter = ListFormatter::createInstance( 624 locale, 625 ULISTFMT_TYPE_UNITS, 626 listWidths[getRegularWidth(fWidth)], 627 status); 628 } 629 630 void MeasureFormat::adoptNumberFormat( 631 NumberFormat *nfToAdopt, UErrorCode &status) { 632 LocalPointer<NumberFormat> nf(nfToAdopt); 633 if (U_FAILURE(status)) { 634 return; 635 } 636 SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias()); 637 if (shared == nullptr) { 638 status = U_MEMORY_ALLOCATION_ERROR; 639 return; 640 } 641 nf.orphan(); 642 SharedObject::copyPtr(shared, numberFormat); 643 } 644 645 UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) { 646 if (U_FAILURE(status) || locale == getLocale(status)) { 647 return false; 648 } 649 initMeasureFormat(locale, fWidth, nullptr, status); 650 return U_SUCCESS(status); 651 } 652 653 const NumberFormat &MeasureFormat::getNumberFormatInternal() const { 654 return **numberFormat; 655 } 656 657 const NumberFormat &MeasureFormat::getCurrencyFormatInternal() const { 658 return *cache->getCurrencyFormat(UMEASFMT_WIDTH_NARROW); 659 } 660 661 const PluralRules &MeasureFormat::getPluralRules() const { 662 return **pluralRules; 663 } 664 665 Locale MeasureFormat::getLocale(UErrorCode &status) const { 666 return Format::getLocale(ULOC_VALID_LOCALE, status); 667 } 668 669 const char *MeasureFormat::getLocaleID(UErrorCode &status) const { 670 return Format::getLocaleID(ULOC_VALID_LOCALE, status); 671 } 672 673 UnicodeString &MeasureFormat::formatMeasure( 674 const Measure &measure, 675 const NumberFormat &nf, 676 UnicodeString &appendTo, 677 FieldPosition &pos, 678 UErrorCode &status) const { 679 if (U_FAILURE(status)) { 680 return appendTo; 681 } 682 const Formattable& amtNumber = measure.getNumber(); 683 const MeasureUnit& amtUnit = measure.getUnit(); 684 if (isCurrency(amtUnit)) { 685 char16_t isoCode[4]; 686 u_charsToUChars(amtUnit.getSubtype(), isoCode, 4); 687 return cache->getCurrencyFormat(fWidth)->format( 688 new CurrencyAmount(amtNumber, isoCode, status), 689 appendTo, 690 pos, 691 status); 692 } 693 const auto* df = dynamic_cast<const DecimalFormat*>(&nf); 694 if (df == nullptr) { 695 // Handle other types of NumberFormat using the ICU 63 code, modified to 696 // get the unitPattern from LongNameHandler and handle fallback to OTHER. 697 UnicodeString formattedNumber; 698 StandardPlural::Form pluralForm = QuantityFormatter::selectPlural( 699 amtNumber, nf, **pluralRules, formattedNumber, pos, status); 700 UnicodeString pattern = number::impl::LongNameHandler::getUnitPattern(getLocale(status), 701 amtUnit, getUnitWidth(fWidth), pluralForm, status); 702 // The above handles fallback from other widths to short, and from other plural forms to OTHER 703 if (U_FAILURE(status)) { 704 return appendTo; 705 } 706 SimpleFormatter formatter(pattern, 0, 1, status); 707 return QuantityFormatter::format(formatter, formattedNumber, appendTo, pos, status); 708 } 709 UFormattedNumberData result; 710 if (const auto* lnf = df->toNumberFormatter(status)) { 711 result.quantity.setToDouble(amtNumber.getDouble(status)); 712 lnf->unit(amtUnit) 713 .unitWidth(getUnitWidth(fWidth)) 714 .formatImpl(&result, status); 715 } 716 DecimalFormat::fieldPositionHelper(result, pos, appendTo.length(), status); 717 appendTo.append(result.toTempString(status)); 718 return appendTo; 719 } 720 721 722 // Formats numeric time duration as 5:00:47 or 3:54. 723 UnicodeString &MeasureFormat::formatNumeric( 724 const Formattable *hms, // always length 3 725 int32_t bitMap, // 1=hour set, 2=minute set, 4=second set 726 UnicodeString &appendTo, 727 UErrorCode &status) const { 728 if (U_FAILURE(status)) { 729 return appendTo; 730 } 731 732 UnicodeString pattern; 733 734 double hours = hms[0].getDouble(status); 735 double minutes = hms[1].getDouble(status); 736 double seconds = hms[2].getDouble(status); 737 if (U_FAILURE(status)) { 738 return appendTo; 739 } 740 741 // All possible combinations: "h", "m", "s", "hm", "hs", "ms", "hms" 742 if (bitMap == 5 || bitMap == 7) { // "hms" & "hs" (we add minutes if "hs") 743 pattern = cache->getNumericDateFormatters()->hourMinuteSecond; 744 hours = uprv_trunc(hours); 745 minutes = uprv_trunc(minutes); 746 } else if (bitMap == 3) { // "hm" 747 pattern = cache->getNumericDateFormatters()->hourMinute; 748 hours = uprv_trunc(hours); 749 } else if (bitMap == 6) { // "ms" 750 pattern = cache->getNumericDateFormatters()->minuteSecond; 751 minutes = uprv_trunc(minutes); 752 } else { // h m s, handled outside formatNumeric. No value is also an error. 753 status = U_INTERNAL_PROGRAM_ERROR; 754 return appendTo; 755 } 756 757 const DecimalFormat *numberFormatter = dynamic_cast<const DecimalFormat*>(numberFormat->get()); 758 if (!numberFormatter) { 759 status = U_INTERNAL_PROGRAM_ERROR; 760 return appendTo; 761 } 762 number::LocalizedNumberFormatter numberFormatter2; 763 if (const auto* lnf = numberFormatter->toNumberFormatter(status)) { 764 numberFormatter2 = lnf->integerWidth(number::IntegerWidth::zeroFillTo(2)); 765 } else { 766 return appendTo; 767 } 768 769 FormattedStringBuilder fsb; 770 771 UBool protect = false; 772 const int32_t patternLength = pattern.length(); 773 for (int32_t i = 0; i < patternLength; i++) { 774 char16_t c = pattern[i]; 775 776 // Also set the proper field in this switch 777 // We don't use DateFormat.Field because this is not a date / time, is a duration. 778 double value = 0; 779 switch (c) { 780 case u'H': value = hours; break; 781 case u'm': value = minutes; break; 782 case u's': value = seconds; break; 783 } 784 785 // There is not enough info to add Field(s) for the unit because all we have are plain 786 // text patterns. For example in "21:51" there is no text for something like "hour", 787 // while in something like "21h51" there is ("h"). But we can't really tell... 788 switch (c) { 789 case u'H': 790 case u'm': 791 case u's': 792 if (protect) { 793 fsb.appendChar16(c, kUndefinedField, status); 794 } else { 795 UnicodeString tmp; 796 if ((i + 1 < patternLength) && pattern[i + 1] == c) { // doubled 797 tmp = numberFormatter2.formatDouble(value, status).toString(status); 798 i++; 799 } else { 800 numberFormatter->format(value, tmp, status); 801 } 802 // TODO: Use proper Field 803 fsb.append(tmp, kUndefinedField, status); 804 } 805 break; 806 case u'\'': 807 // '' is escaped apostrophe 808 if ((i + 1 < patternLength) && pattern[i + 1] == c) { 809 fsb.appendChar16(c, kUndefinedField, status); 810 i++; 811 } else { 812 protect = !protect; 813 } 814 break; 815 default: 816 fsb.appendChar16(c, kUndefinedField, status); 817 } 818 } 819 820 appendTo.append(fsb.toTempUnicodeString()); 821 822 return appendTo; 823 } 824 825 UnicodeString &MeasureFormat::formatMeasuresSlowTrack( 826 const Measure *measures, 827 int32_t measureCount, 828 UnicodeString& appendTo, 829 FieldPosition& pos, 830 UErrorCode& status) const { 831 if (U_FAILURE(status)) { 832 return appendTo; 833 } 834 FieldPosition dontCare(FieldPosition::DONT_CARE); 835 FieldPosition fpos(pos.getField()); 836 LocalArray<UnicodeString> results(new UnicodeString[measureCount], status); 837 int32_t fieldPositionFoundIndex = -1; 838 for (int32_t i = 0; i < measureCount; ++i) { 839 const NumberFormat *nf = cache->getIntegerFormat(); 840 if (i == measureCount - 1) { 841 nf = numberFormat->get(); 842 } 843 if (fieldPositionFoundIndex == -1) { 844 formatMeasure(measures[i], *nf, results[i], fpos, status); 845 if (U_FAILURE(status)) { 846 return appendTo; 847 } 848 if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) { 849 fieldPositionFoundIndex = i; 850 } 851 } else { 852 formatMeasure(measures[i], *nf, results[i], dontCare, status); 853 } 854 } 855 int32_t offset; 856 listFormatter->format( 857 results.getAlias(), 858 measureCount, 859 appendTo, 860 fieldPositionFoundIndex, 861 offset, 862 status); 863 if (U_FAILURE(status)) { 864 return appendTo; 865 } 866 // Fix up FieldPosition indexes if our field is found. 867 if (fieldPositionFoundIndex != -1 && offset != -1) { 868 pos.setBeginIndex(fpos.getBeginIndex() + offset); 869 pos.setEndIndex(fpos.getEndIndex() + offset); 870 } 871 return appendTo; 872 } 873 874 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale, 875 UErrorCode& ec) { 876 if (U_FAILURE(ec)) { 877 return nullptr; 878 } 879 LocalPointer<CurrencyFormat> fmt(new CurrencyFormat(locale, ec), ec); 880 return fmt.orphan(); 881 } 882 883 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) { 884 if (U_FAILURE(ec)) { 885 return nullptr; 886 } 887 return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec); 888 } 889 890 U_NAMESPACE_END 891 892 #endif /* #if !UCONFIG_NO_FORMATTING */