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