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