2 *******************************************************************************
3 * Copyright (C) 2008-2012, Google, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 *******************************************************************************
8 #include "utypeinfo.h" // for 'typeid' to work
10 #include "unicode/tmutfmt.h"
12 #if !UCONFIG_NO_FORMATTING
20 #include "unicode/msgfmt.h"
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)
43 //TODO: define in compile time
44 //#define TMUTFMT_DEBUG 1
54 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat
)
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";
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};
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};
80 TimeUnitFormat::TimeUnitFormat(UErrorCode
& status
)
81 : fNumberFormat(NULL
),
83 create(Locale::getDefault(), UTMUTFMT_FULL_STYLE
, status
);
87 TimeUnitFormat::TimeUnitFormat(const Locale
& locale
, UErrorCode
& status
)
88 : fNumberFormat(NULL
),
90 create(locale
, UTMUTFMT_FULL_STYLE
, status
);
94 TimeUnitFormat::TimeUnitFormat(const Locale
& locale
, UTimeUnitFormatStyle style
, UErrorCode
& status
)
95 : fNumberFormat(NULL
),
97 create(locale
, style
, status
);
101 TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat
& other
)
102 : MeasureFormat(other
),
105 fStyle(UTMUTFMT_FULL_STYLE
)
107 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
108 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
109 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
110 fTimeUnitToCountToPatterns
[i
] = NULL
;
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
;
131 TimeUnitFormat::clone(void) const {
132 return new TimeUnitFormat(*this);
137 TimeUnitFormat::operator=(const TimeUnitFormat
& other
) {
138 if (this == &other
) {
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
;
149 if (other
.fNumberFormat
) {
150 fNumberFormat
= (NumberFormat
*)other
.fNumberFormat
->clone();
152 fNumberFormat
= NULL
;
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
);
163 delete fTimeUnitToCountToPatterns
[i
];
164 fTimeUnitToCountToPatterns
[i
] = NULL
;
167 if (other
.fPluralRules
) {
168 fPluralRules
= (PluralRules
*)other
.fPluralRules
->clone();
172 fStyle
= other
.fStyle
;
178 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
);
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
]));
201 TimeUnitFormat::format(const Formattable
& obj
, UnicodeString
& toAppendTo
,
202 FieldPosition
& pos
, UErrorCode
& status
) const {
203 if (U_FAILURE(status
)) {
206 if (obj
.getType() == Formattable::kObject
) {
207 const UObject
* formatObj
= obj
.getObject();
208 const TimeUnitAmount
* amount
= dynamic_cast<const TimeUnitAmount
*>(formatObj
);
210 Hashtable
* countToPattern
= fTimeUnitToCountToPatterns
[amount
->getTimeUnitField()];
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();
218 status
= U_ILLEGAL_ARGUMENT_ERROR
;
221 UnicodeString count
= fPluralRules
->select(number
);
224 count
.extract(0, count
.length(), result
, "UTF-8");
225 std::cout
<< "number: " << number
<< "; format plural count: " << result
<< "\n";
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
);
233 status
= U_ILLEGAL_ARGUMENT_ERROR
;
239 TimeUnitFormat::parseObject(const UnicodeString
& source
,
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();
247 int32_t longestParseDistance
= 0;
248 UnicodeString
* countOfLongestMatch
= NULL
;
251 source
.extract(0, source
.length(), res
, "UTF-8");
252 std::cout
<< "parse source: " << res
<< "\n";
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
;
266 count
->extract(0, count
->length(), res
, "UTF-8");
267 std::cout
<< "parse plural count: " << res
<< "\n";
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
279 pattern
->parseObject(source
, parsed
, pos
);
280 if (pos
.getErrorIndex() != -1 || pos
.getIndex() == oldPos
) {
284 std::cout
<< "parsed.getType: " << parsed
.getType() << "\n";
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();
298 UnicodeString select
= fPluralRules
->select(tmpNumber
);
300 select
.extract(0, select
.length(), res
, "UTF-8");
301 std::cout
<< "parse plural select count: " << res
<< "\n";
303 if (*count
!= select
) {
307 int32_t parseDistance
= pos
.getIndex() - oldPos
;
308 if (parseDistance
> longestParseDistance
) {
309 if (pattern
->getArgTypeCount() != 0) {
310 resultNumber
= tmpNumber
;
311 withNumberFormat
= true;
313 withNumberFormat
= false;
316 newPos
= pos
.getIndex();
317 longestParseDistance
= parseDistance
;
318 countOfLongestMatch
= count
;
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.
328 if (withNumberFormat
== false && longestParseDistance
!= 0) {
329 // set the number using plurrual count
330 if (0 == countOfLongestMatch
->compare(PLURAL_COUNT_ZERO
, 4)) {
332 } else if (0 == countOfLongestMatch
->compare(PLURAL_COUNT_ONE
, 3)) {
334 } else if (0 == countOfLongestMatch
->compare(PLURAL_COUNT_TWO
, 3)) {
337 // should not happen.
338 // TODO: how to handle?
342 if (longestParseDistance
== 0) {
343 pos
.setIndex(oldPos
);
344 pos
.setErrorIndex(0);
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);
353 pos
.setIndex(oldPos
);
354 pos
.setErrorIndex(0);
361 TimeUnitFormat::create(const Locale
& locale
, UTimeUnitFormatStyle style
, UErrorCode
& status
) {
362 if (U_FAILURE(status
)) {
365 if (style
< UTMUTFMT_FULL_STYLE
|| style
> UTMUTFMT_ABBREVIATED_STYLE
) {
366 status
= U_ILLEGAL_ARGUMENT_ERROR
;
371 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
372 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
373 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
374 fTimeUnitToCountToPatterns
[i
] = NULL
;
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.
388 TimeUnitFormat::setup(UErrorCode
& err
) {
389 initDataMembers(err
);
391 UVector
pluralCounts(0, uhash_compareUnicodeString
, 6, err
);
392 StringEnumeration
* keywords
= fPluralRules
->getKeywords(err
);
393 if (U_FAILURE(err
)) {
396 UnicodeString
* pluralCount
;
397 while ((pluralCount
= const_cast<UnicodeString
*>(keywords
->snext(err
))) != NULL
) {
398 pluralCounts
.addElement(pluralCount
, err
);
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
);
409 TimeUnitFormat::initDataMembers(UErrorCode
& err
){
410 if (U_FAILURE(err
)) {
413 if (fNumberFormat
== NULL
) {
414 fNumberFormat
= NumberFormat::createInstance(fLocale
, err
);
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
;
430 TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style
, const char* key
,
431 const UVector
& pluralCounts
, UErrorCode
& err
) {
432 if (U_FAILURE(err
)) {
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
);
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
,
453 if (U_SUCCESS(status
)) {
454 const char* timeUnitName
= ures_getKey(oneTimeUnit
);
455 if (timeUnitName
== NULL
) {
456 ures_close(oneTimeUnit
);
459 UResourceBundle
* countsToPatternRB
= ures_getByKey(unitsRes
,
462 if (countsToPatternRB
== NULL
|| U_FAILURE(status
)) {
463 ures_close(countsToPatternRB
);
464 ures_close(oneTimeUnit
);
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
;
483 ures_close(countsToPatternRB
);
484 ures_close(oneTimeUnit
);
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
;
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
)) {
506 UnicodeString
pluralCountUniStr(pluralCount
, -1, US_INV
);
507 if (!pluralCounts
.contains(&pluralCountUniStr
)) {
510 MessageFormat
* messageFormat
= new MessageFormat(pattern
, fLocale
, err
);
511 if ( U_SUCCESS(err
) ) {
512 if (fNumberFormat
!= NULL
) {
513 messageFormat
->setFormat(0, *fNumberFormat
);
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
);
525 if (U_SUCCESS(err
)) {
526 //delete formatters[style];
527 formatters
[style
] = messageFormat
;
530 if (U_FAILURE(err
)) {
531 ures_close(countsToPatternRB
);
532 ures_close(oneTimeUnit
);
533 ures_close(unitsRes
);
535 delete messageFormat
;
536 delete countToPatterns
;
540 if (fTimeUnitToCountToPatterns
[timeUnitField
] == NULL
) {
541 fTimeUnitToCountToPatterns
[timeUnitField
] = countToPatterns
;
543 ures_close(countsToPatternRB
);
545 ures_close(oneTimeUnit
);
547 ures_close(unitsRes
);
553 TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style
, const char* key
, UErrorCode
& err
) {
554 if (U_FAILURE(err
)) {
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".
572 // Following is consistency check to create pattern for each
573 // plural rule in each time unit using above fall-back rule.
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
;
590 fTimeUnitToCountToPatterns
[i
] = countToPatterns
;
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
);
612 // srcPluralCount is the original plural count on which the pattern is
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.
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
,
627 if (U_FAILURE(err
)) {
630 UErrorCode status
= U_ZERO_ERROR
;
631 char parentLocale
[ULOC_FULLNAME_CAPACITY
];
632 uprv_strcpy(parentLocale
, localeName
);
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
;
645 pattern
= ures_getStringByKeyWithFallback(countsToPatternRB
, searchPluralCount
, &ptLength
, &status
);
646 if (U_SUCCESS(status
)) {
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
);
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
;
664 if (U_SUCCESS(err
)) {
665 //delete formatters[style];
666 formatters
[style
] = messageFormat
;
669 delete messageFormat
;
671 ures_close(countsToPatternRB
);
672 ures_close(unitsRes
);
676 ures_close(countsToPatternRB
);
677 ures_close(unitsRes
);
679 status
= U_ZERO_ERROR
;
680 if ( locNameLen
==0 ) {
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) {
689 std::cout
<< "loop into searchInLocaleChain since Short-Long-Alternative \n";
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
) {
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
;
725 if (pattern
!= NULL
) {
726 messageFormat
= new MessageFormat(UnicodeString(TRUE
, pattern
, -1), fLocale
, err
);
728 if (U_SUCCESS(err
)) {
729 if (fNumberFormat
!= NULL
&& messageFormat
!= NULL
) {
730 messageFormat
->setFormat(0, *fNumberFormat
);
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
;
743 if (U_SUCCESS(err
)) {
744 //delete formatters[style];
745 formatters
[style
] = messageFormat
;
748 delete messageFormat
;
751 // fall back to rule "other", and search in parents
752 searchInLocaleChain(style
, key
, localeName
, srcTimeUnitField
, srcPluralCount
,
753 gPluralCountOther
, countToPatterns
, err
);
758 TimeUnitFormat::setLocale(const Locale
& locale
, UErrorCode
& status
) {
759 if (U_SUCCESS(status
) && fLocale
!= locale
) {
767 TimeUnitFormat::setNumberFormat(const NumberFormat
& format
, UErrorCode
& status
){
768 if (U_FAILURE(status
) || (fNumberFormat
&& format
== *fNumberFormat
)) {
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)) {
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
;
783 pattern
[UTMUTFMT_FULL_STYLE
]->setFormat(0, format
);
784 pattern
[UTMUTFMT_ABBREVIATED_STYLE
]->setFormat(0, format
);
791 TimeUnitFormat::deleteHash(Hashtable
* htable
) {
793 const UHashElement
* element
= NULL
;
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
];
809 TimeUnitFormat::copyHash(const Hashtable
* source
, Hashtable
* target
, UErrorCode
& status
) {
810 if ( U_FAILURE(status
) ) {
814 const UHashElement
* element
= NULL
;
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
) ) {
839 * set hash table value comparator
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
845 static UBool U_CALLCONV
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];
857 TimeUnitFormat::initHash(UErrorCode
& status
) {
858 if ( U_FAILURE(status
) ) {
862 if ( (hTable
= new Hashtable(TRUE
, status
)) == NULL
) {
863 status
= U_MEMORY_ALLOCATION_ERROR
;
866 if ( U_FAILURE(status
) ) {
870 hTable
->setValueComparator(tmutfmtHashTableValueComparator
);
876 TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField
,
877 UErrorCode
& status
) {
878 if (U_FAILURE(status
)) {
882 case TimeUnit::UTIMEUNIT_YEAR
:
883 return gTimeUnitYear
;
884 case TimeUnit::UTIMEUNIT_MONTH
:
885 return gTimeUnitMonth
;
886 case TimeUnit::UTIMEUNIT_DAY
:
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
;
897 status
= U_ILLEGAL_ARGUMENT_ERROR
;