/* ******************************************************************************* * Copyright (C) 2008-2013, Google, International Business Machines Corporation * and others. All Rights Reserved. ******************************************************************************* */ #include "utypeinfo.h" // for 'typeid' to work #include "unicode/tmutfmt.h" #if !UCONFIG_NO_FORMATTING #include "uvector.h" #include "charstr.h" #include "cmemory.h" #include "cstring.h" #include "hash.h" #include "uresimp.h" #include "unicode/msgfmt.h" #include "uassert.h" #define LEFT_CURLY_BRACKET ((UChar)0x007B) #define RIGHT_CURLY_BRACKET ((UChar)0x007D) #define SPACE ((UChar)0x0020) #define DIGIT_ZERO ((UChar)0x0030) #define LOW_S ((UChar)0x0073) #define LOW_M ((UChar)0x006D) #define LOW_I ((UChar)0x0069) #define LOW_N ((UChar)0x006E) #define LOW_H ((UChar)0x0068) #define LOW_W ((UChar)0x0077) #define LOW_D ((UChar)0x0064) #define LOW_Y ((UChar)0x0079) #define LOW_Z ((UChar)0x007A) #define LOW_E ((UChar)0x0065) #define LOW_R ((UChar)0x0072) #define LOW_O ((UChar)0x006F) #define LOW_N ((UChar)0x006E) #define LOW_T ((UChar)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 UChar DEFAULT_PATTERN_FOR_SECOND[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_S, 0}; static const UChar DEFAULT_PATTERN_FOR_MINUTE[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, LOW_I, LOW_N, 0}; static const UChar DEFAULT_PATTERN_FOR_HOUR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_H, 0}; static const UChar DEFAULT_PATTERN_FOR_WEEK[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_W, 0}; static const UChar DEFAULT_PATTERN_FOR_DAY[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_D, 0}; static const UChar DEFAULT_PATTERN_FOR_MONTH[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, 0}; static const UChar DEFAULT_PATTERN_FOR_YEAR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_Y, 0}; static const UChar PLURAL_COUNT_ZERO[] = {LOW_Z, LOW_E, LOW_R, LOW_O, 0}; static const UChar PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0}; static const UChar PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0}; TimeUnitFormat::TimeUnitFormat(UErrorCode& status) : fNumberFormat(NULL), fPluralRules(NULL) { create(Locale::getDefault(), UTMUTFMT_FULL_STYLE, status); } TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) : fNumberFormat(NULL), fPluralRules(NULL) { create(locale, UTMUTFMT_FULL_STYLE, status); } TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) : fNumberFormat(NULL), fPluralRules(NULL) { create(locale, style, status); } TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other) : MeasureFormat(other), fNumberFormat(NULL), fPluralRules(NULL), fStyle(UTMUTFMT_FULL_STYLE) { for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; i = (TimeUnit::UTimeUnitFields)(i+1)) { fTimeUnitToCountToPatterns[i] = NULL; } *this = other; } TimeUnitFormat::~TimeUnitFormat() { delete fNumberFormat; fNumberFormat = NULL; for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; i = (TimeUnit::UTimeUnitFields)(i+1)) { deleteHash(fTimeUnitToCountToPatterns[i]); fTimeUnitToCountToPatterns[i] = NULL; } delete fPluralRules; fPluralRules = NULL; } Format* TimeUnitFormat::clone(void) const { return new TimeUnitFormat(*this); } TimeUnitFormat& TimeUnitFormat::operator=(const TimeUnitFormat& other) { if (this == &other) { return *this; } delete fNumberFormat; for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; i = (TimeUnit::UTimeUnitFields)(i+1)) { deleteHash(fTimeUnitToCountToPatterns[i]); fTimeUnitToCountToPatterns[i] = NULL; } delete fPluralRules; if (other.fNumberFormat) { fNumberFormat = (NumberFormat*)other.fNumberFormat->clone(); } else { fNumberFormat = NULL; } fLocale = other.fLocale; 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] = NULL; } } if (other.fPluralRules) { fPluralRules = (PluralRules*)other.fPluralRules->clone(); } else { fPluralRules = NULL; } fStyle = other.fStyle; return *this; } UBool TimeUnitFormat::operator==(const Format& other) const { if (typeid(*this) == typeid(other)) { TimeUnitFormat* fmt = (TimeUnitFormat*)&other; UBool ret = ( ((fNumberFormat && fmt->fNumberFormat && *fNumberFormat == *fmt->fNumberFormat) || fNumberFormat == fmt->fNumberFormat ) && fLocale == fmt->fLocale && ((fPluralRules && fmt->fPluralRules && *fPluralRules == *fmt->fPluralRules) || fPluralRules == fmt->fPluralRules) && fStyle == fmt->fStyle); if (ret) { for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; i < TimeUnit::UTIMEUNIT_FIELD_COUNT && ret; i = (TimeUnit::UTimeUnitFields)(i+1)) { ret = fTimeUnitToCountToPatterns[i]->equals(*(fmt->fTimeUnitToCountToPatterns[i])); } } return ret; } return false; } UnicodeString& TimeUnitFormat::format(const Formattable& obj, UnicodeString& toAppendTo, FieldPosition& pos, UErrorCode& status) const { if (U_FAILURE(status)) { return toAppendTo; } if (obj.getType() == Formattable::kObject) { const UObject* formatObj = obj.getObject(); const TimeUnitAmount* amount = dynamic_cast<const TimeUnitAmount*>(formatObj); if (amount != NULL){ Hashtable* countToPattern = fTimeUnitToCountToPatterns[amount->getTimeUnitField()]; double number; const Formattable& amtNumber = amount->getNumber(); if (amtNumber.getType() == Formattable::kDouble) { number = amtNumber.getDouble(); } else if (amtNumber.getType() == Formattable::kLong) { number = amtNumber.getLong(); } else { status = U_ILLEGAL_ARGUMENT_ERROR; return toAppendTo; } UnicodeString count = fPluralRules->select(number); #ifdef TMUTFMT_DEBUG char result[1000]; count.extract(0, count.length(), result, "UTF-8"); std::cout << "number: " << number << "; format plural count: " << result << "\n"; #endif MessageFormat* pattern = ((MessageFormat**)countToPattern->get(count))[fStyle]; Formattable formattable[1]; formattable[0].setDouble(number); return pattern->format(formattable, 1, toAppendTo, pos, status); } } status = U_ILLEGAL_ARGUMENT_ERROR; return toAppendTo; } void TimeUnitFormat::parseObject(const UnicodeString& source, Formattable& result, ParsePosition& pos) const { double resultNumber = -1; 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 = NULL; #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 = -1; const UHashElement* elem = NULL; while ((elem = countToPatterns->nextElement(elemPos)) != NULL){ 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 double tmpNumber = 0; if (pattern->getArgTypeCount() != 0) { // pattern with Number as beginning, such as "{0} d". // check to make sure that the timeUnit is consistent Formattable& temp = parsed[0]; if (temp.getType() == Formattable::kDouble) { tmpNumber = temp.getDouble(); } else if (temp.getType() == Formattable::kLong) { tmpNumber = temp.getLong(); } else { continue; } UnicodeString select = fPluralRules->select(tmpNumber); #ifdef TMUTFMT_DEBUG select.extract(0, select.length(), res, "UTF-8"); std::cout << "parse plural select count: " << res << "\n"; #endif if (*count != select) { 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 = 0; } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ONE, 3)) { resultNumber = 1; } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_TWO, 3)) { resultNumber = 2; } else { // should not happen. // TODO: how to handle? resultNumber = 3; } } if (longestParseDistance == 0) { pos.setIndex(oldPos); pos.setErrorIndex(0); } else { UErrorCode status = U_ZERO_ERROR; TimeUnitAmount* tmutamt = new TimeUnitAmount(resultNumber, resultTimeUnit, status); if (U_SUCCESS(status)) { result.adoptObject(tmutamt); pos.setIndex(newPos); pos.setErrorIndex(-1); } else { pos.setIndex(oldPos); pos.setErrorIndex(0); } } } void TimeUnitFormat::create(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) { if (U_FAILURE(status)) { return; } if (style < UTMUTFMT_FULL_STYLE || style >= UTMUTFMT_FORMAT_STYLE_COUNT) { status = U_ILLEGAL_ARGUMENT_ERROR; return; } fStyle = style; fLocale = locale; for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; i = (TimeUnit::UTimeUnitFields)(i+1)) { fTimeUnitToCountToPatterns[i] = NULL; } //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(0, uhash_compareUnicodeString, 6, err); StringEnumeration* keywords = fPluralRules->getKeywords(err); if (U_FAILURE(err)) { return; } UnicodeString* pluralCount; while ((pluralCount = const_cast<UnicodeString*>(keywords->snext(err))) != NULL) { 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); delete keywords; } void TimeUnitFormat::initDataMembers(UErrorCode& err){ if (U_FAILURE(err)) { return; } if (fNumberFormat == NULL) { fNumberFormat = NumberFormat::createInstance(fLocale, err); } delete fPluralRules; fPluralRules = PluralRules::forLocale(fLocale, err); for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; i = (TimeUnit::UTimeUnitFields)(i+1)) { deleteHash(fTimeUnitToCountToPatterns[i]); fTimeUnitToCountToPatterns[i] = NULL; } } 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; UResourceBundle *rb, *unitsRes; rb = ures_open(NULL, fLocale.getName(), &status); unitsRes = ures_getByKey(rb, key, NULL, &status); unitsRes = ures_getByKey(unitsRes, "duration", unitsRes, &status); if (U_FAILURE(status)) { ures_close(unitsRes); ures_close(rb); return; } int32_t size = ures_getSize(unitsRes); for ( int32_t index = 0; index < size; ++index) { // resource of one time unit UResourceBundle* oneTimeUnit = ures_getByIndex(unitsRes, index, NULL, &status); if (U_SUCCESS(status)) { const char* timeUnitName = ures_getKey(oneTimeUnit); if (timeUnitName == NULL) { ures_close(oneTimeUnit); continue; } UResourceBundle* countsToPatternRB = ures_getByKey(unitsRes, timeUnitName, NULL, &status); if (countsToPatternRB == NULL || U_FAILURE(status)) { ures_close(countsToPatternRB); ures_close(oneTimeUnit); 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 { ures_close(countsToPatternRB); ures_close(oneTimeUnit); continue; } Hashtable* countToPatterns = fTimeUnitToCountToPatterns[timeUnitField]; if (countToPatterns == NULL) { countToPatterns = initHash(err); if (U_FAILURE(err)) { ures_close(countsToPatternRB); ures_close(oneTimeUnit); delete countToPatterns; break; } } int32_t count = ures_getSize(countsToPatternRB); const char* pluralCount; for ( int32_t pluralIndex = 0; pluralIndex < count; ++pluralIndex) { // resource of count to pattern UnicodeString pattern = ures_getNextUnicodeString(countsToPatternRB, &pluralCount, &status); if (U_FAILURE(status)) { continue; } UnicodeString pluralCountUniStr(pluralCount, -1, US_INV); if (!pluralCounts.contains(&pluralCountUniStr)) { continue; } MessageFormat* messageFormat = new MessageFormat(pattern, fLocale, err); if ( U_SUCCESS(err) ) { if (fNumberFormat != NULL) { messageFormat->setFormat(0, *fNumberFormat); } MessageFormat** formatters = (MessageFormat**)countToPatterns->get(pluralCountUniStr); if (formatters == NULL) { formatters = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)); formatters[UTMUTFMT_FULL_STYLE] = NULL; formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL; countToPatterns->put(pluralCountUniStr, formatters, err); if (U_FAILURE(err)) { uprv_free(formatters); } } if (U_SUCCESS(err)) { //delete formatters[style]; formatters[style] = messageFormat; } } if (U_FAILURE(err)) { ures_close(countsToPatternRB); ures_close(oneTimeUnit); ures_close(unitsRes); ures_close(rb); delete messageFormat; delete countToPatterns; return; } } if (fTimeUnitToCountToPatterns[timeUnitField] == NULL) { fTimeUnitToCountToPatterns[timeUnitField] = countToPatterns; } ures_close(countsToPatternRB); } ures_close(oneTimeUnit); } ures_close(unitsRes); ures_close(rb); } 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. // StringEnumeration* keywords = fPluralRules->getKeywords(err); if (U_SUCCESS(err)) { const UnicodeString* pluralCount; while ((pluralCount = keywords->snext(err)) != NULL) { if ( U_SUCCESS(err) ) { 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 == NULL ) { countToPatterns = initHash(err); if (U_FAILURE(err)) { delete countToPatterns; return; } fTimeUnitToCountToPatterns[i] = countToPatterns; } MessageFormat** formatters = (MessageFormat**)countToPatterns->get(*pluralCount); if( formatters == NULL || formatters[style] == NULL ) { // look through parents const char* localeName = fLocale.getName(); CharString pluralCountChars; pluralCountChars.appendInvariantChars(*pluralCount, err); searchInLocaleChain(style, key, localeName, (TimeUnit::UTimeUnitFields)i, *pluralCount, pluralCountChars.data(), countToPatterns, err); } } } } } delete keywords; } // 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 != NULL); while ((locNameLen = uloc_getParent(parentLocale, parentLocale, ULOC_FULLNAME_CAPACITY, &status)) >= 0){ // look for pattern for srcPluralCount in locale tree UResourceBundle *rb, *unitsRes, *countsToPatternRB; rb = ures_open(NULL, parentLocale, &status); unitsRes = ures_getByKey(rb, key, NULL, &status); const char* timeUnitName = getTimeUnitName(srcTimeUnitField, status); countsToPatternRB = ures_getByKey(unitsRes, timeUnitName, NULL, &status); const UChar* pattern; int32_t ptLength; pattern = ures_getStringByKeyWithFallback(countsToPatternRB, searchPluralCount, &ptLength, &status); if (U_SUCCESS(status)) { //found MessageFormat* messageFormat = new MessageFormat(UnicodeString(TRUE, pattern, ptLength), fLocale, err); if (U_SUCCESS(err)) { if (fNumberFormat != NULL) { messageFormat->setFormat(0, *fNumberFormat); } MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount); if (formatters == NULL) { formatters = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)); formatters[UTMUTFMT_FULL_STYLE] = NULL; formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL; countToPatterns->put(srcPluralCount, formatters, err); if (U_FAILURE(err)) { uprv_free(formatters); delete messageFormat; } } if (U_SUCCESS(err)) { //delete formatters[style]; formatters[style] = messageFormat; } } else { delete messageFormat; } ures_close(countsToPatternRB); ures_close(unitsRes); ures_close(rb); return; } ures_close(countsToPatternRB); ures_close(unitsRes); ures_close(rb); 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 char pLocale[ULOC_FULLNAME_CAPACITY]; uprv_strcpy(pLocale, localeName); // Add an underscore at the tail of locale name, // so that searchInLocaleChain will check the current locale before falling back uprv_strcat(pLocale, "_"); searchInLocaleChain(style, gUnitsTag, pLocale, srcTimeUnitField, srcPluralCount, searchPluralCount, countToPatterns, err); MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount); if (formatters != NULL && formatters[style] != NULL) { 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 MessageFormat* messageFormat = NULL; const UChar *pattern = NULL; 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 != NULL) { messageFormat = new MessageFormat(UnicodeString(TRUE, pattern, -1), fLocale, err); } if (U_SUCCESS(err)) { if (fNumberFormat != NULL && messageFormat != NULL) { messageFormat->setFormat(0, *fNumberFormat); } MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount); if (formatters == NULL) { formatters = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)); formatters[UTMUTFMT_FULL_STYLE] = NULL; formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL; countToPatterns->put(srcPluralCount, formatters, err); if (U_FAILURE(err)) { uprv_free(formatters); delete messageFormat; } } if (U_SUCCESS(err)) { //delete formatters[style]; formatters[style] = messageFormat; } } else { delete messageFormat; } } 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 (U_SUCCESS(status) && fLocale != locale) { fLocale = locale; setup(status); } } void TimeUnitFormat::setNumberFormat(const NumberFormat& format, UErrorCode& status){ if (U_FAILURE(status) || (fNumberFormat && format == *fNumberFormat)) { return; } delete fNumberFormat; fNumberFormat = (NumberFormat*)format.clone(); // reset the number formatter in the fTimeUnitToCountToPatterns map for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; i = (TimeUnit::UTimeUnitFields)(i+1)) { int32_t pos = -1; const UHashElement* elem = NULL; while ((elem = fTimeUnitToCountToPatterns[i]->nextElement(pos)) != NULL){ const UHashTok keyTok = elem->value; MessageFormat** pattern = (MessageFormat**)keyTok.pointer; pattern[UTMUTFMT_FULL_STYLE]->setFormat(0, format); pattern[UTMUTFMT_ABBREVIATED_STYLE]->setFormat(0, format); } } } void TimeUnitFormat::deleteHash(Hashtable* htable) { int32_t pos = -1; const UHashElement* element = NULL; if ( htable ) { while ( (element = htable->nextElement(pos)) != NULL ) { 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 = -1; const UHashElement* element = NULL; if ( source ) { while ( (element = source->nextElement(pos)) != NULL ) { 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] = (MessageFormat*)value[0]->clone(); newVal[1] = (MessageFormat*)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 NULL; } Hashtable* hTable; if ( (hTable = new Hashtable(TRUE, status)) == NULL ) { status = U_MEMORY_ALLOCATION_ERROR; return NULL; } if ( U_FAILURE(status) ) { delete hTable; return NULL; } hTable->setValueComparator(tmutfmtHashTableValueComparator); return hTable; } const char* TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField, UErrorCode& status) { if (U_FAILURE(status)) { return NULL; } 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 NULL; } } U_NAMESPACE_END #endif