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