intl/icu/source/i18n/tmutfmt.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial