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 "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) 42 43 U_NAMESPACE_BEGIN 44 45 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat) 46 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; ++currentWidth) { 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>::createObject( 347 const void * /*unused*/, UErrorCode &status) const { 348 const char *localeId = fLoc.getName(); 349 LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, localeId, &status)); 350 static UNumberFormatStyle currencyStyles[] = { 351 UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY}; 352 LocalPointer<MeasureFormatCacheData> result(new MeasureFormatCacheData(), status); 353 if (U_FAILURE(status)) { 354 return NULL; 355 } 356 if (!loadMeasureUnitData( 357 unitsBundle.getAlias(), 358 *result, 359 status)) { 360 return NULL; 361 } 362 result->adoptNumericDateFormatters(loadNumericDateFormatters( 363 unitsBundle.getAlias(), status)); 364 if (U_FAILURE(status)) { 365 return NULL; 366 } 367 368 for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { 369 result->adoptCurrencyFormat(i, NumberFormat::createInstance( 370 localeId, currencyStyles[i], status)); 371 if (U_FAILURE(status)) { 372 return NULL; 373 } 374 } 375 NumberFormat *inf = NumberFormat::createInstance( 376 localeId, UNUM_DECIMAL, status); 377 if (U_FAILURE(status)) { 378 return NULL; 379 } 380 inf->setMaximumFractionDigits(0); 381 DecimalFormat *decfmt = dynamic_cast<DecimalFormat *>(inf); 382 if (decfmt != NULL) { 383 decfmt->setRoundingMode(DecimalFormat::kRoundDown); 384 } 385 result->adoptIntegerFormat(inf); 386 result->addRef(); 387 return result.orphan(); 388 } 389 390 static UBool isTimeUnit(const MeasureUnit &mu, const char *tu) { 391 return uprv_strcmp(mu.getType(), "duration") == 0 && 392 uprv_strcmp(mu.getSubtype(), tu) == 0; 393 } 394 395 // Converts a composite measure into hours-minutes-seconds and stores at hms 396 // array. [0] is hours; [1] is minutes; [2] is seconds. Returns a bit map of 397 // units found: 1=hours, 2=minutes, 4=seconds. For example, if measures 398 // contains hours-minutes, this function would return 3. 399 // 400 // If measures cannot be converted into hours, minutes, seconds or if amounts 401 // are negative, or if hours, minutes, seconds are out of order, returns 0. 402 static int32_t toHMS( 403 const Measure *measures, 404 int32_t measureCount, 405 Formattable *hms, 406 UErrorCode &status) { 407 if (U_FAILURE(status)) { 408 return 0; 409 } 410 int32_t result = 0; 411 if (U_FAILURE(status)) { 412 return 0; 413 } 414 // We use copy constructor to ensure that both sides of equality operator 415 // are instances of MeasureUnit base class and not a subclass. Otherwise, 416 // operator== will immediately return false. 417 for (int32_t i = 0; i < measureCount; ++i) { 418 if (isTimeUnit(measures[i].getUnit(), "hour")) { 419 // hour must come first 420 if (result >= 1) { 421 return 0; 422 } 423 hms[0] = measures[i].getNumber(); 424 if (hms[0].getDouble() < 0.0) { 425 return 0; 426 } 427 result |= 1; 428 } else if (isTimeUnit(measures[i].getUnit(), "minute")) { 429 // minute must come after hour 430 if (result >= 2) { 431 return 0; 432 } 433 hms[1] = measures[i].getNumber(); 434 if (hms[1].getDouble() < 0.0) { 435 return 0; 436 } 437 result |= 2; 438 } else if (isTimeUnit(measures[i].getUnit(), "second")) { 439 // second must come after hour and minute 440 if (result >= 4) { 441 return 0; 442 } 443 hms[2] = measures[i].getNumber(); 444 if (hms[2].getDouble() < 0.0) { 445 return 0; 446 } 447 result |= 4; 448 } else { 449 return 0; 450 } 451 } 452 return result; 453 } 454 455 456 MeasureFormat::MeasureFormat( 457 const Locale &locale, UMeasureFormatWidth w, UErrorCode &status) 458 : cache(NULL), 459 numberFormat(NULL), 460 pluralRules(NULL), 461 width(w), 462 listFormatter(NULL) { 463 initMeasureFormat(locale, w, NULL, status); 464 } 465 466 MeasureFormat::MeasureFormat( 467 const Locale &locale, 468 UMeasureFormatWidth w, 469 NumberFormat *nfToAdopt, 470 UErrorCode &status) 471 : cache(NULL), 472 numberFormat(NULL), 473 pluralRules(NULL), 474 width(w), 475 listFormatter(NULL) { 476 initMeasureFormat(locale, w, nfToAdopt, status); 477 } 478 479 MeasureFormat::MeasureFormat(const MeasureFormat &other) : 480 Format(other), 481 cache(other.cache), 482 numberFormat(other.numberFormat), 483 pluralRules(other.pluralRules), 484 width(other.width), 485 listFormatter(NULL) { 486 cache->addRef(); 487 numberFormat->addRef(); 488 pluralRules->addRef(); 489 listFormatter = new ListFormatter(*other.listFormatter); 490 } 491 492 MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) { 493 if (this == &other) { 494 return *this; 495 } 496 Format::operator=(other); 497 SharedObject::copyPtr(other.cache, cache); 498 SharedObject::copyPtr(other.numberFormat, numberFormat); 499 SharedObject::copyPtr(other.pluralRules, pluralRules); 500 width = other.width; 501 delete listFormatter; 502 listFormatter = new ListFormatter(*other.listFormatter); 503 return *this; 504 } 505 506 MeasureFormat::MeasureFormat() : 507 cache(NULL), 508 numberFormat(NULL), 509 pluralRules(NULL), 510 width(UMEASFMT_WIDTH_WIDE), 511 listFormatter(NULL) { 512 } 513 514 MeasureFormat::~MeasureFormat() { 515 if (cache != NULL) { 516 cache->removeRef(); 517 } 518 if (numberFormat != NULL) { 519 numberFormat->removeRef(); 520 } 521 if (pluralRules != NULL) { 522 pluralRules->removeRef(); 523 } 524 delete listFormatter; 525 } 526 527 UBool MeasureFormat::operator==(const Format &other) const { 528 if (this == &other) { // Same object, equal 529 return TRUE; 530 } 531 if (!Format::operator==(other)) { 532 return FALSE; 533 } 534 const MeasureFormat &rhs = static_cast<const MeasureFormat &>(other); 535 536 // Note: Since the ListFormatter depends only on Locale and width, we 537 // don't have to check it here. 538 539 // differing widths aren't equivalent 540 if (width != rhs.width) { 541 return FALSE; 542 } 543 // Width the same check locales. 544 // We don't need to check locales if both objects have same cache. 545 if (cache != rhs.cache) { 546 UErrorCode status = U_ZERO_ERROR; 547 const char *localeId = getLocaleID(status); 548 const char *rhsLocaleId = rhs.getLocaleID(status); 549 if (U_FAILURE(status)) { 550 // On failure, assume not equal 551 return FALSE; 552 } 553 if (uprv_strcmp(localeId, rhsLocaleId) != 0) { 554 return FALSE; 555 } 556 } 557 // Locales same, check NumberFormat if shared data differs. 558 return ( 559 numberFormat == rhs.numberFormat || 560 **numberFormat == **rhs.numberFormat); 561 } 562 563 Format *MeasureFormat::clone() const { 564 return new MeasureFormat(*this); 565 } 566 567 UnicodeString &MeasureFormat::format( 568 const Formattable &obj, 569 UnicodeString &appendTo, 570 FieldPosition &pos, 571 UErrorCode &status) const { 572 if (U_FAILURE(status)) return appendTo; 573 if (obj.getType() == Formattable::kObject) { 574 const UObject* formatObj = obj.getObject(); 575 const Measure* amount = dynamic_cast<const Measure*>(formatObj); 576 if (amount != NULL) { 577 return formatMeasure( 578 *amount, **numberFormat, appendTo, pos, status); 579 } 580 } 581 status = U_ILLEGAL_ARGUMENT_ERROR; 582 return appendTo; 583 } 584 585 void MeasureFormat::parseObject( 586 const UnicodeString & /*source*/, 587 Formattable & /*result*/, 588 ParsePosition& /*pos*/) const { 589 return; 590 } 591 592 UnicodeString &MeasureFormat::formatMeasurePerUnit( 593 const Measure &measure, 594 const MeasureUnit &perUnit, 595 UnicodeString &appendTo, 596 FieldPosition &pos, 597 UErrorCode &status) const { 598 if (U_FAILURE(status)) { 599 return appendTo; 600 } 601 MeasureUnit *resolvedUnit = 602 MeasureUnit::resolveUnitPerUnit(measure.getUnit(), perUnit); 603 if (resolvedUnit != NULL) { 604 Measure newMeasure(measure.getNumber(), resolvedUnit, status); 605 return formatMeasure( 606 newMeasure, **numberFormat, appendTo, pos, status); 607 } 608 FieldPosition fpos(pos.getField()); 609 UnicodeString result; 610 int32_t offset = withPerUnitAndAppend( 611 formatMeasure( 612 measure, **numberFormat, result, fpos, status), 613 perUnit, 614 appendTo, 615 status); 616 if (U_FAILURE(status)) { 617 return appendTo; 618 } 619 if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) { 620 pos.setBeginIndex(fpos.getBeginIndex() + offset); 621 pos.setEndIndex(fpos.getEndIndex() + offset); 622 } 623 return appendTo; 624 } 625 626 UnicodeString &MeasureFormat::formatMeasures( 627 const Measure *measures, 628 int32_t measureCount, 629 UnicodeString &appendTo, 630 FieldPosition &pos, 631 UErrorCode &status) const { 632 if (U_FAILURE(status)) { 633 return appendTo; 634 } 635 if (measureCount == 0) { 636 return appendTo; 637 } 638 if (measureCount == 1) { 639 return formatMeasure(measures[0], **numberFormat, appendTo, pos, status); 640 } 641 if (width == UMEASFMT_WIDTH_NUMERIC) { 642 Formattable hms[3]; 643 int32_t bitMap = toHMS(measures, measureCount, hms, status); 644 if (bitMap > 0) { 645 return formatNumeric(hms, bitMap, appendTo, status); 646 } 647 } 648 if (pos.getField() != FieldPosition::DONT_CARE) { 649 return formatMeasuresSlowTrack( 650 measures, measureCount, appendTo, pos, status); 651 } 652 UnicodeString *results = new UnicodeString[measureCount]; 653 if (results == NULL) { 654 status = U_MEMORY_ALLOCATION_ERROR; 655 return appendTo; 656 } 657 for (int32_t i = 0; i < measureCount; ++i) { 658 const NumberFormat *nf = cache->getIntegerFormat(); 659 if (i == measureCount - 1) { 660 nf = numberFormat->get(); 661 } 662 formatMeasure( 663 measures[i], 664 *nf, 665 results[i], 666 pos, 667 status); 668 } 669 listFormatter->format(results, measureCount, appendTo, status); 670 delete [] results; 671 return appendTo; 672 } 673 674 void MeasureFormat::initMeasureFormat( 675 const Locale &locale, 676 UMeasureFormatWidth w, 677 NumberFormat *nfToAdopt, 678 UErrorCode &status) { 679 static const char *listStyles[] = {"unit", "unit-short", "unit-narrow"}; 680 LocalPointer<NumberFormat> nf(nfToAdopt); 681 if (U_FAILURE(status)) { 682 return; 683 } 684 const char *name = locale.getName(); 685 setLocaleIDs(name, name); 686 687 UnifiedCache::getByLocale(locale, cache, status); 688 if (U_FAILURE(status)) { 689 return; 690 } 691 692 const SharedPluralRules *pr = PluralRules::createSharedInstance( 693 locale, UPLURAL_TYPE_CARDINAL, status); 694 if (U_FAILURE(status)) { 695 return; 696 } 697 SharedObject::copyPtr(pr, pluralRules); 698 pr->removeRef(); 699 if (nf.isNull()) { 700 const SharedNumberFormat *shared = NumberFormat::createSharedInstance( 701 locale, UNUM_DECIMAL, status); 702 if (U_FAILURE(status)) { 703 return; 704 } 705 SharedObject::copyPtr(shared, numberFormat); 706 shared->removeRef(); 707 } else { 708 adoptNumberFormat(nf.orphan(), status); 709 if (U_FAILURE(status)) { 710 return; 711 } 712 } 713 width = w; 714 delete listFormatter; 715 listFormatter = ListFormatter::createInstance( 716 locale, 717 listStyles[widthToIndex(width)], 718 status); 719 } 720 721 void MeasureFormat::adoptNumberFormat( 722 NumberFormat *nfToAdopt, UErrorCode &status) { 723 LocalPointer<NumberFormat> nf(nfToAdopt); 724 if (U_FAILURE(status)) { 725 return; 726 } 727 SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias()); 728 if (shared == NULL) { 729 status = U_MEMORY_ALLOCATION_ERROR; 730 return; 731 } 732 nf.orphan(); 733 SharedObject::copyPtr(shared, numberFormat); 734 } 735 736 UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) { 737 if (U_FAILURE(status) || locale == getLocale(status)) { 738 return FALSE; 739 } 740 initMeasureFormat(locale, width, NULL, status); 741 return U_SUCCESS(status); 742 } 743 744 const NumberFormat &MeasureFormat::getNumberFormat() const { 745 return **numberFormat; 746 } 747 748 const PluralRules &MeasureFormat::getPluralRules() const { 749 return **pluralRules; 750 } 751 752 Locale MeasureFormat::getLocale(UErrorCode &status) const { 753 return Format::getLocale(ULOC_VALID_LOCALE, status); 754 } 755 756 const char *MeasureFormat::getLocaleID(UErrorCode &status) const { 757 return Format::getLocaleID(ULOC_VALID_LOCALE, status); 758 } 759 760 UnicodeString &MeasureFormat::formatMeasure( 761 const Measure &measure, 762 const NumberFormat &nf, 763 UnicodeString &appendTo, 764 FieldPosition &pos, 765 UErrorCode &status) const { 766 if (U_FAILURE(status)) { 767 return appendTo; 768 } 769 const Formattable& amtNumber = measure.getNumber(); 770 const MeasureUnit& amtUnit = measure.getUnit(); 771 if (isCurrency(amtUnit)) { 772 UChar isoCode[4]; 773 u_charsToUChars(amtUnit.getSubtype(), isoCode, 4); 774 return cache->getCurrencyFormat(widthToIndex(width))->format( 775 new CurrencyAmount(amtNumber, isoCode, status), 776 appendTo, 777 pos, 778 status); 779 } 780 const QuantityFormatter *quantityFormatter = getQuantityFormatter( 781 amtUnit.getIndex(), widthToIndex(width), status); 782 if (U_FAILURE(status)) { 783 return appendTo; 784 } 785 return quantityFormatter->format( 786 amtNumber, 787 nf, 788 **pluralRules, 789 appendTo, 790 pos, 791 status); 792 } 793 794 // Formats hours-minutes-seconds as 5:37:23 or similar. 795 UnicodeString &MeasureFormat::formatNumeric( 796 const Formattable *hms, // always length 3 797 int32_t bitMap, // 1=hourset, 2=minuteset, 4=secondset 798 UnicodeString &appendTo, 799 UErrorCode &status) const { 800 if (U_FAILURE(status)) { 801 return appendTo; 802 } 803 UDate millis = 804 (UDate) (((uprv_trunc(hms[0].getDouble(status)) * 60.0 805 + uprv_trunc(hms[1].getDouble(status))) * 60.0 806 + uprv_trunc(hms[2].getDouble(status))) * 1000.0); 807 switch (bitMap) { 808 case 5: // hs 809 case 7: // hms 810 return formatNumeric( 811 millis, 812 cache->getNumericDateFormatters()->hourMinuteSecond, 813 UDAT_SECOND_FIELD, 814 hms[2], 815 appendTo, 816 status); 817 break; 818 case 6: // ms 819 return formatNumeric( 820 millis, 821 cache->getNumericDateFormatters()->minuteSecond, 822 UDAT_SECOND_FIELD, 823 hms[2], 824 appendTo, 825 status); 826 break; 827 case 3: // hm 828 return formatNumeric( 829 millis, 830 cache->getNumericDateFormatters()->hourMinute, 831 UDAT_MINUTE_FIELD, 832 hms[1], 833 appendTo, 834 status); 835 break; 836 default: 837 status = U_INTERNAL_PROGRAM_ERROR; 838 return appendTo; 839 break; 840 } 841 return appendTo; 842 } 843 844 static void appendRange( 845 const UnicodeString &src, 846 int32_t start, 847 int32_t end, 848 UnicodeString &dest) { 849 dest.append(src, start, end - start); 850 } 851 852 static void appendRange( 853 const UnicodeString &src, 854 int32_t end, 855 UnicodeString &dest) { 856 dest.append(src, end, src.length() - end); 857 } 858 859 // Formats time like 5:37:23 860 UnicodeString &MeasureFormat::formatNumeric( 861 UDate date, // Time since epoch 1:30:00 would be 5400000 862 const DateFormat &dateFmt, // h:mm, m:ss, or h:mm:ss 863 UDateFormatField smallestField, // seconds in 5:37:23.5 864 const Formattable &smallestAmount, // 23.5 for 5:37:23.5 865 UnicodeString &appendTo, 866 UErrorCode &status) const { 867 if (U_FAILURE(status)) { 868 return appendTo; 869 } 870 // Format the smallest amount with this object's NumberFormat 871 UnicodeString smallestAmountFormatted; 872 873 // We keep track of the integer part of smallest amount so that 874 // we can replace it later so that we get '0:00:09.3' instead of 875 // '0:00:9.3' 876 FieldPosition intFieldPosition(UNUM_INTEGER_FIELD); 877 (*numberFormat)->format( 878 smallestAmount, smallestAmountFormatted, intFieldPosition, status); 879 if ( 880 intFieldPosition.getBeginIndex() == 0 && 881 intFieldPosition.getEndIndex() == 0) { 882 status = U_INTERNAL_PROGRAM_ERROR; 883 return appendTo; 884 } 885 886 // Format time. draft becomes something like '5:30:45' 887 FieldPosition smallestFieldPosition(smallestField); 888 UnicodeString draft; 889 dateFmt.format(date, draft, smallestFieldPosition, status); 890 891 // If we find field for smallest amount replace it with the formatted 892 // smallest amount from above taking care to replace the integer part 893 // with what is in original time. For example, If smallest amount 894 // is 9.35s and the formatted time is 0:00:09 then 9.35 becomes 09.35 895 // and replacing yields 0:00:09.35 896 if (smallestFieldPosition.getBeginIndex() != 0 || 897 smallestFieldPosition.getEndIndex() != 0) { 898 appendRange(draft, 0, smallestFieldPosition.getBeginIndex(), appendTo); 899 appendRange( 900 smallestAmountFormatted, 901 0, 902 intFieldPosition.getBeginIndex(), 903 appendTo); 904 appendRange( 905 draft, 906 smallestFieldPosition.getBeginIndex(), 907 smallestFieldPosition.getEndIndex(), 908 appendTo); 909 appendRange( 910 smallestAmountFormatted, 911 intFieldPosition.getEndIndex(), 912 appendTo); 913 appendRange( 914 draft, 915 smallestFieldPosition.getEndIndex(), 916 appendTo); 917 } else { 918 appendTo.append(draft); 919 } 920 return appendTo; 921 } 922 923 const QuantityFormatter *MeasureFormat::getQuantityFormatter( 924 int32_t index, 925 int32_t widthIndex, 926 UErrorCode &status) const { 927 if (U_FAILURE(status)) { 928 return NULL; 929 } 930 const QuantityFormatter *formatters = 931 cache->formatters[index]; 932 if (formatters[widthIndex].isValid()) { 933 return &formatters[widthIndex]; 934 } 935 if (formatters[UMEASFMT_WIDTH_SHORT].isValid()) { 936 return &formatters[UMEASFMT_WIDTH_SHORT]; 937 } 938 if (formatters[UMEASFMT_WIDTH_WIDE].isValid()) { 939 return &formatters[UMEASFMT_WIDTH_WIDE]; 940 } 941 status = U_MISSING_RESOURCE_ERROR; 942 return NULL; 943 } 944 945 const SimplePatternFormatter *MeasureFormat::getPerUnitFormatter( 946 int32_t index, 947 int32_t widthIndex) const { 948 const SimplePatternFormatter * const * perUnitFormatters = 949 cache->getPerUnitFormattersByIndex(index); 950 if (perUnitFormatters[widthIndex] != NULL) { 951 return perUnitFormatters[widthIndex]; 952 } 953 if (perUnitFormatters[UMEASFMT_WIDTH_SHORT] != NULL) { 954 return perUnitFormatters[UMEASFMT_WIDTH_SHORT]; 955 } 956 if (perUnitFormatters[UMEASFMT_WIDTH_WIDE] != NULL) { 957 return perUnitFormatters[UMEASFMT_WIDTH_WIDE]; 958 } 959 return NULL; 960 } 961 962 const SimplePatternFormatter *MeasureFormat::getPerFormatter( 963 int32_t widthIndex, 964 UErrorCode &status) const { 965 if (U_FAILURE(status)) { 966 return NULL; 967 } 968 const SimplePatternFormatter * perFormatters = cache->perFormatters; 969 970 if (perFormatters[widthIndex].getPlaceholderCount() == 2) { 971 return &perFormatters[widthIndex]; 972 } 973 if (perFormatters[UMEASFMT_WIDTH_SHORT].getPlaceholderCount() == 2) { 974 return &perFormatters[UMEASFMT_WIDTH_SHORT]; 975 } 976 if (perFormatters[UMEASFMT_WIDTH_WIDE].getPlaceholderCount() == 2) { 977 return &perFormatters[UMEASFMT_WIDTH_WIDE]; 978 } 979 status = U_MISSING_RESOURCE_ERROR; 980 return NULL; 981 } 982 983 static void getPerUnitString( 984 const QuantityFormatter &formatter, 985 UnicodeString &result) { 986 result = formatter.getByVariant("one")->getPatternWithNoPlaceholders(); 987 result.trim(); 988 } 989 990 int32_t MeasureFormat::withPerUnitAndAppend( 991 const UnicodeString &formatted, 992 const MeasureUnit &perUnit, 993 UnicodeString &appendTo, 994 UErrorCode &status) const { 995 int32_t offset = -1; 996 if (U_FAILURE(status)) { 997 return offset; 998 } 999 const SimplePatternFormatter *perUnitFormatter = getPerUnitFormatter( 1000 perUnit.getIndex(), widthToIndex(width)); 1001 if (perUnitFormatter != NULL) { 1002 const UnicodeString *params[] = {&formatted}; 1003 perUnitFormatter->formatAndAppend( 1004 params, 1005 UPRV_LENGTHOF(params), 1006 appendTo, 1007 &offset, 1008 1, 1009 status); 1010 return offset; 1011 } 1012 const SimplePatternFormatter *perFormatter = getPerFormatter( 1013 widthToIndex(width), status); 1014 const QuantityFormatter *qf = getQuantityFormatter( 1015 perUnit.getIndex(), widthToIndex(width), status); 1016 if (U_FAILURE(status)) { 1017 return offset; 1018 } 1019 UnicodeString perUnitString; 1020 getPerUnitString(*qf, perUnitString); 1021 const UnicodeString *params[] = {&formatted, &perUnitString}; 1022 perFormatter->formatAndAppend( 1023 params, 1024 UPRV_LENGTHOF(params), 1025 appendTo, 1026 &offset, 1027 1, 1028 status); 1029 return offset; 1030 } 1031 1032 UnicodeString &MeasureFormat::formatMeasuresSlowTrack( 1033 const Measure *measures, 1034 int32_t measureCount, 1035 UnicodeString& appendTo, 1036 FieldPosition& pos, 1037 UErrorCode& status) const { 1038 if (U_FAILURE(status)) { 1039 return appendTo; 1040 } 1041 FieldPosition dontCare(FieldPosition::DONT_CARE); 1042 FieldPosition fpos(pos.getField()); 1043 UnicodeString *results = new UnicodeString[measureCount]; 1044 int32_t fieldPositionFoundIndex = -1; 1045 for (int32_t i = 0; i < measureCount; ++i) { 1046 const NumberFormat *nf = cache->getIntegerFormat(); 1047 if (i == measureCount - 1) { 1048 nf = numberFormat->get(); 1049 } 1050 if (fieldPositionFoundIndex == -1) { 1051 formatMeasure(measures[i], *nf, results[i], fpos, status); 1052 if (U_FAILURE(status)) { 1053 delete [] results; 1054 return appendTo; 1055 } 1056 if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) { 1057 fieldPositionFoundIndex = i; 1058 } 1059 } else { 1060 formatMeasure(measures[i], *nf, results[i], dontCare, status); 1061 } 1062 } 1063 int32_t offset; 1064 listFormatter->format( 1065 results, 1066 measureCount, 1067 appendTo, 1068 fieldPositionFoundIndex, 1069 offset, 1070 status); 1071 if (U_FAILURE(status)) { 1072 delete [] results; 1073 return appendTo; 1074 } 1075 if (offset != -1) { 1076 pos.setBeginIndex(fpos.getBeginIndex() + offset); 1077 pos.setEndIndex(fpos.getEndIndex() + offset); 1078 } 1079 delete [] results; 1080 return appendTo; 1081 } 1082 1083 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale, 1084 UErrorCode& ec) { 1085 CurrencyFormat* fmt = NULL; 1086 if (U_SUCCESS(ec)) { 1087 fmt = new CurrencyFormat(locale, ec); 1088 if (U_FAILURE(ec)) { 1089 delete fmt; 1090 fmt = NULL; 1091 } 1092 } 1093 return fmt; 1094 } 1095 1096 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) { 1097 if (U_FAILURE(ec)) { 1098 return NULL; 1099 } 1100 return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec); 1101 } 1102 1103 U_NAMESPACE_END 1104 1105 #endif /* #if !UCONFIG_NO_FORMATTING */ 1106