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