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