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