| /* |
| ******************************************************************************* |
| * Copyright (C) 2008-2012, 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_ABBREVIATED_STYLE) { |
| 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); |
| 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 |