tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

measfmt.cpp (29050B)


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