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