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