tor-browser

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

tmutfmt.cpp (32427B)


      1 // © 2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4 *******************************************************************************
      5 * Copyright (C) 2008-2015, Google, International Business Machines Corporation
      6 * and others. All Rights Reserved.
      7 *******************************************************************************
      8 */
      9 
     10 #include "unicode/tmutfmt.h"
     11 
     12 #if !UCONFIG_NO_FORMATTING
     13 
     14 #include <utility>
     15 
     16 #include "unicode/decimfmt.h"
     17 #include "unicode/localpointer.h"
     18 #include "plurrule_impl.h"
     19 #include "uvector.h"
     20 #include "charstr.h"
     21 #include "cmemory.h"
     22 #include "cstring.h"
     23 #include "hash.h"
     24 #include "ulocimp.h"
     25 #include "uresimp.h"
     26 #include "ureslocs.h"
     27 #include "unicode/msgfmt.h"
     28 #include "uassert.h"
     29 
     30 #define LEFT_CURLY_BRACKET  ((char16_t)0x007B)
     31 #define RIGHT_CURLY_BRACKET ((char16_t)0x007D)
     32 #define SPACE             ((char16_t)0x0020)
     33 #define DIGIT_ZERO        ((char16_t)0x0030)
     34 #define LOW_S             ((char16_t)0x0073)
     35 #define LOW_M             ((char16_t)0x006D)
     36 #define LOW_I             ((char16_t)0x0069)
     37 #define LOW_N             ((char16_t)0x006E)
     38 #define LOW_H             ((char16_t)0x0068)
     39 #define LOW_W             ((char16_t)0x0077)
     40 #define LOW_D             ((char16_t)0x0064)
     41 #define LOW_Y             ((char16_t)0x0079)
     42 #define LOW_Z             ((char16_t)0x007A)
     43 #define LOW_E             ((char16_t)0x0065)
     44 #define LOW_R             ((char16_t)0x0072)
     45 #define LOW_O             ((char16_t)0x006F)
     46 #define LOW_N             ((char16_t)0x006E)
     47 #define LOW_T             ((char16_t)0x0074)
     48 
     49 
     50 //TODO: define in compile time
     51 //#define TMUTFMT_DEBUG 1
     52 
     53 #ifdef TMUTFMT_DEBUG
     54 #include <iostream>
     55 #endif
     56 
     57 U_NAMESPACE_BEGIN
     58 
     59 
     60 
     61 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat)
     62 
     63 static const char gUnitsTag[] = "units";
     64 static const char gShortUnitsTag[] = "unitsShort";
     65 static const char gTimeUnitYear[] = "year";
     66 static const char gTimeUnitMonth[] = "month";
     67 static const char gTimeUnitDay[] = "day";
     68 static const char gTimeUnitWeek[] = "week";
     69 static const char gTimeUnitHour[] = "hour";
     70 static const char gTimeUnitMinute[] = "minute";
     71 static const char gTimeUnitSecond[] = "second";
     72 static const char gPluralCountOther[] = "other";
     73 
     74 static const char16_t DEFAULT_PATTERN_FOR_SECOND[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_S, 0};
     75 static const char16_t DEFAULT_PATTERN_FOR_MINUTE[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, LOW_I, LOW_N, 0};
     76 static const char16_t DEFAULT_PATTERN_FOR_HOUR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_H, 0};
     77 static const char16_t DEFAULT_PATTERN_FOR_WEEK[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_W, 0};
     78 static const char16_t DEFAULT_PATTERN_FOR_DAY[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_D, 0};
     79 static const char16_t DEFAULT_PATTERN_FOR_MONTH[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, 0};
     80 static const char16_t DEFAULT_PATTERN_FOR_YEAR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_Y, 0};
     81 
     82 static const char16_t PLURAL_COUNT_ZERO[] = {LOW_Z, LOW_E, LOW_R, LOW_O, 0};
     83 static const char16_t PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0};
     84 static const char16_t PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0};
     85 
     86 TimeUnitFormat::TimeUnitFormat(UErrorCode& status) {
     87    initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE, nullptr, status);
     88    create(UTMUTFMT_FULL_STYLE, status);
     89 }
     90 
     91 
     92 TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) {
     93    initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, nullptr, status);
     94    create(UTMUTFMT_FULL_STYLE, status);
     95 }
     96 
     97 
     98 TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) {
     99    switch (style) {
    100    case UTMUTFMT_FULL_STYLE:
    101        initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, nullptr, status);
    102        break;
    103    case UTMUTFMT_ABBREVIATED_STYLE:
    104        initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, nullptr, status);
    105        break;
    106    default:
    107        initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, nullptr, status);
    108        break;
    109    }
    110    create(style, status);
    111 }
    112 
    113 TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other)
    114 :   MeasureFormat(other),
    115    fStyle(other.fStyle)
    116 {
    117    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
    118         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
    119         i = static_cast<TimeUnit::UTimeUnitFields>(i + 1)) {
    120        UErrorCode status = U_ZERO_ERROR;
    121        fTimeUnitToCountToPatterns[i] = initHash(status);
    122        if (U_SUCCESS(status)) {
    123            copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
    124        } else {
    125            delete fTimeUnitToCountToPatterns[i];
    126            fTimeUnitToCountToPatterns[i] = nullptr;
    127        }
    128    }
    129 }
    130 
    131 
    132 TimeUnitFormat::~TimeUnitFormat() {
    133    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
    134         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
    135         i = static_cast<TimeUnit::UTimeUnitFields>(i + 1)) {
    136        deleteHash(fTimeUnitToCountToPatterns[i]);
    137        fTimeUnitToCountToPatterns[i] = nullptr;
    138    }
    139 }
    140 
    141 
    142 TimeUnitFormat*
    143 TimeUnitFormat::clone() const {
    144    return new TimeUnitFormat(*this);
    145 }
    146 
    147 
    148 TimeUnitFormat&
    149 TimeUnitFormat::operator=(const TimeUnitFormat& other) {
    150    if (this == &other) {
    151        return *this;
    152    }
    153    MeasureFormat::operator=(other);
    154    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
    155         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
    156         i = static_cast<TimeUnit::UTimeUnitFields>(i + 1)) {
    157        deleteHash(fTimeUnitToCountToPatterns[i]);
    158        fTimeUnitToCountToPatterns[i] = nullptr;
    159    }
    160    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
    161         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
    162         i = static_cast<TimeUnit::UTimeUnitFields>(i + 1)) {
    163        UErrorCode status = U_ZERO_ERROR;
    164        fTimeUnitToCountToPatterns[i] = initHash(status);
    165        if (U_SUCCESS(status)) {
    166            copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
    167        } else {
    168            delete fTimeUnitToCountToPatterns[i];
    169            fTimeUnitToCountToPatterns[i] = nullptr;
    170        }
    171    }
    172    fStyle = other.fStyle;
    173    return *this;
    174 }
    175 
    176 void
    177 TimeUnitFormat::parseObject(const UnicodeString& source,
    178                            Formattable& result,
    179                            ParsePosition& pos) const {
    180    Formattable resultNumber(0.0);
    181    UBool withNumberFormat = false;
    182    TimeUnit::UTimeUnitFields resultTimeUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT;
    183    int32_t oldPos = pos.getIndex();
    184    int32_t newPos = -1;
    185    int32_t longestParseDistance = 0;
    186    UnicodeString* countOfLongestMatch = nullptr;
    187 #ifdef TMUTFMT_DEBUG
    188    char res[1000];
    189    source.extract(0, source.length(), res, "UTF-8");
    190    std::cout << "parse source: " << res << "\n";
    191 #endif
    192    // parse by iterating through all available patterns
    193    // and looking for the longest match.
    194    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
    195         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
    196         i = static_cast<TimeUnit::UTimeUnitFields>(i + 1)) {
    197        Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
    198        int32_t elemPos = UHASH_FIRST;
    199        const UHashElement* elem = nullptr;
    200        while ((elem = countToPatterns->nextElement(elemPos)) != nullptr){
    201            const UHashTok keyTok = elem->key;
    202            UnicodeString* count = static_cast<UnicodeString*>(keyTok.pointer);
    203 #ifdef TMUTFMT_DEBUG
    204            count->extract(0, count->length(), res, "UTF-8");
    205            std::cout << "parse plural count: " << res << "\n";
    206 #endif
    207            const UHashTok valueTok = elem->value;
    208            // the value is a pair of MessageFormat*
    209            MessageFormat** patterns = static_cast<MessageFormat**>(valueTok.pointer);
    210            for (UTimeUnitFormatStyle style = UTMUTFMT_FULL_STYLE; style < UTMUTFMT_FORMAT_STYLE_COUNT;
    211                 style = static_cast<UTimeUnitFormatStyle>(style + 1)) {
    212                MessageFormat* pattern = patterns[style];
    213                pos.setErrorIndex(-1);
    214                pos.setIndex(oldPos);
    215                // see if we can parse
    216                Formattable parsed;
    217                pattern->parseObject(source, parsed, pos);
    218                if (pos.getErrorIndex() != -1 || pos.getIndex() == oldPos) {
    219                    continue;
    220                }
    221    #ifdef TMUTFMT_DEBUG
    222                std::cout << "parsed.getType: " << parsed.getType() << "\n";
    223    #endif
    224                Formattable tmpNumber(0.0);
    225                if (pattern->getArgTypeCount() != 0) {
    226                    Formattable& temp = parsed[0];
    227                    if (temp.getType() == Formattable::kString) {
    228                        UnicodeString tmpString;
    229                        UErrorCode pStatus = U_ZERO_ERROR;
    230                        getNumberFormatInternal().parse(temp.getString(tmpString), tmpNumber, pStatus);
    231                        if (U_FAILURE(pStatus)) {
    232                            continue;
    233                        }
    234                    } else if (temp.isNumeric()) {
    235                        tmpNumber = temp;
    236                    } else {
    237                        continue;
    238                    }
    239                }
    240                int32_t parseDistance = pos.getIndex() - oldPos;
    241                if (parseDistance > longestParseDistance) {
    242                    if (pattern->getArgTypeCount() != 0) {
    243                        resultNumber = tmpNumber;
    244                        withNumberFormat = true;
    245                    } else {
    246                        withNumberFormat = false;
    247                    }
    248                    resultTimeUnit = i;
    249                    newPos = pos.getIndex();
    250                    longestParseDistance = parseDistance;
    251                    countOfLongestMatch = count;
    252                }
    253            }
    254        }
    255    }
    256    /* After find the longest match, parse the number.
    257     * Result number could be null for the pattern without number pattern.
    258     * such as unit pattern in Arabic.
    259     * When result number is null, use plural rule to set the number.
    260     */
    261    if (withNumberFormat == false && longestParseDistance != 0) {
    262        // set the number using plurrual count
    263        if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ZERO, 4)) {
    264            resultNumber = Formattable(0.0);
    265        } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ONE, 3)) {
    266            resultNumber = Formattable(1.0);
    267        } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_TWO, 3)) {
    268            resultNumber = Formattable(2.0);
    269        } else {
    270            // should not happen.
    271            // TODO: how to handle?
    272            resultNumber = Formattable(3.0);
    273        }
    274    }
    275    if (longestParseDistance == 0) {
    276        pos.setIndex(oldPos);
    277        pos.setErrorIndex(0);
    278    } else {
    279        UErrorCode status = U_ZERO_ERROR;
    280        LocalPointer<TimeUnitAmount> tmutamt(new TimeUnitAmount(resultNumber, resultTimeUnit, status), status);
    281        if (U_SUCCESS(status)) {
    282            result.adoptObject(tmutamt.orphan());
    283            pos.setIndex(newPos);
    284            pos.setErrorIndex(-1);
    285        } else {
    286            pos.setIndex(oldPos);
    287            pos.setErrorIndex(0);
    288        }
    289    }
    290 }
    291 
    292 void
    293 TimeUnitFormat::create(UTimeUnitFormatStyle style, UErrorCode& status) {
    294    // fTimeUnitToCountToPatterns[] must have its elements initialized to nullptr first
    295    // before checking for failure status.
    296    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
    297         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
    298         i = static_cast<TimeUnit::UTimeUnitFields>(i + 1)) {
    299        fTimeUnitToCountToPatterns[i] = nullptr;
    300    }
    301 
    302    if (U_FAILURE(status)) {
    303        return;
    304    }
    305    if (style < UTMUTFMT_FULL_STYLE || style >= UTMUTFMT_FORMAT_STYLE_COUNT) {
    306        status = U_ILLEGAL_ARGUMENT_ERROR;
    307        return;
    308    }
    309    fStyle = style;
    310 
    311    //TODO: format() and parseObj() are const member functions,
    312    //so, can not do lazy initialization in C++.
    313    //setup has to be done in constructors.
    314    //and here, the behavior is not consistent with Java.
    315    //In Java, create an empty instance does not setup locale as
    316    //default locale. If it followed by setNumberFormat(),
    317    //in format(), the locale will set up as the locale in fNumberFormat.
    318    //But in C++, this sets the locale as the default locale.
    319    setup(status);
    320 }
    321 
    322 void
    323 TimeUnitFormat::setup(UErrorCode& err) {
    324    initDataMembers(err);
    325 
    326    UVector pluralCounts(nullptr, uhash_compareUnicodeString, 6, err);
    327    LocalPointer<StringEnumeration> keywords(getPluralRules().getKeywords(err), err);
    328    if (U_FAILURE(err)) {
    329        return;
    330    }
    331    UnicodeString* pluralCount;
    332    while ((pluralCount = const_cast<UnicodeString*>(keywords->snext(err))) != nullptr) {
    333      pluralCounts.addElement(pluralCount, err);
    334    }
    335    readFromCurrentLocale(UTMUTFMT_FULL_STYLE, gUnitsTag, pluralCounts, err);
    336    checkConsistency(UTMUTFMT_FULL_STYLE, gUnitsTag, err);
    337    readFromCurrentLocale(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, pluralCounts, err);
    338    checkConsistency(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, err);
    339 }
    340 
    341 
    342 void
    343 TimeUnitFormat::initDataMembers(UErrorCode& err){
    344    if (U_FAILURE(err)) {
    345        return;
    346    }
    347    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
    348         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
    349         i = static_cast<TimeUnit::UTimeUnitFields>(i + 1)) {
    350        deleteHash(fTimeUnitToCountToPatterns[i]);
    351        fTimeUnitToCountToPatterns[i] = nullptr;
    352    }
    353 }
    354 
    355 struct TimeUnitFormatReadSink : public ResourceSink {
    356    TimeUnitFormat *timeUnitFormatObj;
    357    const UVector &pluralCounts;
    358    UTimeUnitFormatStyle style;
    359    UBool beenHere;
    360 
    361    TimeUnitFormatReadSink(TimeUnitFormat *timeUnitFormatObj,
    362            const UVector &pluralCounts, UTimeUnitFormatStyle style) :
    363            timeUnitFormatObj(timeUnitFormatObj), pluralCounts(pluralCounts),
    364            style(style), beenHere(false){}
    365 
    366    virtual ~TimeUnitFormatReadSink();
    367 
    368    virtual void put(const char *key, ResourceValue &value, UBool, UErrorCode &errorCode) override {
    369        // Skip all put() calls except the first one -- discard all fallback data.
    370        if (beenHere) {
    371            return;
    372        } else {
    373            beenHere = true;
    374        }
    375 
    376        ResourceTable units = value.getTable(errorCode);
    377        if (U_FAILURE(errorCode)) { return; }
    378 
    379        for (int32_t i = 0; units.getKeyAndValue(i, key, value); ++i) {
    380            const char* timeUnitName = key;
    381            if (timeUnitName == nullptr) {
    382                continue;
    383            }
    384 
    385            TimeUnit::UTimeUnitFields timeUnitField = TimeUnit::UTIMEUNIT_FIELD_COUNT;
    386            if ( uprv_strcmp(timeUnitName, gTimeUnitYear) == 0 ) {
    387                timeUnitField = TimeUnit::UTIMEUNIT_YEAR;
    388            } else if ( uprv_strcmp(timeUnitName, gTimeUnitMonth) == 0 ) {
    389                timeUnitField = TimeUnit::UTIMEUNIT_MONTH;
    390            } else if ( uprv_strcmp(timeUnitName, gTimeUnitDay) == 0 ) {
    391                timeUnitField = TimeUnit::UTIMEUNIT_DAY;
    392            } else if ( uprv_strcmp(timeUnitName, gTimeUnitHour) == 0 ) {
    393                timeUnitField = TimeUnit::UTIMEUNIT_HOUR;
    394            } else if ( uprv_strcmp(timeUnitName, gTimeUnitMinute) == 0 ) {
    395                timeUnitField = TimeUnit::UTIMEUNIT_MINUTE;
    396            } else if ( uprv_strcmp(timeUnitName, gTimeUnitSecond) == 0 ) {
    397                timeUnitField = TimeUnit::UTIMEUNIT_SECOND;
    398            } else if ( uprv_strcmp(timeUnitName, gTimeUnitWeek) == 0 ) {
    399                timeUnitField = TimeUnit::UTIMEUNIT_WEEK;
    400            } else {
    401                continue;
    402            }
    403            LocalPointer<Hashtable> localCountToPatterns;
    404            Hashtable *countToPatterns =
    405                timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField];
    406            if (countToPatterns == nullptr) {
    407                localCountToPatterns.adoptInsteadAndCheckErrorCode(
    408                    timeUnitFormatObj->initHash(errorCode), errorCode);
    409                countToPatterns = localCountToPatterns.getAlias();
    410                if (U_FAILURE(errorCode)) {
    411                    return;
    412                }
    413            }
    414 
    415            ResourceTable countsToPatternTable = value.getTable(errorCode);
    416            if (U_FAILURE(errorCode)) {
    417                continue;
    418            }
    419            for (int32_t j = 0; countsToPatternTable.getKeyAndValue(j, key, value); ++j) {
    420                errorCode = U_ZERO_ERROR;
    421                UnicodeString pattern = value.getUnicodeString(errorCode);
    422                if (U_FAILURE(errorCode)) {
    423                    continue;
    424                }
    425                UnicodeString pluralCountUniStr(key, -1, US_INV);
    426                if (!pluralCounts.contains(&pluralCountUniStr)) {
    427                    continue;
    428                }
    429                LocalPointer<MessageFormat> messageFormat(new MessageFormat(
    430                    pattern, timeUnitFormatObj->getLocale(errorCode), errorCode), errorCode);
    431                if (U_FAILURE(errorCode)) {
    432                    return;
    433                }
    434                MessageFormat** formatters =
    435                    static_cast<MessageFormat**>(countToPatterns->get(pluralCountUniStr));
    436                if (formatters == nullptr) {
    437                    LocalMemory<MessageFormat *> localFormatters(
    438                        static_cast<MessageFormat**>(uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT * sizeof(MessageFormat*))));
    439                    if (localFormatters.isNull()) {
    440                        errorCode = U_MEMORY_ALLOCATION_ERROR;
    441                        return;
    442                    }
    443                    localFormatters[UTMUTFMT_FULL_STYLE] = nullptr;
    444                    localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = nullptr;
    445                    countToPatterns->put(pluralCountUniStr, localFormatters.getAlias(), errorCode);
    446                    if (U_FAILURE(errorCode)) {
    447                        return;
    448                    }
    449                    formatters = localFormatters.orphan();
    450                }
    451                formatters[style] = messageFormat.orphan();
    452            }
    453 
    454            if (timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] == nullptr) {
    455                timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] = localCountToPatterns.orphan();
    456            }
    457        }
    458    }
    459 
    460 };
    461 
    462 TimeUnitFormatReadSink::~TimeUnitFormatReadSink() {}
    463 
    464 void
    465 TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key,
    466                                      const UVector& pluralCounts, UErrorCode& err) {
    467    if (U_FAILURE(err)) {
    468        return;
    469    }
    470    // fill timeUnitToCountToPatterns from resource file
    471    // err is used to indicate wrong status except missing resource.
    472    // status is an error code used in resource lookup.
    473    // status does not affect "err".
    474    UErrorCode status = U_ZERO_ERROR;
    475    LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_UNIT, getLocaleID(status), &status));
    476 
    477    LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, nullptr, &status));
    478    ures_getByKey(unitsRes.getAlias(), "duration", unitsRes.getAlias(), &status);
    479    if (U_FAILURE(status)) {
    480        return;
    481    }
    482 
    483    TimeUnitFormatReadSink sink(this, pluralCounts, style);
    484    ures_getAllItemsWithFallback(unitsRes.getAlias(), "", sink, status);
    485 }
    486 
    487 void
    488 TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style, const char* key, UErrorCode& err) {
    489    if (U_FAILURE(err)) {
    490        return;
    491    }
    492    // there should be patterns for each plural rule in each time unit.
    493    // For each time unit,
    494    //     for each plural rule, following is unit pattern fall-back rule:
    495    //         ( for example: "one" hour )
    496    //         look for its unit pattern in its locale tree.
    497    //         if pattern is not found in its own locale, such as de_DE,
    498    //         look for the pattern in its parent, such as de,
    499    //         keep looking till found or till root.
    500    //         if the pattern is not found in root either,
    501    //         fallback to plural count "other",
    502    //         look for the pattern of "other" in the locale tree:
    503    //         "de_DE" to "de" to "root".
    504    //         If not found, fall back to value of
    505    //         static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h".
    506    //
    507    // Following is consistency check to create pattern for each
    508    // plural rule in each time unit using above fall-back rule.
    509    //
    510    LocalPointer<StringEnumeration> keywords(
    511            getPluralRules().getKeywords(err), err);
    512    const UnicodeString* pluralCount;
    513    while (U_SUCCESS(err) && (pluralCount = keywords->snext(err)) != nullptr) {
    514        for (int32_t i = 0; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; ++i) {
    515            // for each time unit,
    516            // get all the patterns for each plural rule in this locale.
    517            Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
    518            if ( countToPatterns == nullptr ) {
    519                fTimeUnitToCountToPatterns[i] = countToPatterns = initHash(err);
    520                if (U_FAILURE(err)) {
    521                    return;
    522                }
    523            }
    524            MessageFormat** formatters = static_cast<MessageFormat**>(countToPatterns->get(*pluralCount));
    525            if( formatters == nullptr || formatters[style] == nullptr ) {
    526                // look through parents
    527                const char* localeName = getLocaleID(err);
    528                CharString pluralCountChars;
    529                pluralCountChars.appendInvariantChars(*pluralCount, err);
    530                searchInLocaleChain(style, key, localeName,
    531                                    static_cast<TimeUnit::UTimeUnitFields>(i),
    532                                    *pluralCount, pluralCountChars.data(),
    533                                    countToPatterns, err);
    534            }
    535            // TODO: what to do with U_FAILURE(err) at this point.
    536            //       As is, the outer loop continues to run, but does nothing.
    537        }
    538    }
    539 }
    540 
    541 
    542 
    543 // srcPluralCount is the original plural count on which the pattern is
    544 // searched for.
    545 // searchPluralCount is the fallback plural count.
    546 // For example, to search for pattern for ""one" hour",
    547 // "one" is the srcPluralCount,
    548 // if the pattern is not found even in root, fallback to
    549 // using patterns of plural count "other",
    550 // then, "other" is the searchPluralCount.
    551 void
    552 TimeUnitFormat::searchInLocaleChain(UTimeUnitFormatStyle style, const char* key, const char* localeName,
    553                                TimeUnit::UTimeUnitFields srcTimeUnitField,
    554                                const UnicodeString& srcPluralCount,
    555                                const char* searchPluralCount,
    556                                Hashtable* countToPatterns,
    557                                UErrorCode& err) {
    558    if (U_FAILURE(err)) {
    559        return;
    560    }
    561    UErrorCode status = U_ZERO_ERROR;
    562    CharString parentLocale(localeName, status);
    563    U_ASSERT(countToPatterns != nullptr);
    564    for (;;) {
    565        parentLocale = ulocimp_getParent(parentLocale.data(), status);
    566        // look for pattern for srcPluralCount in locale tree
    567        LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_UNIT, parentLocale.data(), &status));
    568        LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, nullptr, &status));
    569        const char* timeUnitName = getTimeUnitName(srcTimeUnitField, status);
    570        LocalUResourceBundlePointer countsToPatternRB(ures_getByKey(unitsRes.getAlias(), timeUnitName, nullptr, &status));
    571        const char16_t* pattern;
    572        int32_t      ptLength;
    573        pattern = ures_getStringByKeyWithFallback(countsToPatternRB.getAlias(), searchPluralCount, &ptLength, &status);
    574        if (U_SUCCESS(status)) {
    575            //found
    576            LocalPointer<MessageFormat> messageFormat(
    577                new MessageFormat(UnicodeString(true, pattern, ptLength), getLocale(err), err), err);
    578            if (U_FAILURE(err)) {
    579                return;
    580            }
    581            MessageFormat** formatters = static_cast<MessageFormat**>(countToPatterns->get(srcPluralCount));
    582            if (formatters == nullptr) {
    583                LocalMemory<MessageFormat *> localFormatters(
    584                        static_cast<MessageFormat**>(uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT * sizeof(MessageFormat*))));
    585                formatters = localFormatters.getAlias();
    586                localFormatters[UTMUTFMT_FULL_STYLE] = nullptr;
    587                localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = nullptr;
    588                countToPatterns->put(srcPluralCount, localFormatters.orphan(), err);
    589                if (U_FAILURE(err)) {
    590                    return;
    591                }
    592            }
    593            //delete formatters[style];
    594            formatters[style] = messageFormat.orphan();
    595            return;
    596        }
    597        status = U_ZERO_ERROR;
    598        if (parentLocale.isEmpty()) {
    599            break;
    600        }
    601    }
    602 
    603    // if no unitsShort resource was found even after fallback to root locale
    604    // then search the units resource fallback from the current level to root
    605    if ( parentLocale.isEmpty() && uprv_strcmp(key, gShortUnitsTag) == 0) {
    606 #ifdef TMUTFMT_DEBUG
    607        std::cout << "loop into searchInLocaleChain since Short-Long-Alternative \n";
    608 #endif
    609        CharString pLocale(localeName, -1, err);
    610        // Add an underscore at the tail of locale name,
    611        // so that searchInLocaleChain will check the current locale before falling back
    612        pLocale.append('_', err);
    613        searchInLocaleChain(style, gUnitsTag, pLocale.data(), srcTimeUnitField, srcPluralCount,
    614                             searchPluralCount, countToPatterns, err);
    615        if (U_FAILURE(err)) {
    616            return;
    617        }
    618        MessageFormat** formatters = static_cast<MessageFormat**>(countToPatterns->get(srcPluralCount));
    619        if (formatters != nullptr && formatters[style] != nullptr) {
    620            return;
    621        }
    622    }
    623 
    624    // if not found the pattern for this plural count at all,
    625    // fall-back to plural count "other"
    626    if ( uprv_strcmp(searchPluralCount, gPluralCountOther) == 0 ) {
    627        // set default fall back the same as the resource in root
    628        LocalPointer<MessageFormat> messageFormat;
    629        const char16_t *pattern = nullptr;
    630        if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_SECOND ) {
    631            pattern = DEFAULT_PATTERN_FOR_SECOND;
    632        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MINUTE ) {
    633            pattern = DEFAULT_PATTERN_FOR_MINUTE;
    634        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_HOUR ) {
    635            pattern = DEFAULT_PATTERN_FOR_HOUR;
    636        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_WEEK ) {
    637            pattern = DEFAULT_PATTERN_FOR_WEEK;
    638        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_DAY ) {
    639            pattern = DEFAULT_PATTERN_FOR_DAY;
    640        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MONTH ) {
    641            pattern = DEFAULT_PATTERN_FOR_MONTH;
    642        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_YEAR ) {
    643            pattern = DEFAULT_PATTERN_FOR_YEAR;
    644        }
    645        if (pattern != nullptr) {
    646            messageFormat.adoptInsteadAndCheckErrorCode(
    647                     new MessageFormat(UnicodeString(true, pattern, -1), getLocale(err), err), err);
    648        }
    649        if (U_FAILURE(err)) {
    650            return;
    651        }
    652        MessageFormat** formatters = static_cast<MessageFormat**>(countToPatterns->get(srcPluralCount));
    653        if (formatters == nullptr) {
    654            LocalMemory<MessageFormat *> localFormatters (
    655                    static_cast<MessageFormat**>(uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT * sizeof(MessageFormat*))));
    656            if (localFormatters.isNull()) {
    657                err = U_MEMORY_ALLOCATION_ERROR;
    658                return;
    659            }
    660            formatters = localFormatters.getAlias();
    661            formatters[UTMUTFMT_FULL_STYLE] = nullptr;
    662            formatters[UTMUTFMT_ABBREVIATED_STYLE] = nullptr;
    663            countToPatterns->put(srcPluralCount, localFormatters.orphan(), err);
    664        }
    665        if (U_SUCCESS(err)) {
    666            //delete formatters[style];
    667            formatters[style] = messageFormat.orphan();
    668        }
    669    } else {
    670        // fall back to rule "other", and search in parents
    671        searchInLocaleChain(style, key, localeName, srcTimeUnitField, srcPluralCount,
    672                            gPluralCountOther, countToPatterns, err);
    673    }
    674 }
    675 
    676 void
    677 TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) {
    678    if (setMeasureFormatLocale(locale, status)) {
    679        setup(status);
    680    }
    681 }
    682 
    683 
    684 void
    685 TimeUnitFormat::setNumberFormat(const NumberFormat& format, UErrorCode& status){
    686    if (U_FAILURE(status)) {
    687        return;
    688    }
    689    adoptNumberFormat(format.clone(), status);
    690 }
    691 
    692 
    693 void
    694 TimeUnitFormat::deleteHash(Hashtable* htable) {
    695    int32_t pos = UHASH_FIRST;
    696    const UHashElement* element = nullptr;
    697    if ( htable ) {
    698        while ( (element = htable->nextElement(pos)) != nullptr ) {
    699            const UHashTok valueTok = element->value;
    700            const MessageFormat** value = static_cast<const MessageFormat**>(valueTok.pointer);
    701            delete value[UTMUTFMT_FULL_STYLE];
    702            delete value[UTMUTFMT_ABBREVIATED_STYLE];
    703            //delete[] value;
    704            uprv_free(value);
    705        }
    706    }
    707    delete htable;
    708 }
    709 
    710 
    711 void
    712 TimeUnitFormat::copyHash(const Hashtable* source, Hashtable* target, UErrorCode& status) {
    713    if ( U_FAILURE(status) ) {
    714        return;
    715    }
    716    int32_t pos = UHASH_FIRST;
    717    const UHashElement* element = nullptr;
    718    if ( source ) {
    719        while ( (element = source->nextElement(pos)) != nullptr ) {
    720            const UHashTok keyTok = element->key;
    721            const UnicodeString* key = static_cast<UnicodeString*>(keyTok.pointer);
    722            const UHashTok valueTok = element->value;
    723            const MessageFormat** value = static_cast<const MessageFormat**>(valueTok.pointer);
    724            MessageFormat** newVal = static_cast<MessageFormat**>(uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT * sizeof(MessageFormat*)));
    725            newVal[0] = value[0]->clone();
    726            newVal[1] = value[1]->clone();
    727            target->put(UnicodeString(*key), newVal, status);
    728            if ( U_FAILURE(status) ) {
    729                delete newVal[0];
    730                delete newVal[1];
    731                uprv_free(newVal);
    732                return;
    733            }
    734        }
    735    }
    736 }
    737 
    738 
    739 U_CDECL_BEGIN
    740 
    741 /**
    742 * set hash table value comparator
    743 *
    744 * @param val1  one value in comparison
    745 * @param val2  the other value in comparison
    746 * @return      true if 2 values are the same, false otherwise
    747 */
    748 static UBool U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2);
    749 
    750 static UBool
    751 U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2) {
    752    const MessageFormat** pattern1 = (const MessageFormat**)val1.pointer;
    753    const MessageFormat** pattern2 = (const MessageFormat**)val2.pointer;
    754    return *pattern1[0] == *pattern2[0] && *pattern1[1] == *pattern2[1];
    755 }
    756 
    757 U_CDECL_END
    758 
    759 Hashtable*
    760 TimeUnitFormat::initHash(UErrorCode& status) {
    761    if ( U_FAILURE(status) ) {
    762        return nullptr;
    763    }
    764    Hashtable* hTable;
    765    if ( (hTable = new Hashtable(true, status)) == nullptr ) {
    766        status = U_MEMORY_ALLOCATION_ERROR;
    767        return nullptr;
    768    }
    769    if ( U_FAILURE(status) ) {
    770        delete hTable;
    771        return nullptr;
    772    }
    773    hTable->setValueComparator(tmutfmtHashTableValueComparator);
    774    return hTable;
    775 }
    776 
    777 
    778 const char*
    779 TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField,
    780                                UErrorCode& status) {
    781    if (U_FAILURE(status)) {
    782        return nullptr;
    783    }
    784    switch (unitField) {
    785      case TimeUnit::UTIMEUNIT_YEAR:
    786        return gTimeUnitYear;
    787      case TimeUnit::UTIMEUNIT_MONTH:
    788        return gTimeUnitMonth;
    789      case TimeUnit::UTIMEUNIT_DAY:
    790        return gTimeUnitDay;
    791      case TimeUnit::UTIMEUNIT_WEEK:
    792        return gTimeUnitWeek;
    793      case TimeUnit::UTIMEUNIT_HOUR:
    794        return gTimeUnitHour;
    795      case TimeUnit::UTIMEUNIT_MINUTE:
    796        return gTimeUnitMinute;
    797      case TimeUnit::UTIMEUNIT_SECOND:
    798        return gTimeUnitSecond;
    799      default:
    800        status = U_ILLEGAL_ARGUMENT_ERROR;
    801        return nullptr;
    802    }
    803 }
    804 
    805 U_NAMESPACE_END
    806 
    807 #endif