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