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