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