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