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