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