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