1// Copyright (C) 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
54U_NAMESPACE_BEGIN
55
56
57
58UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat)
59
60static const char gUnitsTag[] = "units";
61static const char gShortUnitsTag[] = "unitsShort";
62static const char gTimeUnitYear[] = "year";
63static const char gTimeUnitMonth[] = "month";
64static const char gTimeUnitDay[] = "day";
65static const char gTimeUnitWeek[] = "week";
66static const char gTimeUnitHour[] = "hour";
67static const char gTimeUnitMinute[] = "minute";
68static const char gTimeUnitSecond[] = "second";
69static const char gPluralCountOther[] = "other";
70
71static const UChar DEFAULT_PATTERN_FOR_SECOND[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_S, 0};
72static const UChar DEFAULT_PATTERN_FOR_MINUTE[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, LOW_I, LOW_N, 0};
73static const UChar DEFAULT_PATTERN_FOR_HOUR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_H, 0};
74static const UChar DEFAULT_PATTERN_FOR_WEEK[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_W, 0};
75static const UChar DEFAULT_PATTERN_FOR_DAY[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_D, 0};
76static const UChar DEFAULT_PATTERN_FOR_MONTH[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, 0};
77static const UChar DEFAULT_PATTERN_FOR_YEAR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_Y, 0};
78
79static const UChar PLURAL_COUNT_ZERO[] = {LOW_Z, LOW_E, LOW_R, LOW_O, 0};
80static const UChar PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0};
81static const UChar PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0};
82
83TimeUnitFormat::TimeUnitFormat(UErrorCode& status) {
84    initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE, NULL, status);
85    create(UTMUTFMT_FULL_STYLE, status);
86}
87
88
89TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) {
90    initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
91    create(UTMUTFMT_FULL_STYLE, status);
92}
93
94
95TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) {
96    switch (style) {
97    case UTMUTFMT_FULL_STYLE:
98        initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
99        break;
100    case UTMUTFMT_ABBREVIATED_STYLE:
101        initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, NULL, status);
102        break;
103    default:
104        initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
105        break;
106    }
107    create(style, status);
108}
109
110TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other)
111:   MeasureFormat(other),
112    fStyle(other.fStyle)
113{
114    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
115         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
116         i = (TimeUnit::UTimeUnitFields)(i+1)) {
117        UErrorCode status = U_ZERO_ERROR;
118        fTimeUnitToCountToPatterns[i] = initHash(status);
119        if (U_SUCCESS(status)) {
120            copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
121        } else {
122            delete fTimeUnitToCountToPatterns[i];
123            fTimeUnitToCountToPatterns[i] = NULL;
124        }
125    }
126}
127
128
129TimeUnitFormat::~TimeUnitFormat() {
130    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
131         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
132         i = (TimeUnit::UTimeUnitFields)(i+1)) {
133        deleteHash(fTimeUnitToCountToPatterns[i]);
134        fTimeUnitToCountToPatterns[i] = NULL;
135    }
136}
137
138
139Format*
140TimeUnitFormat::clone(void) const {
141    return new TimeUnitFormat(*this);
142}
143
144
145TimeUnitFormat&
146TimeUnitFormat::operator=(const TimeUnitFormat& other) {
147    if (this == &other) {
148        return *this;
149    }
150    MeasureFormat::operator=(other);
151    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
152         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
153         i = (TimeUnit::UTimeUnitFields)(i+1)) {
154        deleteHash(fTimeUnitToCountToPatterns[i]);
155        fTimeUnitToCountToPatterns[i] = NULL;
156    }
157    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
158         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
159         i = (TimeUnit::UTimeUnitFields)(i+1)) {
160        UErrorCode status = U_ZERO_ERROR;
161        fTimeUnitToCountToPatterns[i] = initHash(status);
162        if (U_SUCCESS(status)) {
163            copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
164        } else {
165            delete fTimeUnitToCountToPatterns[i];
166            fTimeUnitToCountToPatterns[i] = NULL;
167        }
168    }
169    fStyle = other.fStyle;
170    return *this;
171}
172
173void
174TimeUnitFormat::parseObject(const UnicodeString& source,
175                            Formattable& result,
176                            ParsePosition& pos) const {
177    Formattable resultNumber(0.0);
178    UBool withNumberFormat = false;
179    TimeUnit::UTimeUnitFields resultTimeUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT;
180    int32_t oldPos = pos.getIndex();
181    int32_t newPos = -1;
182    int32_t longestParseDistance = 0;
183    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    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
192         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
193         i = (TimeUnit::UTimeUnitFields)(i+1)) {
194        Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
195        int32_t elemPos = UHASH_FIRST;
196        const UHashElement* elem = NULL;
197        while ((elem = countToPatterns->nextElement(elemPos)) != NULL){
198            const UHashTok keyTok = elem->key;
199            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            const UHashTok valueTok = elem->value;
205            // the value is a pair of MessageFormat*
206            MessageFormat** patterns = (MessageFormat**)valueTok.pointer;
207            for (UTimeUnitFormatStyle style = UTMUTFMT_FULL_STYLE; style < UTMUTFMT_FORMAT_STYLE_COUNT;
208                 style = (UTimeUnitFormatStyle)(style + 1)) {
209                MessageFormat* pattern = patterns[style];
210                pos.setErrorIndex(-1);
211                pos.setIndex(oldPos);
212                // see if we can parse
213                Formattable parsed;
214                pattern->parseObject(source, parsed, pos);
215                if (pos.getErrorIndex() != -1 || pos.getIndex() == oldPos) {
216                    continue;
217                }
218    #ifdef TMUTFMT_DEBUG
219                std::cout << "parsed.getType: " << parsed.getType() << "\n";
220    #endif
221                Formattable tmpNumber(0.0);
222                if (pattern->getArgTypeCount() != 0) {
223                    Formattable& temp = parsed[0];
224                    if (temp.getType() == Formattable::kString) {
225                        UnicodeString tmpString;
226                        UErrorCode pStatus = U_ZERO_ERROR;
227                        getNumberFormat().parse(temp.getString(tmpString), tmpNumber, pStatus);
228                        if (U_FAILURE(pStatus)) {
229                            continue;
230                        }
231                    } else if (temp.isNumeric()) {
232                        tmpNumber = temp;
233                    } else {
234                        continue;
235                    }
236                }
237                int32_t parseDistance = pos.getIndex() - oldPos;
238                if (parseDistance > longestParseDistance) {
239                    if (pattern->getArgTypeCount() != 0) {
240                        resultNumber = tmpNumber;
241                        withNumberFormat = true;
242                    } else {
243                        withNumberFormat = false;
244                    }
245                    resultTimeUnit = i;
246                    newPos = pos.getIndex();
247                    longestParseDistance = parseDistance;
248                    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    if (withNumberFormat == false && longestParseDistance != 0) {
259        // set the number using plurrual count
260        if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ZERO, 4)) {
261            resultNumber = Formattable(0.0);
262        } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ONE, 3)) {
263            resultNumber = Formattable(1.0);
264        } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_TWO, 3)) {
265            resultNumber = Formattable(2.0);
266        } else {
267            // should not happen.
268            // TODO: how to handle?
269            resultNumber = Formattable(3.0);
270        }
271    }
272    if (longestParseDistance == 0) {
273        pos.setIndex(oldPos);
274        pos.setErrorIndex(0);
275    } else {
276        UErrorCode status = U_ZERO_ERROR;
277        LocalPointer<TimeUnitAmount> tmutamt(new TimeUnitAmount(resultNumber, resultTimeUnit, status), status);
278        if (U_SUCCESS(status)) {
279            result.adoptObject(tmutamt.orphan());
280            pos.setIndex(newPos);
281            pos.setErrorIndex(-1);
282        } else {
283            pos.setIndex(oldPos);
284            pos.setErrorIndex(0);
285        }
286    }
287}
288
289void
290TimeUnitFormat::create(UTimeUnitFormatStyle style, UErrorCode& status) {
291    // fTimeUnitToCountToPatterns[] must have its elements initialized to NULL first
292    // before checking for failure status.
293    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
294         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
295         i = (TimeUnit::UTimeUnitFields)(i+1)) {
296        fTimeUnitToCountToPatterns[i] = NULL;
297    }
298
299    if (U_FAILURE(status)) {
300        return;
301    }
302    if (style < UTMUTFMT_FULL_STYLE || style >= UTMUTFMT_FORMAT_STYLE_COUNT) {
303        status = U_ILLEGAL_ARGUMENT_ERROR;
304        return;
305    }
306    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    setup(status);
317}
318
319void
320TimeUnitFormat::setup(UErrorCode& err) {
321    initDataMembers(err);
322
323    UVector pluralCounts(0, uhash_compareUnicodeString, 6, err);
324    LocalPointer<StringEnumeration> keywords(getPluralRules().getKeywords(err), err);
325    if (U_FAILURE(err)) {
326        return;
327    }
328    UnicodeString* pluralCount;
329    while ((pluralCount = const_cast<UnicodeString*>(keywords->snext(err))) != NULL) {
330      pluralCounts.addElement(pluralCount, err);
331    }
332    readFromCurrentLocale(UTMUTFMT_FULL_STYLE, gUnitsTag, pluralCounts, err);
333    checkConsistency(UTMUTFMT_FULL_STYLE, gUnitsTag, err);
334    readFromCurrentLocale(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, pluralCounts, err);
335    checkConsistency(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, err);
336}
337
338
339void
340TimeUnitFormat::initDataMembers(UErrorCode& err){
341    if (U_FAILURE(err)) {
342        return;
343    }
344    for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
345         i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
346         i = (TimeUnit::UTimeUnitFields)(i+1)) {
347        deleteHash(fTimeUnitToCountToPatterns[i]);
348        fTimeUnitToCountToPatterns[i] = NULL;
349    }
350}
351
352struct TimeUnitFormatReadSink : public ResourceSink {
353    TimeUnitFormat *timeUnitFormatObj;
354    const UVector &pluralCounts;
355    UTimeUnitFormatStyle style;
356    UBool beenHere;
357
358    TimeUnitFormatReadSink(TimeUnitFormat *timeUnitFormatObj,
359            const UVector &pluralCounts, UTimeUnitFormatStyle style) :
360            timeUnitFormatObj(timeUnitFormatObj), pluralCounts(pluralCounts),
361            style(style), beenHere(FALSE){}
362
363    virtual ~TimeUnitFormatReadSink();
364
365    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        if (beenHere) {
368            return;
369        } else {
370            beenHere = TRUE;
371        }
372
373        ResourceTable units = value.getTable(errorCode);
374        if (U_FAILURE(errorCode)) { return; }
375
376        for (int32_t i = 0; units.getKeyAndValue(i, key, value); ++i) {
377            const char* timeUnitName = key;
378            if (timeUnitName == NULL) {
379                continue;
380            }
381
382            TimeUnit::UTimeUnitFields timeUnitField = TimeUnit::UTIMEUNIT_FIELD_COUNT;
383            if ( uprv_strcmp(timeUnitName, gTimeUnitYear) == 0 ) {
384                timeUnitField = TimeUnit::UTIMEUNIT_YEAR;
385            } else if ( uprv_strcmp(timeUnitName, gTimeUnitMonth) == 0 ) {
386                timeUnitField = TimeUnit::UTIMEUNIT_MONTH;
387            } else if ( uprv_strcmp(timeUnitName, gTimeUnitDay) == 0 ) {
388                timeUnitField = TimeUnit::UTIMEUNIT_DAY;
389            } else if ( uprv_strcmp(timeUnitName, gTimeUnitHour) == 0 ) {
390                timeUnitField = TimeUnit::UTIMEUNIT_HOUR;
391            } else if ( uprv_strcmp(timeUnitName, gTimeUnitMinute) == 0 ) {
392                timeUnitField = TimeUnit::UTIMEUNIT_MINUTE;
393            } else if ( uprv_strcmp(timeUnitName, gTimeUnitSecond) == 0 ) {
394                timeUnitField = TimeUnit::UTIMEUNIT_SECOND;
395            } else if ( uprv_strcmp(timeUnitName, gTimeUnitWeek) == 0 ) {
396                timeUnitField = TimeUnit::UTIMEUNIT_WEEK;
397            } else {
398                continue;
399            }
400            LocalPointer<Hashtable> localCountToPatterns;
401            Hashtable *countToPatterns =
402                timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField];
403            if (countToPatterns == NULL) {
404                localCountToPatterns.adoptInsteadAndCheckErrorCode(
405                    timeUnitFormatObj->initHash(errorCode), errorCode);
406                countToPatterns = localCountToPatterns.getAlias();
407                if (U_FAILURE(errorCode)) {
408                    return;
409                }
410            }
411
412            ResourceTable countsToPatternTable = value.getTable(errorCode);
413            if (U_FAILURE(errorCode)) {
414                continue;
415            }
416            for (int32_t j = 0; countsToPatternTable.getKeyAndValue(j, key, value); ++j) {
417                errorCode = U_ZERO_ERROR;
418                UnicodeString pattern = value.getUnicodeString(errorCode);
419                if (U_FAILURE(errorCode)) {
420                    continue;
421                }
422                UnicodeString pluralCountUniStr(key, -1, US_INV);
423                if (!pluralCounts.contains(&pluralCountUniStr)) {
424                    continue;
425                }
426                LocalPointer<MessageFormat> messageFormat(new MessageFormat(
427                    pattern, timeUnitFormatObj->getLocale(errorCode), errorCode), errorCode);
428                if (U_FAILURE(errorCode)) {
429                    return;
430                }
431                MessageFormat** formatters =
432                    (MessageFormat**)countToPatterns->get(pluralCountUniStr);
433                if (formatters == NULL) {
434                    LocalMemory<MessageFormat *> localFormatters(
435                        (MessageFormat **)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
436                    if (localFormatters.isNull()) {
437                        errorCode = U_MEMORY_ALLOCATION_ERROR;
438                        return;
439                    }
440                    localFormatters[UTMUTFMT_FULL_STYLE] = NULL;
441                    localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
442                    countToPatterns->put(pluralCountUniStr, localFormatters.getAlias(), errorCode);
443                    if (U_FAILURE(errorCode)) {
444                        return;
445                    }
446                    formatters = localFormatters.orphan();
447                }
448                formatters[style] = messageFormat.orphan();
449            }
450
451            if (timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] == NULL) {
452                timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] = localCountToPatterns.orphan();
453            }
454        }
455    }
456
457};
458
459TimeUnitFormatReadSink::~TimeUnitFormatReadSink() {}
460
461void
462TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key,
463                                      const UVector& pluralCounts, UErrorCode& err) {
464    if (U_FAILURE(err)) {
465        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    UErrorCode status = U_ZERO_ERROR;
472    LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_UNIT, getLocaleID(status), &status));
473
474    LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, NULL, &status));
475    ures_getByKey(unitsRes.getAlias(), "duration", unitsRes.getAlias(), &status);
476    if (U_FAILURE(status)) {
477        return;
478    }
479
480    TimeUnitFormatReadSink sink(this, pluralCounts, style);
481    ures_getAllItemsWithFallback(unitsRes.getAlias(), "", sink, status);
482}
483
484void
485TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style, const char* key, UErrorCode& err) {
486    if (U_FAILURE(err)) {
487        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            getPluralRules().getKeywords(err), err);
509    const UnicodeString* pluralCount;
510    while (U_SUCCESS(err) && (pluralCount = keywords->snext(err)) != NULL) {
511        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            Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
515            if ( countToPatterns == NULL ) {
516                fTimeUnitToCountToPatterns[i] = countToPatterns = initHash(err);
517                if (U_FAILURE(err)) {
518                    return;
519                }
520            }
521            MessageFormat** formatters = (MessageFormat**)countToPatterns->get(*pluralCount);
522            if( formatters == NULL || formatters[style] == NULL ) {
523                // look through parents
524                const char* localeName = getLocaleID(err);
525                CharString pluralCountChars;
526                pluralCountChars.appendInvariantChars(*pluralCount, err);
527                searchInLocaleChain(style, key, localeName,
528                                    (TimeUnit::UTimeUnitFields)i,
529                                    *pluralCount, pluralCountChars.data(),
530                                    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.
548void
549TimeUnitFormat::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    if (U_FAILURE(err)) {
556        return;
557    }
558    UErrorCode status = U_ZERO_ERROR;
559    char parentLocale[ULOC_FULLNAME_CAPACITY];
560    uprv_strcpy(parentLocale, localeName);
561    int32_t locNameLen;
562    U_ASSERT(countToPatterns != NULL);
563    while ((locNameLen = uloc_getParent(parentLocale, parentLocale,
564                                        ULOC_FULLNAME_CAPACITY, &status)) >= 0){
565        // look for pattern for srcPluralCount in locale tree
566        LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_UNIT, parentLocale, &status));
567        LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, NULL, &status));
568        const char* timeUnitName = getTimeUnitName(srcTimeUnitField, status);
569        LocalUResourceBundlePointer countsToPatternRB(ures_getByKey(unitsRes.getAlias(), timeUnitName, NULL, &status));
570        const UChar* pattern;
571        int32_t      ptLength;
572        pattern = ures_getStringByKeyWithFallback(countsToPatternRB.getAlias(), searchPluralCount, &ptLength, &status);
573        if (U_SUCCESS(status)) {
574            //found
575            LocalPointer<MessageFormat> messageFormat(
576                new MessageFormat(UnicodeString(TRUE, pattern, ptLength), getLocale(err), err), err);
577            if (U_FAILURE(err)) {
578                return;
579            }
580            MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
581            if (formatters == NULL) {
582                LocalMemory<MessageFormat *> localFormatters(
583                        (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
584                formatters = localFormatters.getAlias();
585                localFormatters[UTMUTFMT_FULL_STYLE] = NULL;
586                localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
587                countToPatterns->put(srcPluralCount, localFormatters.orphan(), err);
588                if (U_FAILURE(err)) {
589                    return;
590                }
591            }
592            //delete formatters[style];
593            formatters[style] = messageFormat.orphan();
594            return;
595        }
596        status = U_ZERO_ERROR;
597        if (locNameLen == 0) {
598            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    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        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        pLocale.append('_', err);
612        searchInLocaleChain(style, gUnitsTag, pLocale.data(), srcTimeUnitField, srcPluralCount,
613                             searchPluralCount, countToPatterns, err);
614        if (U_FAILURE(err)) {
615            return;
616        }
617        MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
618        if (formatters != NULL && formatters[style] != NULL) {
619            return;
620        }
621    }
622
623    // if not found the pattern for this plural count at all,
624    // fall-back to plural count "other"
625    if ( uprv_strcmp(searchPluralCount, gPluralCountOther) == 0 ) {
626        // set default fall back the same as the resource in root
627        LocalPointer<MessageFormat> messageFormat;
628        const UChar *pattern = NULL;
629        if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_SECOND ) {
630            pattern = DEFAULT_PATTERN_FOR_SECOND;
631        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MINUTE ) {
632            pattern = DEFAULT_PATTERN_FOR_MINUTE;
633        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_HOUR ) {
634            pattern = DEFAULT_PATTERN_FOR_HOUR;
635        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_WEEK ) {
636            pattern = DEFAULT_PATTERN_FOR_WEEK;
637        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_DAY ) {
638            pattern = DEFAULT_PATTERN_FOR_DAY;
639        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MONTH ) {
640            pattern = DEFAULT_PATTERN_FOR_MONTH;
641        } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_YEAR ) {
642            pattern = DEFAULT_PATTERN_FOR_YEAR;
643        }
644        if (pattern != NULL) {
645            messageFormat.adoptInsteadAndCheckErrorCode(
646                     new MessageFormat(UnicodeString(TRUE, pattern, -1), getLocale(err), err), err);
647        }
648        if (U_FAILURE(err)) {
649            return;
650        }
651        MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
652        if (formatters == NULL) {
653            LocalMemory<MessageFormat *> localFormatters (
654                    (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
655            if (localFormatters.isNull()) {
656                err = U_MEMORY_ALLOCATION_ERROR;
657                return;
658            }
659            formatters = localFormatters.getAlias();
660            formatters[UTMUTFMT_FULL_STYLE] = NULL;
661            formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
662            countToPatterns->put(srcPluralCount, localFormatters.orphan(), err);
663        }
664        if (U_SUCCESS(err)) {
665            //delete formatters[style];
666            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                            gPluralCountOther, countToPatterns, err);
672    }
673}
674
675void
676TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) {
677    if (setMeasureFormatLocale(locale, status)) {
678        setup(status);
679    }
680}
681
682
683void
684TimeUnitFormat::setNumberFormat(const NumberFormat& format, UErrorCode& status){
685    if (U_FAILURE(status)) {
686        return;
687    }
688    adoptNumberFormat((NumberFormat *)format.clone(), status);
689}
690
691
692void
693TimeUnitFormat::deleteHash(Hashtable* htable) {
694    int32_t pos = UHASH_FIRST;
695    const UHashElement* element = NULL;
696    if ( htable ) {
697        while ( (element = htable->nextElement(pos)) != NULL ) {
698            const UHashTok valueTok = element->value;
699            const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
700            delete value[UTMUTFMT_FULL_STYLE];
701            delete value[UTMUTFMT_ABBREVIATED_STYLE];
702            //delete[] value;
703            uprv_free(value);
704        }
705    }
706    delete htable;
707}
708
709
710void
711TimeUnitFormat::copyHash(const Hashtable* source, Hashtable* target, UErrorCode& status) {
712    if ( U_FAILURE(status) ) {
713        return;
714    }
715    int32_t pos = UHASH_FIRST;
716    const UHashElement* element = NULL;
717    if ( source ) {
718        while ( (element = source->nextElement(pos)) != NULL ) {
719            const UHashTok keyTok = element->key;
720            const UnicodeString* key = (UnicodeString*)keyTok.pointer;
721            const UHashTok valueTok = element->value;
722            const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
723            MessageFormat** newVal = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*));
724            newVal[0] = (MessageFormat*)value[0]->clone();
725            newVal[1] = (MessageFormat*)value[1]->clone();
726            target->put(UnicodeString(*key), newVal, status);
727            if ( U_FAILURE(status) ) {
728                delete newVal[0];
729                delete newVal[1];
730                uprv_free(newVal);
731                return;
732            }
733        }
734    }
735}
736
737
738U_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 */
747static UBool U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2);
748
749static UBool
750U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2) {
751    const MessageFormat** pattern1 = (const MessageFormat**)val1.pointer;
752    const MessageFormat** pattern2 = (const MessageFormat**)val2.pointer;
753    return *pattern1[0] == *pattern2[0] && *pattern1[1] == *pattern2[1];
754}
755
756U_CDECL_END
757
758Hashtable*
759TimeUnitFormat::initHash(UErrorCode& status) {
760    if ( U_FAILURE(status) ) {
761        return NULL;
762    }
763    Hashtable* hTable;
764    if ( (hTable = new Hashtable(TRUE, status)) == NULL ) {
765        status = U_MEMORY_ALLOCATION_ERROR;
766        return NULL;
767    }
768    if ( U_FAILURE(status) ) {
769        delete hTable;
770        return NULL;
771    }
772    hTable->setValueComparator(tmutfmtHashTableValueComparator);
773    return hTable;
774}
775
776
777const char*
778TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField,
779                                UErrorCode& status) {
780    if (U_FAILURE(status)) {
781        return NULL;
782    }
783    switch (unitField) {
784      case TimeUnit::UTIMEUNIT_YEAR:
785        return gTimeUnitYear;
786      case TimeUnit::UTIMEUNIT_MONTH:
787        return gTimeUnitMonth;
788      case TimeUnit::UTIMEUNIT_DAY:
789        return gTimeUnitDay;
790      case TimeUnit::UTIMEUNIT_WEEK:
791        return gTimeUnitWeek;
792      case TimeUnit::UTIMEUNIT_HOUR:
793        return gTimeUnitHour;
794      case TimeUnit::UTIMEUNIT_MINUTE:
795        return gTimeUnitMinute;
796      case TimeUnit::UTIMEUNIT_SECOND:
797        return gTimeUnitSecond;
798      default:
799        status = U_ILLEGAL_ARGUMENT_ERROR;
800        return NULL;
801    }
802}
803
804U_NAMESPACE_END
805
806#endif
807