807 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			807 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C++
		
	
	
	
| // © 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  ((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) {
 | |
|     initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE, NULL, status);
 | |
|     create(UTMUTFMT_FULL_STYLE, status);
 | |
| }
 | |
| 
 | |
| 
 | |
| TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) {
 | |
|     initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, 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, NULL, status);
 | |
|         break;
 | |
|     case UTMUTFMT_ABBREVIATED_STYLE:
 | |
|         initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, NULL, status);
 | |
|         break;
 | |
|     default:
 | |
|         initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, 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] = NULL;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| TimeUnitFormat::~TimeUnitFormat() {
 | |
|     for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
 | |
|          i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
 | |
|          i = (TimeUnit::UTimeUnitFields)(i+1)) {
 | |
|         deleteHash(fTimeUnitToCountToPatterns[i]);
 | |
|         fTimeUnitToCountToPatterns[i] = NULL;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| 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] = NULL;
 | |
|     }
 | |
|     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;
 | |
|         }
 | |
|     }
 | |
|     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 = 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 = UHASH_FIRST;
 | |
|         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
 | |
|                 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 NULL 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] = NULL;
 | |
|     }
 | |
| 
 | |
|     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(0, 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))) != NULL) {
 | |
|       pluralCounts.addElementX(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] = NULL;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 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 == NULL) {
 | |
|                 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 == NULL) {
 | |
|                 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 == NULL) {
 | |
|                     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] = NULL;
 | |
|                     localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
 | |
|                     countToPatterns->put(pluralCountUniStr, localFormatters.getAlias(), errorCode);
 | |
|                     if (U_FAILURE(errorCode)) {
 | |
|                         return;
 | |
|                     }
 | |
|                     formatters = localFormatters.orphan();
 | |
|                 }
 | |
|                 formatters[style] = messageFormat.orphan();
 | |
|             }
 | |
| 
 | |
|             if (timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] == NULL) {
 | |
|                 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, NULL, &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)) != NULL) {
 | |
|         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 ) {
 | |
|                 fTimeUnitToCountToPatterns[i] = countToPatterns = initHash(err);
 | |
|                 if (U_FAILURE(err)) {
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|             MessageFormat** formatters = (MessageFormat**)countToPatterns->get(*pluralCount);
 | |
|             if( formatters == NULL || formatters[style] == NULL ) {
 | |
|                 // 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 != NULL);
 | |
|     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, NULL, &status));
 | |
|         const char* timeUnitName = getTimeUnitName(srcTimeUnitField, status);
 | |
|         LocalUResourceBundlePointer countsToPatternRB(ures_getByKey(unitsRes.getAlias(), timeUnitName, NULL, &status));
 | |
|         const UChar* 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 == NULL) {
 | |
|                 LocalMemory<MessageFormat *> localFormatters(
 | |
|                         (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
 | |
|                 formatters = localFormatters.getAlias();
 | |
|                 localFormatters[UTMUTFMT_FULL_STYLE] = NULL;
 | |
|                 localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
 | |
|                 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 != 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
 | |
|         LocalPointer<MessageFormat> messageFormat;
 | |
|         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.adoptInsteadAndCheckErrorCode(
 | |
|                      new MessageFormat(UnicodeString(TRUE, pattern, -1), getLocale(err), err), err);
 | |
|         }
 | |
|         if (U_FAILURE(err)) {
 | |
|             return;
 | |
|         }
 | |
|         MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
 | |
|         if (formatters == NULL) {
 | |
|             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] = NULL;
 | |
|             formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
 | |
|             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 = 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 = UHASH_FIRST;
 | |
|     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] = 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 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
 |