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