123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806 |
- // © 2016 and later: Unicode, Inc. and others.
- // License & terms of use: http://www.unicode.org/copyright.html
- /*
- *******************************************************************************
- * Copyright (C) 2008-2015, Google, International Business Machines Corporation
- * and others. All Rights Reserved.
- *******************************************************************************
- */
- #include "unicode/tmutfmt.h"
- #if !UCONFIG_NO_FORMATTING
- #include "unicode/decimfmt.h"
- #include "unicode/localpointer.h"
- #include "plurrule_impl.h"
- #include "uvector.h"
- #include "charstr.h"
- #include "cmemory.h"
- #include "cstring.h"
- #include "hash.h"
- #include "uresimp.h"
- #include "ureslocs.h"
- #include "unicode/msgfmt.h"
- #include "uassert.h"
- #define LEFT_CURLY_BRACKET ((char16_t)0x007B)
- #define RIGHT_CURLY_BRACKET ((char16_t)0x007D)
- #define SPACE ((char16_t)0x0020)
- #define DIGIT_ZERO ((char16_t)0x0030)
- #define LOW_S ((char16_t)0x0073)
- #define LOW_M ((char16_t)0x006D)
- #define LOW_I ((char16_t)0x0069)
- #define LOW_N ((char16_t)0x006E)
- #define LOW_H ((char16_t)0x0068)
- #define LOW_W ((char16_t)0x0077)
- #define LOW_D ((char16_t)0x0064)
- #define LOW_Y ((char16_t)0x0079)
- #define LOW_Z ((char16_t)0x007A)
- #define LOW_E ((char16_t)0x0065)
- #define LOW_R ((char16_t)0x0072)
- #define LOW_O ((char16_t)0x006F)
- #define LOW_N ((char16_t)0x006E)
- #define LOW_T ((char16_t)0x0074)
- //TODO: define in compile time
- //#define TMUTFMT_DEBUG 1
- #ifdef TMUTFMT_DEBUG
- #include <iostream>
- #endif
- U_NAMESPACE_BEGIN
- UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat)
- static const char gUnitsTag[] = "units";
- static const char gShortUnitsTag[] = "unitsShort";
- static const char gTimeUnitYear[] = "year";
- static const char gTimeUnitMonth[] = "month";
- static const char gTimeUnitDay[] = "day";
- static const char gTimeUnitWeek[] = "week";
- static const char gTimeUnitHour[] = "hour";
- static const char gTimeUnitMinute[] = "minute";
- static const char gTimeUnitSecond[] = "second";
- static const char gPluralCountOther[] = "other";
- static const char16_t DEFAULT_PATTERN_FOR_SECOND[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_S, 0};
- static const char16_t DEFAULT_PATTERN_FOR_MINUTE[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, LOW_I, LOW_N, 0};
- static const char16_t DEFAULT_PATTERN_FOR_HOUR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_H, 0};
- static const char16_t DEFAULT_PATTERN_FOR_WEEK[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_W, 0};
- static const char16_t DEFAULT_PATTERN_FOR_DAY[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_D, 0};
- static const char16_t DEFAULT_PATTERN_FOR_MONTH[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, 0};
- static const char16_t DEFAULT_PATTERN_FOR_YEAR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_Y, 0};
- static const char16_t PLURAL_COUNT_ZERO[] = {LOW_Z, LOW_E, LOW_R, LOW_O, 0};
- static const char16_t PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0};
- static const char16_t PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0};
- TimeUnitFormat::TimeUnitFormat(UErrorCode& status) {
- initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE, nullptr, status);
- create(UTMUTFMT_FULL_STYLE, status);
- }
- TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) {
- initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, nullptr, status);
- create(UTMUTFMT_FULL_STYLE, status);
- }
- TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) {
- switch (style) {
- case UTMUTFMT_FULL_STYLE:
- initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, nullptr, status);
- break;
- case UTMUTFMT_ABBREVIATED_STYLE:
- initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, nullptr, status);
- break;
- default:
- initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, nullptr, status);
- break;
- }
- create(style, status);
- }
- TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other)
- : MeasureFormat(other),
- fStyle(other.fStyle)
- {
- for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
- i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
- i = (TimeUnit::UTimeUnitFields)(i+1)) {
- UErrorCode status = U_ZERO_ERROR;
- fTimeUnitToCountToPatterns[i] = initHash(status);
- if (U_SUCCESS(status)) {
- copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
- } else {
- delete fTimeUnitToCountToPatterns[i];
- fTimeUnitToCountToPatterns[i] = nullptr;
- }
- }
- }
- TimeUnitFormat::~TimeUnitFormat() {
- for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
- i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
- i = (TimeUnit::UTimeUnitFields)(i+1)) {
- deleteHash(fTimeUnitToCountToPatterns[i]);
- fTimeUnitToCountToPatterns[i] = nullptr;
- }
- }
- TimeUnitFormat*
- TimeUnitFormat::clone() const {
- return new TimeUnitFormat(*this);
- }
- TimeUnitFormat&
- TimeUnitFormat::operator=(const TimeUnitFormat& other) {
- if (this == &other) {
- return *this;
- }
- MeasureFormat::operator=(other);
- for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
- i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
- i = (TimeUnit::UTimeUnitFields)(i+1)) {
- deleteHash(fTimeUnitToCountToPatterns[i]);
- fTimeUnitToCountToPatterns[i] = nullptr;
- }
- for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
- i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
- i = (TimeUnit::UTimeUnitFields)(i+1)) {
- UErrorCode status = U_ZERO_ERROR;
- fTimeUnitToCountToPatterns[i] = initHash(status);
- if (U_SUCCESS(status)) {
- copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
- } else {
- delete fTimeUnitToCountToPatterns[i];
- fTimeUnitToCountToPatterns[i] = nullptr;
- }
- }
- fStyle = other.fStyle;
- return *this;
- }
- void
- TimeUnitFormat::parseObject(const UnicodeString& source,
- Formattable& result,
- ParsePosition& pos) const {
- Formattable resultNumber(0.0);
- UBool withNumberFormat = false;
- TimeUnit::UTimeUnitFields resultTimeUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT;
- int32_t oldPos = pos.getIndex();
- int32_t newPos = -1;
- int32_t longestParseDistance = 0;
- UnicodeString* countOfLongestMatch = nullptr;
- #ifdef TMUTFMT_DEBUG
- char res[1000];
- source.extract(0, source.length(), res, "UTF-8");
- std::cout << "parse source: " << res << "\n";
- #endif
- // parse by iterating through all available patterns
- // and looking for the longest match.
- for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
- i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
- i = (TimeUnit::UTimeUnitFields)(i+1)) {
- Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
- int32_t elemPos = UHASH_FIRST;
- const UHashElement* elem = nullptr;
- while ((elem = countToPatterns->nextElement(elemPos)) != nullptr){
- const UHashTok keyTok = elem->key;
- UnicodeString* count = (UnicodeString*)keyTok.pointer;
- #ifdef TMUTFMT_DEBUG
- count->extract(0, count->length(), res, "UTF-8");
- std::cout << "parse plural count: " << res << "\n";
- #endif
- const UHashTok valueTok = elem->value;
- // the value is a pair of MessageFormat*
- MessageFormat** patterns = (MessageFormat**)valueTok.pointer;
- for (UTimeUnitFormatStyle style = UTMUTFMT_FULL_STYLE; style < UTMUTFMT_FORMAT_STYLE_COUNT;
- style = (UTimeUnitFormatStyle)(style + 1)) {
- MessageFormat* pattern = patterns[style];
- pos.setErrorIndex(-1);
- pos.setIndex(oldPos);
- // see if we can parse
- Formattable parsed;
- pattern->parseObject(source, parsed, pos);
- if (pos.getErrorIndex() != -1 || pos.getIndex() == oldPos) {
- continue;
- }
- #ifdef TMUTFMT_DEBUG
- std::cout << "parsed.getType: " << parsed.getType() << "\n";
- #endif
- Formattable tmpNumber(0.0);
- if (pattern->getArgTypeCount() != 0) {
- Formattable& temp = parsed[0];
- if (temp.getType() == Formattable::kString) {
- UnicodeString tmpString;
- UErrorCode pStatus = U_ZERO_ERROR;
- getNumberFormatInternal().parse(temp.getString(tmpString), tmpNumber, pStatus);
- if (U_FAILURE(pStatus)) {
- continue;
- }
- } else if (temp.isNumeric()) {
- tmpNumber = temp;
- } else {
- continue;
- }
- }
- int32_t parseDistance = pos.getIndex() - oldPos;
- if (parseDistance > longestParseDistance) {
- if (pattern->getArgTypeCount() != 0) {
- resultNumber = tmpNumber;
- withNumberFormat = true;
- } else {
- withNumberFormat = false;
- }
- resultTimeUnit = i;
- newPos = pos.getIndex();
- longestParseDistance = parseDistance;
- countOfLongestMatch = count;
- }
- }
- }
- }
- /* After find the longest match, parse the number.
- * Result number could be null for the pattern without number pattern.
- * such as unit pattern in Arabic.
- * When result number is null, use plural rule to set the number.
- */
- if (withNumberFormat == false && longestParseDistance != 0) {
- // set the number using plurrual count
- if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ZERO, 4)) {
- resultNumber = Formattable(0.0);
- } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ONE, 3)) {
- resultNumber = Formattable(1.0);
- } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_TWO, 3)) {
- resultNumber = Formattable(2.0);
- } else {
- // should not happen.
- // TODO: how to handle?
- resultNumber = Formattable(3.0);
- }
- }
- if (longestParseDistance == 0) {
- pos.setIndex(oldPos);
- pos.setErrorIndex(0);
- } else {
- UErrorCode status = U_ZERO_ERROR;
- LocalPointer<TimeUnitAmount> tmutamt(new TimeUnitAmount(resultNumber, resultTimeUnit, status), status);
- if (U_SUCCESS(status)) {
- result.adoptObject(tmutamt.orphan());
- pos.setIndex(newPos);
- pos.setErrorIndex(-1);
- } else {
- pos.setIndex(oldPos);
- pos.setErrorIndex(0);
- }
- }
- }
- void
- TimeUnitFormat::create(UTimeUnitFormatStyle style, UErrorCode& status) {
- // fTimeUnitToCountToPatterns[] must have its elements initialized to nullptr first
- // before checking for failure status.
- for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
- i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
- i = (TimeUnit::UTimeUnitFields)(i+1)) {
- fTimeUnitToCountToPatterns[i] = nullptr;
- }
- if (U_FAILURE(status)) {
- return;
- }
- if (style < UTMUTFMT_FULL_STYLE || style >= UTMUTFMT_FORMAT_STYLE_COUNT) {
- status = U_ILLEGAL_ARGUMENT_ERROR;
- return;
- }
- fStyle = style;
- //TODO: format() and parseObj() are const member functions,
- //so, can not do lazy initialization in C++.
- //setup has to be done in constructors.
- //and here, the behavior is not consistent with Java.
- //In Java, create an empty instance does not setup locale as
- //default locale. If it followed by setNumberFormat(),
- //in format(), the locale will set up as the locale in fNumberFormat.
- //But in C++, this sets the locale as the default locale.
- setup(status);
- }
- void
- TimeUnitFormat::setup(UErrorCode& err) {
- initDataMembers(err);
- UVector pluralCounts(nullptr, uhash_compareUnicodeString, 6, err);
- LocalPointer<StringEnumeration> keywords(getPluralRules().getKeywords(err), err);
- if (U_FAILURE(err)) {
- return;
- }
- UnicodeString* pluralCount;
- while ((pluralCount = const_cast<UnicodeString*>(keywords->snext(err))) != nullptr) {
- pluralCounts.addElement(pluralCount, err);
- }
- readFromCurrentLocale(UTMUTFMT_FULL_STYLE, gUnitsTag, pluralCounts, err);
- checkConsistency(UTMUTFMT_FULL_STYLE, gUnitsTag, err);
- readFromCurrentLocale(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, pluralCounts, err);
- checkConsistency(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, err);
- }
- void
- TimeUnitFormat::initDataMembers(UErrorCode& err){
- if (U_FAILURE(err)) {
- return;
- }
- for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
- i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
- i = (TimeUnit::UTimeUnitFields)(i+1)) {
- deleteHash(fTimeUnitToCountToPatterns[i]);
- fTimeUnitToCountToPatterns[i] = nullptr;
- }
- }
- struct TimeUnitFormatReadSink : public ResourceSink {
- TimeUnitFormat *timeUnitFormatObj;
- const UVector &pluralCounts;
- UTimeUnitFormatStyle style;
- UBool beenHere;
- TimeUnitFormatReadSink(TimeUnitFormat *timeUnitFormatObj,
- const UVector &pluralCounts, UTimeUnitFormatStyle style) :
- timeUnitFormatObj(timeUnitFormatObj), pluralCounts(pluralCounts),
- style(style), beenHere(false){}
- virtual ~TimeUnitFormatReadSink();
- virtual void put(const char *key, ResourceValue &value, UBool, UErrorCode &errorCode) override {
- // Skip all put() calls except the first one -- discard all fallback data.
- if (beenHere) {
- return;
- } else {
- beenHere = true;
- }
- ResourceTable units = value.getTable(errorCode);
- if (U_FAILURE(errorCode)) { return; }
- for (int32_t i = 0; units.getKeyAndValue(i, key, value); ++i) {
- const char* timeUnitName = key;
- if (timeUnitName == nullptr) {
- continue;
- }
- TimeUnit::UTimeUnitFields timeUnitField = TimeUnit::UTIMEUNIT_FIELD_COUNT;
- if ( uprv_strcmp(timeUnitName, gTimeUnitYear) == 0 ) {
- timeUnitField = TimeUnit::UTIMEUNIT_YEAR;
- } else if ( uprv_strcmp(timeUnitName, gTimeUnitMonth) == 0 ) {
- timeUnitField = TimeUnit::UTIMEUNIT_MONTH;
- } else if ( uprv_strcmp(timeUnitName, gTimeUnitDay) == 0 ) {
- timeUnitField = TimeUnit::UTIMEUNIT_DAY;
- } else if ( uprv_strcmp(timeUnitName, gTimeUnitHour) == 0 ) {
- timeUnitField = TimeUnit::UTIMEUNIT_HOUR;
- } else if ( uprv_strcmp(timeUnitName, gTimeUnitMinute) == 0 ) {
- timeUnitField = TimeUnit::UTIMEUNIT_MINUTE;
- } else if ( uprv_strcmp(timeUnitName, gTimeUnitSecond) == 0 ) {
- timeUnitField = TimeUnit::UTIMEUNIT_SECOND;
- } else if ( uprv_strcmp(timeUnitName, gTimeUnitWeek) == 0 ) {
- timeUnitField = TimeUnit::UTIMEUNIT_WEEK;
- } else {
- continue;
- }
- LocalPointer<Hashtable> localCountToPatterns;
- Hashtable *countToPatterns =
- timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField];
- if (countToPatterns == nullptr) {
- localCountToPatterns.adoptInsteadAndCheckErrorCode(
- timeUnitFormatObj->initHash(errorCode), errorCode);
- countToPatterns = localCountToPatterns.getAlias();
- if (U_FAILURE(errorCode)) {
- return;
- }
- }
- ResourceTable countsToPatternTable = value.getTable(errorCode);
- if (U_FAILURE(errorCode)) {
- continue;
- }
- for (int32_t j = 0; countsToPatternTable.getKeyAndValue(j, key, value); ++j) {
- errorCode = U_ZERO_ERROR;
- UnicodeString pattern = value.getUnicodeString(errorCode);
- if (U_FAILURE(errorCode)) {
- continue;
- }
- UnicodeString pluralCountUniStr(key, -1, US_INV);
- if (!pluralCounts.contains(&pluralCountUniStr)) {
- continue;
- }
- LocalPointer<MessageFormat> messageFormat(new MessageFormat(
- pattern, timeUnitFormatObj->getLocale(errorCode), errorCode), errorCode);
- if (U_FAILURE(errorCode)) {
- return;
- }
- MessageFormat** formatters =
- (MessageFormat**)countToPatterns->get(pluralCountUniStr);
- if (formatters == nullptr) {
- LocalMemory<MessageFormat *> localFormatters(
- (MessageFormat **)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
- if (localFormatters.isNull()) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- localFormatters[UTMUTFMT_FULL_STYLE] = nullptr;
- localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = nullptr;
- countToPatterns->put(pluralCountUniStr, localFormatters.getAlias(), errorCode);
- if (U_FAILURE(errorCode)) {
- return;
- }
- formatters = localFormatters.orphan();
- }
- formatters[style] = messageFormat.orphan();
- }
- if (timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] == nullptr) {
- timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] = localCountToPatterns.orphan();
- }
- }
- }
- };
- TimeUnitFormatReadSink::~TimeUnitFormatReadSink() {}
- void
- TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key,
- const UVector& pluralCounts, UErrorCode& err) {
- if (U_FAILURE(err)) {
- return;
- }
- // fill timeUnitToCountToPatterns from resource file
- // err is used to indicate wrong status except missing resource.
- // status is an error code used in resource lookup.
- // status does not affect "err".
- UErrorCode status = U_ZERO_ERROR;
- LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_UNIT, getLocaleID(status), &status));
- LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, nullptr, &status));
- ures_getByKey(unitsRes.getAlias(), "duration", unitsRes.getAlias(), &status);
- if (U_FAILURE(status)) {
- return;
- }
- TimeUnitFormatReadSink sink(this, pluralCounts, style);
- ures_getAllItemsWithFallback(unitsRes.getAlias(), "", sink, status);
- }
- void
- TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style, const char* key, UErrorCode& err) {
- if (U_FAILURE(err)) {
- return;
- }
- // there should be patterns for each plural rule in each time unit.
- // For each time unit,
- // for each plural rule, following is unit pattern fall-back rule:
- // ( for example: "one" hour )
- // look for its unit pattern in its locale tree.
- // if pattern is not found in its own locale, such as de_DE,
- // look for the pattern in its parent, such as de,
- // keep looking till found or till root.
- // if the pattern is not found in root either,
- // fallback to plural count "other",
- // look for the pattern of "other" in the locale tree:
- // "de_DE" to "de" to "root".
- // If not found, fall back to value of
- // static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h".
- //
- // Following is consistency check to create pattern for each
- // plural rule in each time unit using above fall-back rule.
- //
- LocalPointer<StringEnumeration> keywords(
- getPluralRules().getKeywords(err), err);
- const UnicodeString* pluralCount;
- while (U_SUCCESS(err) && (pluralCount = keywords->snext(err)) != nullptr) {
- for (int32_t i = 0; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; ++i) {
- // for each time unit,
- // get all the patterns for each plural rule in this locale.
- Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
- if ( countToPatterns == nullptr ) {
- fTimeUnitToCountToPatterns[i] = countToPatterns = initHash(err);
- if (U_FAILURE(err)) {
- return;
- }
- }
- MessageFormat** formatters = (MessageFormat**)countToPatterns->get(*pluralCount);
- if( formatters == nullptr || formatters[style] == nullptr ) {
- // look through parents
- const char* localeName = getLocaleID(err);
- CharString pluralCountChars;
- pluralCountChars.appendInvariantChars(*pluralCount, err);
- searchInLocaleChain(style, key, localeName,
- (TimeUnit::UTimeUnitFields)i,
- *pluralCount, pluralCountChars.data(),
- countToPatterns, err);
- }
- // TODO: what to do with U_FAILURE(err) at this point.
- // As is, the outer loop continues to run, but does nothing.
- }
- }
- }
- // srcPluralCount is the original plural count on which the pattern is
- // searched for.
- // searchPluralCount is the fallback plural count.
- // For example, to search for pattern for ""one" hour",
- // "one" is the srcPluralCount,
- // if the pattern is not found even in root, fallback to
- // using patterns of plural count "other",
- // then, "other" is the searchPluralCount.
- void
- TimeUnitFormat::searchInLocaleChain(UTimeUnitFormatStyle style, const char* key, const char* localeName,
- TimeUnit::UTimeUnitFields srcTimeUnitField,
- const UnicodeString& srcPluralCount,
- const char* searchPluralCount,
- Hashtable* countToPatterns,
- UErrorCode& err) {
- if (U_FAILURE(err)) {
- return;
- }
- UErrorCode status = U_ZERO_ERROR;
- char parentLocale[ULOC_FULLNAME_CAPACITY];
- uprv_strcpy(parentLocale, localeName);
- int32_t locNameLen;
- U_ASSERT(countToPatterns != nullptr);
- while ((locNameLen = uloc_getParent(parentLocale, parentLocale,
- ULOC_FULLNAME_CAPACITY, &status)) >= 0){
- // look for pattern for srcPluralCount in locale tree
- LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_UNIT, parentLocale, &status));
- LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, nullptr, &status));
- const char* timeUnitName = getTimeUnitName(srcTimeUnitField, status);
- LocalUResourceBundlePointer countsToPatternRB(ures_getByKey(unitsRes.getAlias(), timeUnitName, nullptr, &status));
- const char16_t* pattern;
- int32_t ptLength;
- pattern = ures_getStringByKeyWithFallback(countsToPatternRB.getAlias(), searchPluralCount, &ptLength, &status);
- if (U_SUCCESS(status)) {
- //found
- LocalPointer<MessageFormat> messageFormat(
- new MessageFormat(UnicodeString(true, pattern, ptLength), getLocale(err), err), err);
- if (U_FAILURE(err)) {
- return;
- }
- MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
- if (formatters == nullptr) {
- LocalMemory<MessageFormat *> localFormatters(
- (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
- formatters = localFormatters.getAlias();
- localFormatters[UTMUTFMT_FULL_STYLE] = nullptr;
- localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = nullptr;
- countToPatterns->put(srcPluralCount, localFormatters.orphan(), err);
- if (U_FAILURE(err)) {
- return;
- }
- }
- //delete formatters[style];
- formatters[style] = messageFormat.orphan();
- return;
- }
- status = U_ZERO_ERROR;
- if (locNameLen == 0) {
- break;
- }
- }
- // if no unitsShort resource was found even after fallback to root locale
- // then search the units resource fallback from the current level to root
- if ( locNameLen == 0 && uprv_strcmp(key, gShortUnitsTag) == 0) {
- #ifdef TMUTFMT_DEBUG
- std::cout << "loop into searchInLocaleChain since Short-Long-Alternative \n";
- #endif
- CharString pLocale(localeName, -1, err);
- // Add an underscore at the tail of locale name,
- // so that searchInLocaleChain will check the current locale before falling back
- pLocale.append('_', err);
- searchInLocaleChain(style, gUnitsTag, pLocale.data(), srcTimeUnitField, srcPluralCount,
- searchPluralCount, countToPatterns, err);
- if (U_FAILURE(err)) {
- return;
- }
- MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
- if (formatters != nullptr && formatters[style] != nullptr) {
- return;
- }
- }
- // if not found the pattern for this plural count at all,
- // fall-back to plural count "other"
- if ( uprv_strcmp(searchPluralCount, gPluralCountOther) == 0 ) {
- // set default fall back the same as the resource in root
- LocalPointer<MessageFormat> messageFormat;
- const char16_t *pattern = nullptr;
- if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_SECOND ) {
- pattern = DEFAULT_PATTERN_FOR_SECOND;
- } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MINUTE ) {
- pattern = DEFAULT_PATTERN_FOR_MINUTE;
- } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_HOUR ) {
- pattern = DEFAULT_PATTERN_FOR_HOUR;
- } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_WEEK ) {
- pattern = DEFAULT_PATTERN_FOR_WEEK;
- } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_DAY ) {
- pattern = DEFAULT_PATTERN_FOR_DAY;
- } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MONTH ) {
- pattern = DEFAULT_PATTERN_FOR_MONTH;
- } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_YEAR ) {
- pattern = DEFAULT_PATTERN_FOR_YEAR;
- }
- if (pattern != nullptr) {
- messageFormat.adoptInsteadAndCheckErrorCode(
- new MessageFormat(UnicodeString(true, pattern, -1), getLocale(err), err), err);
- }
- if (U_FAILURE(err)) {
- return;
- }
- MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
- if (formatters == nullptr) {
- LocalMemory<MessageFormat *> localFormatters (
- (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
- if (localFormatters.isNull()) {
- err = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- formatters = localFormatters.getAlias();
- formatters[UTMUTFMT_FULL_STYLE] = nullptr;
- formatters[UTMUTFMT_ABBREVIATED_STYLE] = nullptr;
- countToPatterns->put(srcPluralCount, localFormatters.orphan(), err);
- }
- if (U_SUCCESS(err)) {
- //delete formatters[style];
- formatters[style] = messageFormat.orphan();
- }
- } else {
- // fall back to rule "other", and search in parents
- searchInLocaleChain(style, key, localeName, srcTimeUnitField, srcPluralCount,
- gPluralCountOther, countToPatterns, err);
- }
- }
- void
- TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) {
- if (setMeasureFormatLocale(locale, status)) {
- setup(status);
- }
- }
- void
- TimeUnitFormat::setNumberFormat(const NumberFormat& format, UErrorCode& status){
- if (U_FAILURE(status)) {
- return;
- }
- adoptNumberFormat(format.clone(), status);
- }
- void
- TimeUnitFormat::deleteHash(Hashtable* htable) {
- int32_t pos = UHASH_FIRST;
- const UHashElement* element = nullptr;
- if ( htable ) {
- while ( (element = htable->nextElement(pos)) != nullptr ) {
- const UHashTok valueTok = element->value;
- const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
- delete value[UTMUTFMT_FULL_STYLE];
- delete value[UTMUTFMT_ABBREVIATED_STYLE];
- //delete[] value;
- uprv_free(value);
- }
- }
- delete htable;
- }
- void
- TimeUnitFormat::copyHash(const Hashtable* source, Hashtable* target, UErrorCode& status) {
- if ( U_FAILURE(status) ) {
- return;
- }
- int32_t pos = UHASH_FIRST;
- const UHashElement* element = nullptr;
- if ( source ) {
- while ( (element = source->nextElement(pos)) != nullptr ) {
- const UHashTok keyTok = element->key;
- const UnicodeString* key = (UnicodeString*)keyTok.pointer;
- const UHashTok valueTok = element->value;
- const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
- MessageFormat** newVal = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*));
- newVal[0] = value[0]->clone();
- newVal[1] = value[1]->clone();
- target->put(UnicodeString(*key), newVal, status);
- if ( U_FAILURE(status) ) {
- delete newVal[0];
- delete newVal[1];
- uprv_free(newVal);
- return;
- }
- }
- }
- }
- U_CDECL_BEGIN
- /**
- * set hash table value comparator
- *
- * @param val1 one value in comparison
- * @param val2 the other value in comparison
- * @return true if 2 values are the same, false otherwise
- */
- static UBool U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2);
- static UBool
- U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2) {
- const MessageFormat** pattern1 = (const MessageFormat**)val1.pointer;
- const MessageFormat** pattern2 = (const MessageFormat**)val2.pointer;
- return *pattern1[0] == *pattern2[0] && *pattern1[1] == *pattern2[1];
- }
- U_CDECL_END
- Hashtable*
- TimeUnitFormat::initHash(UErrorCode& status) {
- if ( U_FAILURE(status) ) {
- return nullptr;
- }
- Hashtable* hTable;
- if ( (hTable = new Hashtable(true, status)) == nullptr ) {
- status = U_MEMORY_ALLOCATION_ERROR;
- return nullptr;
- }
- if ( U_FAILURE(status) ) {
- delete hTable;
- return nullptr;
- }
- hTable->setValueComparator(tmutfmtHashTableValueComparator);
- return hTable;
- }
- const char*
- TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField,
- UErrorCode& status) {
- if (U_FAILURE(status)) {
- return nullptr;
- }
- switch (unitField) {
- case TimeUnit::UTIMEUNIT_YEAR:
- return gTimeUnitYear;
- case TimeUnit::UTIMEUNIT_MONTH:
- return gTimeUnitMonth;
- case TimeUnit::UTIMEUNIT_DAY:
- return gTimeUnitDay;
- case TimeUnit::UTIMEUNIT_WEEK:
- return gTimeUnitWeek;
- case TimeUnit::UTIMEUNIT_HOUR:
- return gTimeUnitHour;
- case TimeUnit::UTIMEUNIT_MINUTE:
- return gTimeUnitMinute;
- case TimeUnit::UTIMEUNIT_SECOND:
- return gTimeUnitSecond;
- default:
- status = U_ILLEGAL_ARGUMENT_ERROR;
- return nullptr;
- }
- }
- U_NAMESPACE_END
- #endif
|