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