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