LCOV - code coverage report
Current view: top level - intl/icu/source/i18n - tmutfmt.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 402 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 28 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13