2 **********************************************************************
3 * Copyright (c) 2004-2015, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 **********************************************************************
7 * Created: April 20, 2004
9 **********************************************************************
11 #include "utypeinfo.h" // for 'typeid' to work
12 #include "unicode/utypes.h"
14 #if !UCONFIG_NO_FORMATTING
16 #include "unicode/measfmt.h"
17 #include "unicode/numfmt.h"
19 #include "unicode/localpointer.h"
20 #include "simplepatternformatter.h"
21 #include "quantityformatter.h"
22 #include "unicode/plurrule.h"
23 #include "unicode/decimfmt.h"
25 #include "unicode/ures.h"
30 #include "unicode/listformatter.h"
32 #include "unicode/putil.h"
33 #include "unicode/smpdtfmt.h"
36 #include "sharednumberformat.h"
37 #include "sharedpluralrules.h"
38 #include "unifiedcache.h"
40 #define MEAS_UNIT_COUNT 122
41 #define WIDTH_INDEX_COUNT (UMEASFMT_WIDTH_NARROW + 1)
45 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat
)
47 // Used to format durations like 5:47 or 21:35:42.
48 class NumericDateFormatters
: public UMemory
{
51 SimpleDateFormat hourMinute
;
54 SimpleDateFormat minuteSecond
;
56 // formats like H:mm:ss
57 SimpleDateFormat hourMinuteSecond
;
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
,
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
);
75 NumericDateFormatters(const NumericDateFormatters
&other
);
76 NumericDateFormatters
&operator=(const NumericDateFormatters
&other
);
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
{
83 QuantityFormatter formatters
[MEAS_UNIT_COUNT
][WIDTH_INDEX_COUNT
];
84 SimplePatternFormatter perFormatters
[WIDTH_INDEX_COUNT
];
86 MeasureFormatCacheData();
87 void adoptCurrencyFormat(int32_t widthIndex
, NumberFormat
*nfToAdopt
) {
88 delete currencyFormats
[widthIndex
];
89 currencyFormats
[widthIndex
] = nfToAdopt
;
91 const NumberFormat
*getCurrencyFormat(int32_t widthIndex
) const {
92 return currencyFormats
[widthIndex
];
94 void adoptIntegerFormat(NumberFormat
*nfToAdopt
) {
96 integerFormat
= nfToAdopt
;
98 const NumberFormat
*getIntegerFormat() const {
101 void adoptNumericDateFormatters(NumericDateFormatters
*formattersToAdopt
) {
102 delete numericDateFormatters
;
103 numericDateFormatters
= formattersToAdopt
;
105 const NumericDateFormatters
*getNumericDateFormatters() const {
106 return numericDateFormatters
;
108 void adoptPerUnitFormatter(
111 SimplePatternFormatter
*formatterToAdopt
) {
112 delete perUnitFormatters
[index
][widthIndex
];
113 perUnitFormatters
[index
][widthIndex
] = formatterToAdopt
;
115 const SimplePatternFormatter
* const * getPerUnitFormattersByIndex(
116 int32_t index
) const {
117 return perUnitFormatters
[index
];
119 virtual ~MeasureFormatCacheData();
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
);
129 MeasureFormatCacheData::MeasureFormatCacheData() {
130 for (int32_t i
= 0; i
< UPRV_LENGTHOF(currencyFormats
); ++i
) {
131 currencyFormats
[i
] = NULL
;
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
;
138 integerFormat
= NULL
;
139 numericDateFormatters
= NULL
;
142 MeasureFormatCacheData::~MeasureFormatCacheData() {
143 for (int32_t i
= 0; i
< UPRV_LENGTHOF(currencyFormats
); ++i
) {
144 delete currencyFormats
[i
];
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
];
151 delete integerFormat
;
152 delete numericDateFormatters
;
155 static int32_t widthToIndex(UMeasureFormatWidth width
) {
156 if (width
>= WIDTH_INDEX_COUNT
) {
157 return WIDTH_INDEX_COUNT
- 1;
162 static UBool
isCurrency(const MeasureUnit
&unit
) {
163 return (uprv_strcmp(unit
.getType(), "currency") == 0);
166 static UBool
getString(
167 const UResourceBundle
*resource
,
168 UnicodeString
&result
,
169 UErrorCode
&status
) {
171 const UChar
*resStr
= ures_getString(resource
, &len
, &status
);
172 if (U_FAILURE(status
)) {
175 result
.setTo(TRUE
, resStr
, len
);
180 static UBool
loadMeasureUnitData(
181 const UResourceBundle
*resource
,
182 MeasureFormatCacheData
&cacheData
,
183 UErrorCode
&status
) {
184 if (U_FAILURE(status
)) {
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
;
193 units
= new MeasureUnit
[unitCount
];
195 status
= U_MEMORY_ALLOCATION_ERROR
;
198 unitCount
= MeasureUnit::getAvailable(units
, unitCount
, status
);
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
)) {
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
;
216 LocalUResourceBundlePointer
compoundPerBundle(
217 ures_getByKeyWithFallback(
218 widthBundle
.getAlias(),
222 if (U_FAILURE(status
)) {
223 status
= U_ZERO_ERROR
;
225 UnicodeString perPattern
;
226 getString(compoundPerBundle
.getAlias(), perPattern
, status
);
227 cacheData
.perFormatters
[currentWidth
].compile(perPattern
, status
);
230 for (int32_t currentUnit
= 0; currentUnit
< unitCount
; ++currentUnit
) {
231 // Be sure status is clear next lookup may fail.
232 if (U_FAILURE(status
)) {
236 if (isCurrency(units
[currentUnit
])) {
239 CharString pathBuffer
;
240 pathBuffer
.append(units
[currentUnit
].getType(), status
)
242 .append(units
[currentUnit
].getSubtype(), status
);
243 LocalUResourceBundlePointer
unitBundle(
244 ures_getByKeyWithFallback(
245 widthBundle
.getAlias(),
249 // We may not have data for all units in all widths
250 if (status
== U_MISSING_RESOURCE_ERROR
) {
251 status
= U_ZERO_ERROR
;
254 // We must have the unit bundle to proceed
255 if (U_FAILURE(status
)) {
259 int32_t size
= ures_getSize(unitBundle
.getAlias());
260 for (int32_t plIndex
= 0; plIndex
< size
; ++plIndex
) {
261 LocalUResourceBundlePointer
pluralBundle(
263 unitBundle
.getAlias(), plIndex
, NULL
, &status
));
264 if (U_FAILURE(status
)) {
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
272 if (uprv_strcmp(resKey
, "per") == 0) {
273 UnicodeString perPattern
;
274 getString(pluralBundle
.getAlias(), perPattern
, status
);
275 cacheData
.adoptPerUnitFormatter(
276 units
[currentUnit
].getIndex(),
278 new SimplePatternFormatter(perPattern
));
281 UnicodeString rawPattern
;
282 getString(pluralBundle
.getAlias(), rawPattern
, status
);
283 cacheData
.formatters
[units
[currentUnit
].getIndex()][currentWidth
].add(
291 return U_SUCCESS(status
);
294 static UnicodeString
loadNumericDateFormatterPattern(
295 const UResourceBundle
*resource
,
297 UErrorCode
&status
) {
298 UnicodeString result
;
299 if (U_FAILURE(status
)) {
303 chs
.append("durationUnits", status
)
304 .append("/", status
).append(pattern
, status
);
305 LocalUResourceBundlePointer
patternBundle(
306 ures_getByKeyWithFallback(
311 if (U_FAILURE(status
)) {
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'
323 result
.releaseBuffer(len
);
327 static NumericDateFormatters
*loadNumericDateFormatters(
328 const UResourceBundle
*resource
,
329 UErrorCode
&status
) {
330 if (U_FAILURE(status
)) {
333 NumericDateFormatters
*result
= new NumericDateFormatters(
334 loadNumericDateFormatterPattern(resource
, "hm", status
),
335 loadNumericDateFormatterPattern(resource
, "ms", status
),
336 loadNumericDateFormatterPattern(resource
, "hms", status
),
338 if (U_FAILURE(status
)) {
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
)) {
356 if (!loadMeasureUnitData(
357 unitsBundle
.getAlias(),
362 result
->adoptNumericDateFormatters(loadNumericDateFormatters(
363 unitsBundle
.getAlias(), status
));
364 if (U_FAILURE(status
)) {
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
)) {
375 NumberFormat
*inf
= NumberFormat::createInstance(
376 localeId
, UNUM_DECIMAL
, status
);
377 if (U_FAILURE(status
)) {
380 inf
->setMaximumFractionDigits(0);
381 DecimalFormat
*decfmt
= dynamic_cast<DecimalFormat
*>(inf
);
382 if (decfmt
!= NULL
) {
383 decfmt
->setRoundingMode(DecimalFormat::kRoundDown
);
385 result
->adoptIntegerFormat(inf
);
387 return result
.orphan();
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;
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.
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
,
406 UErrorCode
&status
) {
407 if (U_FAILURE(status
)) {
411 if (U_FAILURE(status
)) {
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
423 hms
[0] = measures
[i
].getNumber();
424 if (hms
[0].getDouble() < 0.0) {
428 } else if (isTimeUnit(measures
[i
].getUnit(), "minute")) {
429 // minute must come after hour
433 hms
[1] = measures
[i
].getNumber();
434 if (hms
[1].getDouble() < 0.0) {
438 } else if (isTimeUnit(measures
[i
].getUnit(), "second")) {
439 // second must come after hour and minute
443 hms
[2] = measures
[i
].getNumber();
444 if (hms
[2].getDouble() < 0.0) {
456 MeasureFormat::MeasureFormat(
457 const Locale
&locale
, UMeasureFormatWidth w
, UErrorCode
&status
)
462 listFormatter(NULL
) {
463 initMeasureFormat(locale
, w
, NULL
, status
);
466 MeasureFormat::MeasureFormat(
467 const Locale
&locale
,
468 UMeasureFormatWidth w
,
469 NumberFormat
*nfToAdopt
,
475 listFormatter(NULL
) {
476 initMeasureFormat(locale
, w
, nfToAdopt
, status
);
479 MeasureFormat::MeasureFormat(const MeasureFormat
&other
) :
482 numberFormat(other
.numberFormat
),
483 pluralRules(other
.pluralRules
),
485 listFormatter(NULL
) {
487 numberFormat
->addRef();
488 pluralRules
->addRef();
489 listFormatter
= new ListFormatter(*other
.listFormatter
);
492 MeasureFormat
&MeasureFormat::operator=(const MeasureFormat
&other
) {
493 if (this == &other
) {
496 Format::operator=(other
);
497 SharedObject::copyPtr(other
.cache
, cache
);
498 SharedObject::copyPtr(other
.numberFormat
, numberFormat
);
499 SharedObject::copyPtr(other
.pluralRules
, pluralRules
);
501 delete listFormatter
;
502 listFormatter
= new ListFormatter(*other
.listFormatter
);
506 MeasureFormat::MeasureFormat() :
510 width(UMEASFMT_WIDTH_WIDE
),
511 listFormatter(NULL
) {
514 MeasureFormat::~MeasureFormat() {
518 if (numberFormat
!= NULL
) {
519 numberFormat
->removeRef();
521 if (pluralRules
!= NULL
) {
522 pluralRules
->removeRef();
524 delete listFormatter
;
527 UBool
MeasureFormat::operator==(const Format
&other
) const {
528 if (this == &other
) { // Same object, equal
531 if (!Format::operator==(other
)) {
534 const MeasureFormat
&rhs
= static_cast<const MeasureFormat
&>(other
);
536 // Note: Since the ListFormatter depends only on Locale and width, we
537 // don't have to check it here.
539 // differing widths aren't equivalent
540 if (width
!= rhs
.width
) {
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
553 if (uprv_strcmp(localeId
, rhsLocaleId
) != 0) {
557 // Locales same, check NumberFormat if shared data differs.
559 numberFormat
== rhs
.numberFormat
||
560 **numberFormat
== **rhs
.numberFormat
);
563 Format
*MeasureFormat::clone() const {
564 return new MeasureFormat(*this);
567 UnicodeString
&MeasureFormat::format(
568 const Formattable
&obj
,
569 UnicodeString
&appendTo
,
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
);
581 status
= U_ILLEGAL_ARGUMENT_ERROR
;
585 void MeasureFormat::parseObject(
586 const UnicodeString
& /*source*/,
587 Formattable
& /*result*/,
588 ParsePosition
& /*pos*/) const {
592 UnicodeString
&MeasureFormat::formatMeasurePerUnit(
593 const Measure
&measure
,
594 const MeasureUnit
&perUnit
,
595 UnicodeString
&appendTo
,
597 UErrorCode
&status
) const {
598 if (U_FAILURE(status
)) {
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
);
608 FieldPosition
fpos(pos
.getField());
609 UnicodeString result
;
610 int32_t offset
= withPerUnitAndAppend(
612 measure
, **numberFormat
, result
, fpos
, status
),
616 if (U_FAILURE(status
)) {
619 if (fpos
.getBeginIndex() != 0 || fpos
.getEndIndex() != 0) {
620 pos
.setBeginIndex(fpos
.getBeginIndex() + offset
);
621 pos
.setEndIndex(fpos
.getEndIndex() + offset
);
626 UnicodeString
&MeasureFormat::formatMeasures(
627 const Measure
*measures
,
628 int32_t measureCount
,
629 UnicodeString
&appendTo
,
631 UErrorCode
&status
) const {
632 if (U_FAILURE(status
)) {
635 if (measureCount
== 0) {
638 if (measureCount
== 1) {
639 return formatMeasure(measures
[0], **numberFormat
, appendTo
, pos
, status
);
641 if (width
== UMEASFMT_WIDTH_NUMERIC
) {
643 int32_t bitMap
= toHMS(measures
, measureCount
, hms
, status
);
645 return formatNumeric(hms
, bitMap
, appendTo
, status
);
648 if (pos
.getField() != FieldPosition::DONT_CARE
) {
649 return formatMeasuresSlowTrack(
650 measures
, measureCount
, appendTo
, pos
, status
);
652 UnicodeString
*results
= new UnicodeString
[measureCount
];
653 if (results
== NULL
) {
654 status
= U_MEMORY_ALLOCATION_ERROR
;
657 for (int32_t i
= 0; i
< measureCount
; ++i
) {
658 const NumberFormat
*nf
= cache
->getIntegerFormat();
659 if (i
== measureCount
- 1) {
660 nf
= numberFormat
->get();
669 listFormatter
->format(results
, measureCount
, appendTo
, status
);
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
)) {
684 const char *name
= locale
.getName();
685 setLocaleIDs(name
, name
);
687 UnifiedCache::getByLocale(locale
, cache
, status
);
688 if (U_FAILURE(status
)) {
692 const SharedPluralRules
*pr
= PluralRules::createSharedInstance(
693 locale
, UPLURAL_TYPE_CARDINAL
, status
);
694 if (U_FAILURE(status
)) {
697 SharedObject::copyPtr(pr
, pluralRules
);
700 const SharedNumberFormat
*shared
= NumberFormat::createSharedInstance(
701 locale
, UNUM_DECIMAL
, status
);
702 if (U_FAILURE(status
)) {
705 SharedObject::copyPtr(shared
, numberFormat
);
708 adoptNumberFormat(nf
.orphan(), status
);
709 if (U_FAILURE(status
)) {
714 delete listFormatter
;
715 listFormatter
= ListFormatter::createInstance(
717 listStyles
[widthToIndex(width
)],
721 void MeasureFormat::adoptNumberFormat(
722 NumberFormat
*nfToAdopt
, UErrorCode
&status
) {
723 LocalPointer
<NumberFormat
> nf(nfToAdopt
);
724 if (U_FAILURE(status
)) {
727 SharedNumberFormat
*shared
= new SharedNumberFormat(nf
.getAlias());
728 if (shared
== NULL
) {
729 status
= U_MEMORY_ALLOCATION_ERROR
;
733 SharedObject::copyPtr(shared
, numberFormat
);
736 UBool
MeasureFormat::setMeasureFormatLocale(const Locale
&locale
, UErrorCode
&status
) {
737 if (U_FAILURE(status
) || locale
== getLocale(status
)) {
740 initMeasureFormat(locale
, width
, NULL
, status
);
741 return U_SUCCESS(status
);
744 // Apple-specific for now
745 UMeasureFormatWidth
MeasureFormat::getWidth() const {
749 const NumberFormat
&MeasureFormat::getNumberFormat() const {
750 return **numberFormat
;
753 const PluralRules
&MeasureFormat::getPluralRules() const {
754 return **pluralRules
;
757 Locale
MeasureFormat::getLocale(UErrorCode
&status
) const {
758 return Format::getLocale(ULOC_VALID_LOCALE
, status
);
761 const char *MeasureFormat::getLocaleID(UErrorCode
&status
) const {
762 return Format::getLocaleID(ULOC_VALID_LOCALE
, status
);
765 UnicodeString
&MeasureFormat::formatMeasure(
766 const Measure
&measure
,
767 const NumberFormat
&nf
,
768 UnicodeString
&appendTo
,
770 UErrorCode
&status
) const {
771 if (U_FAILURE(status
)) {
774 const Formattable
& amtNumber
= measure
.getNumber();
775 const MeasureUnit
& amtUnit
= measure
.getUnit();
776 if (isCurrency(amtUnit
)) {
778 u_charsToUChars(amtUnit
.getSubtype(), isoCode
, 4);
779 return cache
->getCurrencyFormat(widthToIndex(width
))->format(
780 new CurrencyAmount(amtNumber
, isoCode
, status
),
785 const QuantityFormatter
*quantityFormatter
= getQuantityFormatter(
786 amtUnit
.getIndex(), widthToIndex(width
), status
);
787 if (U_FAILURE(status
)) {
790 return quantityFormatter
->format(
799 // Formats hours-minutes-seconds as 5:37:23 or similar.
800 UnicodeString
&MeasureFormat::formatNumeric(
801 const Formattable
*hms
, // always length 3
802 int32_t bitMap
, // 1=hourset, 2=minuteset, 4=secondset
803 UnicodeString
&appendTo
,
804 UErrorCode
&status
) const {
805 if (U_FAILURE(status
)) {
809 (UDate
) (((uprv_trunc(hms
[0].getDouble(status
)) * 60.0
810 + uprv_trunc(hms
[1].getDouble(status
))) * 60.0
811 + uprv_trunc(hms
[2].getDouble(status
))) * 1000.0);
815 return formatNumeric(
817 cache
->getNumericDateFormatters()->hourMinuteSecond
,
824 return formatNumeric(
826 cache
->getNumericDateFormatters()->minuteSecond
,
833 return formatNumeric(
835 cache
->getNumericDateFormatters()->hourMinute
,
842 status
= U_INTERNAL_PROGRAM_ERROR
;
849 static void appendRange(
850 const UnicodeString
&src
,
853 UnicodeString
&dest
) {
854 dest
.append(src
, start
, end
- start
);
857 static void appendRange(
858 const UnicodeString
&src
,
860 UnicodeString
&dest
) {
861 dest
.append(src
, end
, src
.length() - end
);
864 // Formats time like 5:37:23
865 UnicodeString
&MeasureFormat::formatNumeric(
866 UDate date
, // Time since epoch 1:30:00 would be 5400000
867 const DateFormat
&dateFmt
, // h:mm, m:ss, or h:mm:ss
868 UDateFormatField smallestField
, // seconds in 5:37:23.5
869 const Formattable
&smallestAmount
, // 23.5 for 5:37:23.5
870 UnicodeString
&appendTo
,
871 UErrorCode
&status
) const {
872 if (U_FAILURE(status
)) {
875 // Format the smallest amount with this object's NumberFormat
876 UnicodeString smallestAmountFormatted
;
878 // We keep track of the integer part of smallest amount so that
879 // we can replace it later so that we get '0:00:09.3' instead of
881 FieldPosition
intFieldPosition(UNUM_INTEGER_FIELD
);
882 (*numberFormat
)->format(
883 smallestAmount
, smallestAmountFormatted
, intFieldPosition
, status
);
885 intFieldPosition
.getBeginIndex() == 0 &&
886 intFieldPosition
.getEndIndex() == 0) {
887 status
= U_INTERNAL_PROGRAM_ERROR
;
891 // Format time. draft becomes something like '5:30:45'
892 FieldPosition
smallestFieldPosition(smallestField
);
894 dateFmt
.format(date
, draft
, smallestFieldPosition
, status
);
896 // If we find field for smallest amount replace it with the formatted
897 // smallest amount from above taking care to replace the integer part
898 // with what is in original time. For example, If smallest amount
899 // is 9.35s and the formatted time is 0:00:09 then 9.35 becomes 09.35
900 // and replacing yields 0:00:09.35
901 if (smallestFieldPosition
.getBeginIndex() != 0 ||
902 smallestFieldPosition
.getEndIndex() != 0) {
903 appendRange(draft
, 0, smallestFieldPosition
.getBeginIndex(), appendTo
);
905 smallestAmountFormatted
,
907 intFieldPosition
.getBeginIndex(),
911 smallestFieldPosition
.getBeginIndex(),
912 smallestFieldPosition
.getEndIndex(),
915 smallestAmountFormatted
,
916 intFieldPosition
.getEndIndex(),
920 smallestFieldPosition
.getEndIndex(),
923 appendTo
.append(draft
);
928 const QuantityFormatter
*MeasureFormat::getQuantityFormatter(
931 UErrorCode
&status
) const {
932 if (U_FAILURE(status
)) {
935 const QuantityFormatter
*formatters
=
936 cache
->formatters
[index
];
937 if (formatters
[widthIndex
].isValid()) {
938 return &formatters
[widthIndex
];
940 if (formatters
[UMEASFMT_WIDTH_SHORT
].isValid()) {
941 return &formatters
[UMEASFMT_WIDTH_SHORT
];
943 if (formatters
[UMEASFMT_WIDTH_WIDE
].isValid()) {
944 return &formatters
[UMEASFMT_WIDTH_WIDE
];
946 status
= U_MISSING_RESOURCE_ERROR
;
950 const SimplePatternFormatter
*MeasureFormat::getPerUnitFormatter(
952 int32_t widthIndex
) const {
953 const SimplePatternFormatter
* const * perUnitFormatters
=
954 cache
->getPerUnitFormattersByIndex(index
);
955 if (perUnitFormatters
[widthIndex
] != NULL
) {
956 return perUnitFormatters
[widthIndex
];
958 if (perUnitFormatters
[UMEASFMT_WIDTH_SHORT
] != NULL
) {
959 return perUnitFormatters
[UMEASFMT_WIDTH_SHORT
];
961 if (perUnitFormatters
[UMEASFMT_WIDTH_WIDE
] != NULL
) {
962 return perUnitFormatters
[UMEASFMT_WIDTH_WIDE
];
967 const SimplePatternFormatter
*MeasureFormat::getPerFormatter(
969 UErrorCode
&status
) const {
970 if (U_FAILURE(status
)) {
973 const SimplePatternFormatter
* perFormatters
= cache
->perFormatters
;
975 if (perFormatters
[widthIndex
].getPlaceholderCount() == 2) {
976 return &perFormatters
[widthIndex
];
978 if (perFormatters
[UMEASFMT_WIDTH_SHORT
].getPlaceholderCount() == 2) {
979 return &perFormatters
[UMEASFMT_WIDTH_SHORT
];
981 if (perFormatters
[UMEASFMT_WIDTH_WIDE
].getPlaceholderCount() == 2) {
982 return &perFormatters
[UMEASFMT_WIDTH_WIDE
];
984 status
= U_MISSING_RESOURCE_ERROR
;
988 static void getPerUnitString(
989 const QuantityFormatter
&formatter
,
990 UnicodeString
&result
) {
991 result
= formatter
.getByVariant("one")->getPatternWithNoPlaceholders();
995 int32_t MeasureFormat::withPerUnitAndAppend(
996 const UnicodeString
&formatted
,
997 const MeasureUnit
&perUnit
,
998 UnicodeString
&appendTo
,
999 UErrorCode
&status
) const {
1000 int32_t offset
= -1;
1001 if (U_FAILURE(status
)) {
1004 const SimplePatternFormatter
*perUnitFormatter
= getPerUnitFormatter(
1005 perUnit
.getIndex(), widthToIndex(width
));
1006 if (perUnitFormatter
!= NULL
) {
1007 const UnicodeString
*params
[] = {&formatted
};
1008 perUnitFormatter
->formatAndAppend(
1010 UPRV_LENGTHOF(params
),
1017 const SimplePatternFormatter
*perFormatter
= getPerFormatter(
1018 widthToIndex(width
), status
);
1019 const QuantityFormatter
*qf
= getQuantityFormatter(
1020 perUnit
.getIndex(), widthToIndex(width
), status
);
1021 if (U_FAILURE(status
)) {
1024 UnicodeString perUnitString
;
1025 getPerUnitString(*qf
, perUnitString
);
1026 const UnicodeString
*params
[] = {&formatted
, &perUnitString
};
1027 perFormatter
->formatAndAppend(
1029 UPRV_LENGTHOF(params
),
1037 UnicodeString
&MeasureFormat::formatMeasuresSlowTrack(
1038 const Measure
*measures
,
1039 int32_t measureCount
,
1040 UnicodeString
& appendTo
,
1042 UErrorCode
& status
) const {
1043 if (U_FAILURE(status
)) {
1046 FieldPosition
dontCare(FieldPosition::DONT_CARE
);
1047 FieldPosition
fpos(pos
.getField());
1048 UnicodeString
*results
= new UnicodeString
[measureCount
];
1049 int32_t fieldPositionFoundIndex
= -1;
1050 for (int32_t i
= 0; i
< measureCount
; ++i
) {
1051 const NumberFormat
*nf
= cache
->getIntegerFormat();
1052 if (i
== measureCount
- 1) {
1053 nf
= numberFormat
->get();
1055 if (fieldPositionFoundIndex
== -1) {
1056 formatMeasure(measures
[i
], *nf
, results
[i
], fpos
, status
);
1057 if (U_FAILURE(status
)) {
1061 if (fpos
.getBeginIndex() != 0 || fpos
.getEndIndex() != 0) {
1062 fieldPositionFoundIndex
= i
;
1065 formatMeasure(measures
[i
], *nf
, results
[i
], dontCare
, status
);
1069 listFormatter
->format(
1073 fieldPositionFoundIndex
,
1076 if (U_FAILURE(status
)) {
1081 pos
.setBeginIndex(fpos
.getBeginIndex() + offset
);
1082 pos
.setEndIndex(fpos
.getEndIndex() + offset
);
1088 MeasureFormat
* U_EXPORT2
MeasureFormat::createCurrencyFormat(const Locale
& locale
,
1090 CurrencyFormat
* fmt
= NULL
;
1091 if (U_SUCCESS(ec
)) {
1092 fmt
= new CurrencyFormat(locale
, ec
);
1093 if (U_FAILURE(ec
)) {
1101 MeasureFormat
* U_EXPORT2
MeasureFormat::createCurrencyFormat(UErrorCode
& ec
) {
1102 if (U_FAILURE(ec
)) {
1105 return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec
);
1110 #endif /* #if !UCONFIG_NO_FORMATTING */