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