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