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