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