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