2 *******************************************************************************
3 * Copyright (C) 2008-2011, Google, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 *******************************************************************************
8 #include <typeinfo> // for 'typeid' to work
10 #include "unicode/tmutfmt.h"
12 #if !UCONFIG_NO_FORMATTING
18 #include "unicode/msgfmt.h"
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)
40 //TODO: define in compile time
41 //#define TMUTFMT_DEBUG 1
51 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat
)
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";
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};
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};
77 TimeUnitFormat::TimeUnitFormat(UErrorCode
& status
)
78 : fNumberFormat(NULL
),
80 create(Locale::getDefault(), kFull
, status
);
84 TimeUnitFormat::TimeUnitFormat(const Locale
& locale
, UErrorCode
& status
)
85 : fNumberFormat(NULL
),
87 create(locale
, kFull
, status
);
91 TimeUnitFormat::TimeUnitFormat(const Locale
& locale
, EStyle style
, UErrorCode
& status
)
92 : fNumberFormat(NULL
),
94 create(locale
, style
, status
);
98 TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat
& other
)
99 : MeasureFormat(other
),
104 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
105 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
106 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
107 fTimeUnitToCountToPatterns
[i
] = NULL
;
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
;
128 TimeUnitFormat::clone(void) const {
129 return new TimeUnitFormat(*this);
134 TimeUnitFormat::operator=(const TimeUnitFormat
& other
) {
135 if (this == &other
) {
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
;
146 if (other
.fNumberFormat
) {
147 fNumberFormat
= (NumberFormat
*)other
.fNumberFormat
->clone();
149 fNumberFormat
= NULL
;
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
);
160 delete fTimeUnitToCountToPatterns
[i
];
161 fTimeUnitToCountToPatterns
[i
] = NULL
;
164 if (other
.fPluralRules
) {
165 fPluralRules
= (PluralRules
*)other
.fPluralRules
->clone();
169 fStyle
= other
.fStyle
;
175 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
);
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
]));
198 TimeUnitFormat::format(const Formattable
& obj
, UnicodeString
& toAppendTo
,
199 FieldPosition
& pos
, UErrorCode
& status
) const {
200 if (U_FAILURE(status
)) {
203 if (obj
.getType() == Formattable::kObject
) {
204 const UObject
* formatObj
= obj
.getObject();
205 const TimeUnitAmount
* amount
= dynamic_cast<const TimeUnitAmount
*>(formatObj
);
207 Hashtable
* countToPattern
= fTimeUnitToCountToPatterns
[amount
->getTimeUnitField()];
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();
215 status
= U_ILLEGAL_ARGUMENT_ERROR
;
218 UnicodeString count
= fPluralRules
->select(number
);
221 count
.extract(0, count
.length(), result
, "UTF-8");
222 std::cout
<< "number: " << number
<< "; format plural count: " << result
<< "\n";
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
);
230 status
= U_ILLEGAL_ARGUMENT_ERROR
;
236 TimeUnitFormat::parseObject(const UnicodeString
& source
,
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();
244 int32_t longestParseDistance
= 0;
245 UnicodeString
* countOfLongestMatch
= NULL
;
248 source
.extract(0, source
.length(), res
, "UTF-8");
249 std::cout
<< "parse source: " << res
<< "\n";
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
;
263 count
->extract(0, count
->length(), res
, "UTF-8");
264 std::cout
<< "parse plural count: " << res
<< "\n";
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
275 pattern
->parseObject(source
, parsed
, pos
);
276 if (pos
.getErrorIndex() != -1 || pos
.getIndex() == oldPos
) {
280 std::cout
<< "parsed.getType: " << parsed
.getType() << "\n";
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();
294 UnicodeString select
= fPluralRules
->select(tmpNumber
);
296 select
.extract(0, select
.length(), res
, "UTF-8");
297 std::cout
<< "parse plural select count: " << res
<< "\n";
299 if (*count
!= select
) {
303 int32_t parseDistance
= pos
.getIndex() - oldPos
;
304 if (parseDistance
> longestParseDistance
) {
305 if (pattern
->getArgTypeCount() != 0) {
306 resultNumber
= tmpNumber
;
307 withNumberFormat
= true;
309 withNumberFormat
= false;
312 newPos
= pos
.getIndex();
313 longestParseDistance
= parseDistance
;
314 countOfLongestMatch
= count
;
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.
324 if (withNumberFormat
== false && longestParseDistance
!= 0) {
325 // set the number using plurrual count
326 if ( *countOfLongestMatch
== PLURAL_COUNT_ZERO
) {
328 } else if ( *countOfLongestMatch
== PLURAL_COUNT_ONE
) {
330 } else if ( *countOfLongestMatch
== PLURAL_COUNT_TWO
) {
333 // should not happen.
334 // TODO: how to handle?
338 if (longestParseDistance
== 0) {
339 pos
.setIndex(oldPos
);
340 pos
.setErrorIndex(0);
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);
349 pos
.setIndex(oldPos
);
350 pos
.setErrorIndex(0);
357 TimeUnitFormat::create(const Locale
& locale
, EStyle style
, UErrorCode
& status
) {
358 if (U_FAILURE(status
)) {
361 if (style
< kFull
|| style
> kAbbreviate
) {
362 status
= U_ILLEGAL_ARGUMENT_ERROR
;
367 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
368 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
369 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
370 fTimeUnitToCountToPatterns
[i
] = NULL
;
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.
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
);
394 TimeUnitFormat::initDataMembers(UErrorCode
& err
){
395 if (U_FAILURE(err
)) {
398 if (fNumberFormat
== NULL
) {
399 fNumberFormat
= NumberFormat::createInstance(fLocale
, err
);
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
;
415 TimeUnitFormat::readFromCurrentLocale(EStyle style
, const char* key
, UErrorCode
& err
) {
416 if (U_FAILURE(err
)) {
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
);
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
,
437 if (U_SUCCESS(status
)) {
438 const char* timeUnitName
= ures_getKey(oneTimeUnit
);
439 if (timeUnitName
== NULL
) {
440 ures_close(oneTimeUnit
);
443 UResourceBundle
* countsToPatternRB
= ures_getByKey(unitsRes
,
446 if (countsToPatternRB
== NULL
|| U_FAILURE(status
)) {
447 ures_close(countsToPatternRB
);
448 ures_close(oneTimeUnit
);
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
;
467 ures_close(countsToPatternRB
);
468 ures_close(oneTimeUnit
);
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
;
481 int32_t count
= ures_getSize(countsToPatternRB
);
482 const UChar
* pattern
;
483 const char* pluralCount
;
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
)) {
492 MessageFormat
* messageFormat
= new MessageFormat(pattern
, fLocale
, err
);
493 if ( U_SUCCESS(err
) ) {
494 if (fNumberFormat
!= NULL
) {
495 messageFormat
->setFormat(0, *fNumberFormat
);
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
);
507 if (U_SUCCESS(err
)) {
508 //delete formatters[style];
509 formatters
[style
] = messageFormat
;
512 if (U_FAILURE(err
)) {
513 ures_close(countsToPatternRB
);
514 ures_close(oneTimeUnit
);
515 ures_close(unitsRes
);
517 delete messageFormat
;
518 delete countToPatterns
;
522 if (fTimeUnitToCountToPatterns
[timeUnitField
] == NULL
) {
523 fTimeUnitToCountToPatterns
[timeUnitField
] = countToPatterns
;
525 ures_close(countsToPatternRB
);
527 ures_close(oneTimeUnit
);
529 ures_close(unitsRes
);
535 TimeUnitFormat::checkConsistency(EStyle style
, const char* key
, UErrorCode
& err
) {
536 if (U_FAILURE(err
)) {
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".
554 // Following is consistency check to create pattern for each
555 // plural rule in each time unit using above fall-back rule.
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
;
572 fTimeUnitToCountToPatterns
[i
] = countToPatterns
;
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
);
592 // srcPluralCount is the original plural count on which the pattern is
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.
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
,
607 if (U_FAILURE(err
)) {
610 UErrorCode status
= U_ZERO_ERROR
;
611 char parentLocale
[ULOC_FULLNAME_CAPACITY
];
612 uprv_strcpy(parentLocale
, localeName
);
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
;
624 pattern
= ures_getStringByKeyWithFallback(countsToPatternRB
, searchPluralCount
, &ptLength
, &status
);
625 if (U_SUCCESS(status
)) {
627 MessageFormat
* messageFormat
= new MessageFormat(pattern
, fLocale
, err
);
628 if (U_SUCCESS(err
)) {
629 if (fNumberFormat
!= NULL
) {
630 messageFormat
->setFormat(0, *fNumberFormat
);
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
;
643 if (U_SUCCESS(err
)) {
644 //delete formatters[style];
645 formatters
[style
] = messageFormat
;
648 delete messageFormat
;
650 ures_close(countsToPatternRB
);
651 ures_close(unitsRes
);
655 ures_close(countsToPatternRB
);
656 ures_close(unitsRes
);
658 status
= U_ZERO_ERROR
;
659 if ( locNameLen
==0 ) {
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) {
668 std::cout
<< "loop into searchInLocaleChain since Short-Long-Alternative \n";
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;
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
);
703 if (U_SUCCESS(err
)) {
704 if (fNumberFormat
!= NULL
&& messageFormat
!= NULL
) {
705 messageFormat
->setFormat(0, *fNumberFormat
);
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
;
718 if (U_SUCCESS(err
)) {
719 //delete formatters[style];
720 formatters
[style
] = messageFormat
;
723 delete messageFormat
;
726 // fall back to rule "other", and search in parents
727 searchInLocaleChain(style
, key
, localeName
, srcTimeUnitField
, srcPluralCount
,
728 gPluralCountOther
, countToPatterns
, err
);
733 TimeUnitFormat::setLocale(const Locale
& locale
, UErrorCode
& status
) {
734 if (U_SUCCESS(status
) && fLocale
!= locale
) {
742 TimeUnitFormat::setNumberFormat(const NumberFormat
& format
, UErrorCode
& status
){
743 if (U_FAILURE(status
) || (fNumberFormat
&& format
== *fNumberFormat
)) {
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)) {
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
);
765 TimeUnitFormat::deleteHash(Hashtable
* htable
) {
767 const UHashElement
* element
= NULL
;
769 while ( (element
= htable
->nextElement(pos
)) != NULL
) {
770 const UHashTok valueTok
= element
->value
;
771 const MessageFormat
** value
= (const MessageFormat
**)valueTok
.pointer
;
773 delete value
[kAbbreviate
];
783 TimeUnitFormat::copyHash(const Hashtable
* source
, Hashtable
* target
, UErrorCode
& status
) {
784 if ( U_FAILURE(status
) ) {
788 const UHashElement
* element
= NULL
;
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
) ) {
813 * set hash table value comparator
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
819 static UBool U_CALLCONV
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];
831 TimeUnitFormat::initHash(UErrorCode
& status
) {
832 if ( U_FAILURE(status
) ) {
836 if ( (hTable
= new Hashtable(TRUE
, status
)) == NULL
) {
837 status
= U_MEMORY_ALLOCATION_ERROR
;
840 if ( U_FAILURE(status
) ) {
844 hTable
->setValueComparator(tmutfmtHashTableValueComparator
);
850 TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField
,
851 UErrorCode
& status
) {
852 if (U_FAILURE(status
)) {
856 case TimeUnit::UTIMEUNIT_YEAR
:
857 return gTimeUnitYear
;
858 case TimeUnit::UTIMEUNIT_MONTH
:
859 return gTimeUnitMonth
;
860 case TimeUnit::UTIMEUNIT_DAY
:
862 case TimeUnit::UTIMEUNIT_WEEK
:
863 return gTimeUnitWeek
;
864 case TimeUnit::UTIMEUNIT_HOUR
:
865 return gTimeUnitHour
;
866 case TimeUnit::UTIMEUNIT_MINUTE
:
867 return gTimeUnitMinute
;
868 case TimeUnit::UTIMEUNIT_SECOND
:
869 return gTimeUnitSecond
;
871 status
= U_ILLEGAL_ARGUMENT_ERROR
;