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