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