2 *******************************************************************************
3 * Copyright (C) 2008-2015, Google, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 *******************************************************************************
8 #include "unicode/tmutfmt.h"
10 #if !UCONFIG_NO_FORMATTING
12 #include "unicode/decimfmt.h"
13 #include "unicode/localpointer.h"
14 #include "plurrule_impl.h"
22 #include "unicode/msgfmt.h"
25 #define LEFT_CURLY_BRACKET ((UChar)0x007B)
26 #define RIGHT_CURLY_BRACKET ((UChar)0x007D)
27 #define SPACE ((UChar)0x0020)
28 #define DIGIT_ZERO ((UChar)0x0030)
29 #define LOW_S ((UChar)0x0073)
30 #define LOW_M ((UChar)0x006D)
31 #define LOW_I ((UChar)0x0069)
32 #define LOW_N ((UChar)0x006E)
33 #define LOW_H ((UChar)0x0068)
34 #define LOW_W ((UChar)0x0077)
35 #define LOW_D ((UChar)0x0064)
36 #define LOW_Y ((UChar)0x0079)
37 #define LOW_Z ((UChar)0x007A)
38 #define LOW_E ((UChar)0x0065)
39 #define LOW_R ((UChar)0x0072)
40 #define LOW_O ((UChar)0x006F)
41 #define LOW_N ((UChar)0x006E)
42 #define LOW_T ((UChar)0x0074)
45 //TODO: define in compile time
46 //#define TMUTFMT_DEBUG 1
56 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat
)
58 static const char gUnitsTag
[] = "units";
59 static const char gShortUnitsTag
[] = "unitsShort";
60 static const char gTimeUnitYear
[] = "year";
61 static const char gTimeUnitMonth
[] = "month";
62 static const char gTimeUnitDay
[] = "day";
63 static const char gTimeUnitWeek
[] = "week";
64 static const char gTimeUnitHour
[] = "hour";
65 static const char gTimeUnitMinute
[] = "minute";
66 static const char gTimeUnitSecond
[] = "second";
67 static const char gPluralCountOther
[] = "other";
69 static const UChar DEFAULT_PATTERN_FOR_SECOND
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
, SPACE
, LOW_S
, 0};
70 static const UChar DEFAULT_PATTERN_FOR_MINUTE
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
, SPACE
, LOW_M
, LOW_I
, LOW_N
, 0};
71 static const UChar DEFAULT_PATTERN_FOR_HOUR
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
, SPACE
, LOW_H
, 0};
72 static const UChar DEFAULT_PATTERN_FOR_WEEK
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
, SPACE
, LOW_W
, 0};
73 static const UChar DEFAULT_PATTERN_FOR_DAY
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
, SPACE
, LOW_D
, 0};
74 static const UChar DEFAULT_PATTERN_FOR_MONTH
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
, SPACE
, LOW_M
, 0};
75 static const UChar DEFAULT_PATTERN_FOR_YEAR
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
, SPACE
, LOW_Y
, 0};
77 static const UChar PLURAL_COUNT_ZERO
[] = {LOW_Z
, LOW_E
, LOW_R
, LOW_O
, 0};
78 static const UChar PLURAL_COUNT_ONE
[] = {LOW_O
, LOW_N
, LOW_E
, 0};
79 static const UChar PLURAL_COUNT_TWO
[] = {LOW_T
, LOW_W
, LOW_O
, 0};
81 TimeUnitFormat::TimeUnitFormat(UErrorCode
& status
) {
82 initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE
, NULL
, status
);
83 create(UTMUTFMT_FULL_STYLE
, status
);
87 TimeUnitFormat::TimeUnitFormat(const Locale
& locale
, UErrorCode
& status
) {
88 initMeasureFormat(locale
, UMEASFMT_WIDTH_WIDE
, NULL
, status
);
89 create(UTMUTFMT_FULL_STYLE
, status
);
93 TimeUnitFormat::TimeUnitFormat(const Locale
& locale
, UTimeUnitFormatStyle style
, UErrorCode
& status
) {
95 case UTMUTFMT_FULL_STYLE
:
96 initMeasureFormat(locale
, UMEASFMT_WIDTH_WIDE
, NULL
, status
);
98 case UTMUTFMT_ABBREVIATED_STYLE
:
99 initMeasureFormat(locale
, UMEASFMT_WIDTH_SHORT
, NULL
, status
);
102 initMeasureFormat(locale
, UMEASFMT_WIDTH_WIDE
, NULL
, status
);
105 create(style
, status
);
108 TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat
& other
)
109 : MeasureFormat(other
),
112 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
113 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
114 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
115 UErrorCode status
= U_ZERO_ERROR
;
116 fTimeUnitToCountToPatterns
[i
] = initHash(status
);
117 if (U_SUCCESS(status
)) {
118 copyHash(other
.fTimeUnitToCountToPatterns
[i
], fTimeUnitToCountToPatterns
[i
], status
);
120 delete fTimeUnitToCountToPatterns
[i
];
121 fTimeUnitToCountToPatterns
[i
] = NULL
;
127 TimeUnitFormat::~TimeUnitFormat() {
128 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
129 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
130 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
131 deleteHash(fTimeUnitToCountToPatterns
[i
]);
132 fTimeUnitToCountToPatterns
[i
] = NULL
;
138 TimeUnitFormat::clone(void) const {
139 return new TimeUnitFormat(*this);
144 TimeUnitFormat::operator=(const TimeUnitFormat
& other
) {
145 if (this == &other
) {
148 MeasureFormat::operator=(other
);
149 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
150 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
151 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
152 deleteHash(fTimeUnitToCountToPatterns
[i
]);
153 fTimeUnitToCountToPatterns
[i
] = NULL
;
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 fStyle
= other
.fStyle
;
172 TimeUnitFormat::parseObject(const UnicodeString
& source
,
174 ParsePosition
& pos
) const {
175 Formattable
resultNumber(0.0);
176 UBool withNumberFormat
= false;
177 TimeUnit::UTimeUnitFields resultTimeUnit
= TimeUnit::UTIMEUNIT_FIELD_COUNT
;
178 int32_t oldPos
= pos
.getIndex();
180 int32_t longestParseDistance
= 0;
181 UnicodeString
* countOfLongestMatch
= NULL
;
184 source
.extract(0, source
.length(), res
, "UTF-8");
185 std::cout
<< "parse source: " << res
<< "\n";
187 // parse by iterating through all available patterns
188 // and looking for the longest match.
189 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
190 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
191 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
192 Hashtable
* countToPatterns
= fTimeUnitToCountToPatterns
[i
];
193 int32_t elemPos
= UHASH_FIRST
;
194 const UHashElement
* elem
= NULL
;
195 while ((elem
= countToPatterns
->nextElement(elemPos
)) != NULL
){
196 const UHashTok keyTok
= elem
->key
;
197 UnicodeString
* count
= (UnicodeString
*)keyTok
.pointer
;
199 count
->extract(0, count
->length(), res
, "UTF-8");
200 std::cout
<< "parse plural count: " << res
<< "\n";
202 const UHashTok valueTok
= elem
->value
;
203 // the value is a pair of MessageFormat*
204 MessageFormat
** patterns
= (MessageFormat
**)valueTok
.pointer
;
205 for (UTimeUnitFormatStyle style
= UTMUTFMT_FULL_STYLE
; style
< UTMUTFMT_FORMAT_STYLE_COUNT
;
206 style
= (UTimeUnitFormatStyle
)(style
+ 1)) {
207 MessageFormat
* pattern
= patterns
[style
];
208 pos
.setErrorIndex(-1);
209 pos
.setIndex(oldPos
);
210 // see if we can parse
212 pattern
->parseObject(source
, parsed
, pos
);
213 if (pos
.getErrorIndex() != -1 || pos
.getIndex() == oldPos
) {
217 std::cout
<< "parsed.getType: " << parsed
.getType() << "\n";
219 Formattable
tmpNumber(0.0);
220 if (pattern
->getArgTypeCount() != 0) {
221 Formattable
& temp
= parsed
[0];
222 if (temp
.getType() == Formattable::kString
) {
223 UnicodeString tmpString
;
224 UErrorCode pStatus
= U_ZERO_ERROR
;
225 getNumberFormat().parse(temp
.getString(tmpString
), tmpNumber
, pStatus
);
226 if (U_FAILURE(pStatus
)) {
229 } else if (temp
.isNumeric()) {
235 int32_t parseDistance
= pos
.getIndex() - oldPos
;
236 if (parseDistance
> longestParseDistance
) {
237 if (pattern
->getArgTypeCount() != 0) {
238 resultNumber
= tmpNumber
;
239 withNumberFormat
= true;
241 withNumberFormat
= false;
244 newPos
= pos
.getIndex();
245 longestParseDistance
= parseDistance
;
246 countOfLongestMatch
= count
;
251 /* After find the longest match, parse the number.
252 * Result number could be null for the pattern without number pattern.
253 * such as unit pattern in Arabic.
254 * When result number is null, use plural rule to set the number.
256 if (withNumberFormat
== false && longestParseDistance
!= 0) {
257 // set the number using plurrual count
258 if (0 == countOfLongestMatch
->compare(PLURAL_COUNT_ZERO
, 4)) {
259 resultNumber
= Formattable(0.0);
260 } else if (0 == countOfLongestMatch
->compare(PLURAL_COUNT_ONE
, 3)) {
261 resultNumber
= Formattable(1.0);
262 } else if (0 == countOfLongestMatch
->compare(PLURAL_COUNT_TWO
, 3)) {
263 resultNumber
= Formattable(2.0);
265 // should not happen.
266 // TODO: how to handle?
267 resultNumber
= Formattable(3.0);
270 if (longestParseDistance
== 0) {
271 pos
.setIndex(oldPos
);
272 pos
.setErrorIndex(0);
274 UErrorCode status
= U_ZERO_ERROR
;
275 LocalPointer
<TimeUnitAmount
> tmutamt(new TimeUnitAmount(resultNumber
, resultTimeUnit
, status
), status
);
276 if (U_SUCCESS(status
)) {
277 result
.adoptObject(tmutamt
.orphan());
278 pos
.setIndex(newPos
);
279 pos
.setErrorIndex(-1);
281 pos
.setIndex(oldPos
);
282 pos
.setErrorIndex(0);
288 TimeUnitFormat::create(UTimeUnitFormatStyle style
, UErrorCode
& status
) {
289 // fTimeUnitToCountToPatterns[] must have its elements initialized to NULL first
290 // before checking for failure status.
291 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
292 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
293 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
294 fTimeUnitToCountToPatterns
[i
] = NULL
;
297 if (U_FAILURE(status
)) {
300 if (style
< UTMUTFMT_FULL_STYLE
|| style
>= UTMUTFMT_FORMAT_STYLE_COUNT
) {
301 status
= U_ILLEGAL_ARGUMENT_ERROR
;
306 //TODO: format() and parseObj() are const member functions,
307 //so, can not do lazy initialization in C++.
308 //setup has to be done in constructors.
309 //and here, the behavior is not consistent with Java.
310 //In Java, create an empty instance does not setup locale as
311 //default locale. If it followed by setNumberFormat(),
312 //in format(), the locale will set up as the locale in fNumberFormat.
313 //But in C++, this sets the locale as the default locale.
318 TimeUnitFormat::setup(UErrorCode
& err
) {
319 initDataMembers(err
);
321 UVector
pluralCounts(0, uhash_compareUnicodeString
, 6, err
);
322 LocalPointer
<StringEnumeration
> keywords(getPluralRules().getKeywords(err
), err
);
323 if (U_FAILURE(err
)) {
326 UnicodeString
* pluralCount
;
327 while ((pluralCount
= const_cast<UnicodeString
*>(keywords
->snext(err
))) != NULL
) {
328 pluralCounts
.addElement(pluralCount
, err
);
330 readFromCurrentLocale(UTMUTFMT_FULL_STYLE
, gUnitsTag
, pluralCounts
, err
);
331 checkConsistency(UTMUTFMT_FULL_STYLE
, gUnitsTag
, err
);
332 readFromCurrentLocale(UTMUTFMT_ABBREVIATED_STYLE
, gShortUnitsTag
, pluralCounts
, err
);
333 checkConsistency(UTMUTFMT_ABBREVIATED_STYLE
, gShortUnitsTag
, err
);
338 TimeUnitFormat::initDataMembers(UErrorCode
& err
){
339 if (U_FAILURE(err
)) {
342 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
343 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
344 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
345 deleteHash(fTimeUnitToCountToPatterns
[i
]);
346 fTimeUnitToCountToPatterns
[i
] = NULL
;
351 TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style
, const char* key
,
352 const UVector
& pluralCounts
, UErrorCode
& err
) {
353 if (U_FAILURE(err
)) {
356 // fill timeUnitToCountToPatterns from resource file
357 // err is used to indicate wrong status except missing resource.
358 // status is an error code used in resource lookup.
359 // status does not affect "err".
360 UErrorCode status
= U_ZERO_ERROR
;
361 LocalUResourceBundlePointer
rb(ures_open(U_ICUDATA_UNIT
, getLocaleID(status
), &status
));
363 LocalUResourceBundlePointer
unitsRes(ures_getByKey(rb
.getAlias(), key
, NULL
, &status
));
364 ures_getByKey(unitsRes
.getAlias(), "duration", unitsRes
.getAlias(), &status
);
365 if (U_FAILURE(status
)) {
368 int32_t size
= ures_getSize(unitsRes
.getAlias());
369 for ( int32_t index
= 0; index
< size
; ++index
) {
370 status
= U_ZERO_ERROR
;
371 // resource of one time unit
372 LocalUResourceBundlePointer
oneTimeUnit(
373 ures_getByIndex(unitsRes
.getAlias(), index
, NULL
, &status
));
374 if (U_FAILURE(status
)) {
377 const char* timeUnitName
= ures_getKey(oneTimeUnit
.getAlias());
378 if (timeUnitName
== NULL
) {
381 LocalUResourceBundlePointer
countsToPatternRB(
382 ures_getByKey(unitsRes
.getAlias(), timeUnitName
, NULL
, &status
));
383 if (countsToPatternRB
.isNull() || U_FAILURE(status
)) {
386 TimeUnit::UTimeUnitFields timeUnitField
= TimeUnit::UTIMEUNIT_FIELD_COUNT
;
387 if ( uprv_strcmp(timeUnitName
, gTimeUnitYear
) == 0 ) {
388 timeUnitField
= TimeUnit::UTIMEUNIT_YEAR
;
389 } else if ( uprv_strcmp(timeUnitName
, gTimeUnitMonth
) == 0 ) {
390 timeUnitField
= TimeUnit::UTIMEUNIT_MONTH
;
391 } else if ( uprv_strcmp(timeUnitName
, gTimeUnitDay
) == 0 ) {
392 timeUnitField
= TimeUnit::UTIMEUNIT_DAY
;
393 } else if ( uprv_strcmp(timeUnitName
, gTimeUnitHour
) == 0 ) {
394 timeUnitField
= TimeUnit::UTIMEUNIT_HOUR
;
395 } else if ( uprv_strcmp(timeUnitName
, gTimeUnitMinute
) == 0 ) {
396 timeUnitField
= TimeUnit::UTIMEUNIT_MINUTE
;
397 } else if ( uprv_strcmp(timeUnitName
, gTimeUnitSecond
) == 0 ) {
398 timeUnitField
= TimeUnit::UTIMEUNIT_SECOND
;
399 } else if ( uprv_strcmp(timeUnitName
, gTimeUnitWeek
) == 0 ) {
400 timeUnitField
= TimeUnit::UTIMEUNIT_WEEK
;
404 LocalPointer
<Hashtable
> localCountToPatterns
;
405 Hashtable
*countToPatterns
= fTimeUnitToCountToPatterns
[timeUnitField
];
406 if (countToPatterns
== NULL
) {
407 localCountToPatterns
.adoptInsteadAndCheckErrorCode(initHash(err
), err
);
408 countToPatterns
= localCountToPatterns
.getAlias();
409 if (U_FAILURE(err
)) {
413 int32_t count
= ures_getSize(countsToPatternRB
.getAlias());
414 const char* pluralCount
;
415 for ( int32_t pluralIndex
= 0; pluralIndex
< count
; ++pluralIndex
) {
416 // resource of count to pattern
417 status
= U_ZERO_ERROR
;
418 UnicodeString pattern
=
419 ures_getNextUnicodeString(countsToPatternRB
.getAlias(), &pluralCount
, &status
);
420 if (U_FAILURE(status
)) {
423 UnicodeString
pluralCountUniStr(pluralCount
, -1, US_INV
);
424 if (!pluralCounts
.contains(&pluralCountUniStr
)) {
427 LocalPointer
<MessageFormat
> messageFormat(new MessageFormat(pattern
, getLocale(err
), err
), err
);
428 if (U_FAILURE(err
)) {
431 MessageFormat
** formatters
= (MessageFormat
**)countToPatterns
->get(pluralCountUniStr
);
432 if (formatters
== NULL
) {
433 LocalMemory
<MessageFormat
*> localFormatters(
434 (MessageFormat
**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT
*sizeof(MessageFormat
*)));
435 if (localFormatters
.isNull()) {
436 err
= U_MEMORY_ALLOCATION_ERROR
;
439 localFormatters
[UTMUTFMT_FULL_STYLE
] = NULL
;
440 localFormatters
[UTMUTFMT_ABBREVIATED_STYLE
] = NULL
;
441 countToPatterns
->put(pluralCountUniStr
, localFormatters
.getAlias(), err
);
442 if (U_FAILURE(err
)) {
445 formatters
= localFormatters
.orphan();
447 //delete formatters[style];
448 formatters
[style
] = messageFormat
.orphan();
450 if (fTimeUnitToCountToPatterns
[timeUnitField
] == NULL
) {
451 fTimeUnitToCountToPatterns
[timeUnitField
] = localCountToPatterns
.orphan();
458 TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style
, const char* key
, UErrorCode
& err
) {
459 if (U_FAILURE(err
)) {
462 // there should be patterns for each plural rule in each time unit.
463 // For each time unit,
464 // for each plural rule, following is unit pattern fall-back rule:
465 // ( for example: "one" hour )
466 // look for its unit pattern in its locale tree.
467 // if pattern is not found in its own locale, such as de_DE,
468 // look for the pattern in its parent, such as de,
469 // keep looking till found or till root.
470 // if the pattern is not found in root either,
471 // fallback to plural count "other",
472 // look for the pattern of "other" in the locale tree:
473 // "de_DE" to "de" to "root".
474 // If not found, fall back to value of
475 // static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h".
477 // Following is consistency check to create pattern for each
478 // plural rule in each time unit using above fall-back rule.
480 LocalPointer
<StringEnumeration
> keywords(
481 getPluralRules().getKeywords(err
), err
);
482 const UnicodeString
* pluralCount
;
483 while (U_SUCCESS(err
) && (pluralCount
= keywords
->snext(err
)) != NULL
) {
484 for (int32_t i
= 0; i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
; ++i
) {
485 // for each time unit,
486 // get all the patterns for each plural rule in this locale.
487 Hashtable
* countToPatterns
= fTimeUnitToCountToPatterns
[i
];
488 if ( countToPatterns
== NULL
) {
489 fTimeUnitToCountToPatterns
[i
] = countToPatterns
= initHash(err
);
490 if (U_FAILURE(err
)) {
494 MessageFormat
** formatters
= (MessageFormat
**)countToPatterns
->get(*pluralCount
);
495 if( formatters
== NULL
|| formatters
[style
] == NULL
) {
496 // look through parents
497 const char* localeName
= getLocaleID(err
);
498 CharString pluralCountChars
;
499 pluralCountChars
.appendInvariantChars(*pluralCount
, err
);
500 searchInLocaleChain(style
, key
, localeName
,
501 (TimeUnit::UTimeUnitFields
)i
,
502 *pluralCount
, pluralCountChars
.data(),
503 countToPatterns
, err
);
505 // TODO: what to do with U_FAILURE(err) at this point.
506 // As is, the outer loop continues to run, but does nothing.
513 // srcPluralCount is the original plural count on which the pattern is
515 // searchPluralCount is the fallback plural count.
516 // For example, to search for pattern for ""one" hour",
517 // "one" is the srcPluralCount,
518 // if the pattern is not found even in root, fallback to
519 // using patterns of plural count "other",
520 // then, "other" is the searchPluralCount.
522 TimeUnitFormat::searchInLocaleChain(UTimeUnitFormatStyle style
, const char* key
, const char* localeName
,
523 TimeUnit::UTimeUnitFields srcTimeUnitField
,
524 const UnicodeString
& srcPluralCount
,
525 const char* searchPluralCount
,
526 Hashtable
* countToPatterns
,
528 if (U_FAILURE(err
)) {
531 UErrorCode status
= U_ZERO_ERROR
;
532 char parentLocale
[ULOC_FULLNAME_CAPACITY
];
533 uprv_strcpy(parentLocale
, localeName
);
535 U_ASSERT(countToPatterns
!= NULL
);
536 while ((locNameLen
= uloc_getParent(parentLocale
, parentLocale
,
537 ULOC_FULLNAME_CAPACITY
, &status
)) >= 0){
538 // look for pattern for srcPluralCount in locale tree
539 LocalUResourceBundlePointer
rb(ures_open(U_ICUDATA_UNIT
, parentLocale
, &status
));
540 LocalUResourceBundlePointer
unitsRes(ures_getByKey(rb
.getAlias(), key
, NULL
, &status
));
541 const char* timeUnitName
= getTimeUnitName(srcTimeUnitField
, status
);
542 LocalUResourceBundlePointer
countsToPatternRB(ures_getByKey(unitsRes
.getAlias(), timeUnitName
, NULL
, &status
));
543 const UChar
* pattern
;
545 pattern
= ures_getStringByKeyWithFallback(countsToPatternRB
.getAlias(), searchPluralCount
, &ptLength
, &status
);
546 if (U_SUCCESS(status
)) {
548 LocalPointer
<MessageFormat
> messageFormat(
549 new MessageFormat(UnicodeString(TRUE
, pattern
, ptLength
), getLocale(err
), err
), err
);
550 if (U_FAILURE(err
)) {
553 MessageFormat
** formatters
= (MessageFormat
**)countToPatterns
->get(srcPluralCount
);
554 if (formatters
== NULL
) {
555 LocalMemory
<MessageFormat
*> localFormatters(
556 (MessageFormat
**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT
*sizeof(MessageFormat
*)));
557 formatters
= localFormatters
.getAlias();
558 localFormatters
[UTMUTFMT_FULL_STYLE
] = NULL
;
559 localFormatters
[UTMUTFMT_ABBREVIATED_STYLE
] = NULL
;
560 countToPatterns
->put(srcPluralCount
, localFormatters
.orphan(), err
);
561 if (U_FAILURE(err
)) {
565 //delete formatters[style];
566 formatters
[style
] = messageFormat
.orphan();
569 status
= U_ZERO_ERROR
;
570 if (locNameLen
== 0) {
575 // if no unitsShort resource was found even after fallback to root locale
576 // then search the units resource fallback from the current level to root
577 if ( locNameLen
== 0 && uprv_strcmp(key
, gShortUnitsTag
) == 0) {
579 std::cout
<< "loop into searchInLocaleChain since Short-Long-Alternative \n";
581 CharString
pLocale(localeName
, -1, err
);
582 // Add an underscore at the tail of locale name,
583 // so that searchInLocaleChain will check the current locale before falling back
584 pLocale
.append('_', err
);
585 searchInLocaleChain(style
, gUnitsTag
, pLocale
.data(), srcTimeUnitField
, srcPluralCount
,
586 searchPluralCount
, countToPatterns
, err
);
587 if (U_FAILURE(err
)) {
590 MessageFormat
** formatters
= (MessageFormat
**)countToPatterns
->get(srcPluralCount
);
591 if (formatters
!= NULL
&& formatters
[style
] != NULL
) {
596 // if not found the pattern for this plural count at all,
597 // fall-back to plural count "other"
598 if ( uprv_strcmp(searchPluralCount
, gPluralCountOther
) == 0 ) {
599 // set default fall back the same as the resource in root
600 LocalPointer
<MessageFormat
> messageFormat
;
601 const UChar
*pattern
= NULL
;
602 if ( srcTimeUnitField
== TimeUnit::UTIMEUNIT_SECOND
) {
603 pattern
= DEFAULT_PATTERN_FOR_SECOND
;
604 } else if ( srcTimeUnitField
== TimeUnit::UTIMEUNIT_MINUTE
) {
605 pattern
= DEFAULT_PATTERN_FOR_MINUTE
;
606 } else if ( srcTimeUnitField
== TimeUnit::UTIMEUNIT_HOUR
) {
607 pattern
= DEFAULT_PATTERN_FOR_HOUR
;
608 } else if ( srcTimeUnitField
== TimeUnit::UTIMEUNIT_WEEK
) {
609 pattern
= DEFAULT_PATTERN_FOR_WEEK
;
610 } else if ( srcTimeUnitField
== TimeUnit::UTIMEUNIT_DAY
) {
611 pattern
= DEFAULT_PATTERN_FOR_DAY
;
612 } else if ( srcTimeUnitField
== TimeUnit::UTIMEUNIT_MONTH
) {
613 pattern
= DEFAULT_PATTERN_FOR_MONTH
;
614 } else if ( srcTimeUnitField
== TimeUnit::UTIMEUNIT_YEAR
) {
615 pattern
= DEFAULT_PATTERN_FOR_YEAR
;
617 if (pattern
!= NULL
) {
618 messageFormat
.adoptInsteadAndCheckErrorCode(
619 new MessageFormat(UnicodeString(TRUE
, pattern
, -1), getLocale(err
), err
), err
);
621 if (U_FAILURE(err
)) {
624 MessageFormat
** formatters
= (MessageFormat
**)countToPatterns
->get(srcPluralCount
);
625 if (formatters
== NULL
) {
626 LocalMemory
<MessageFormat
*> localFormatters (
627 (MessageFormat
**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT
*sizeof(MessageFormat
*)));
628 if (localFormatters
.isNull()) {
629 err
= U_MEMORY_ALLOCATION_ERROR
;
632 formatters
= localFormatters
.getAlias();
633 formatters
[UTMUTFMT_FULL_STYLE
] = NULL
;
634 formatters
[UTMUTFMT_ABBREVIATED_STYLE
] = NULL
;
635 countToPatterns
->put(srcPluralCount
, localFormatters
.orphan(), err
);
637 if (U_SUCCESS(err
)) {
638 //delete formatters[style];
639 formatters
[style
] = messageFormat
.orphan();
642 // fall back to rule "other", and search in parents
643 searchInLocaleChain(style
, key
, localeName
, srcTimeUnitField
, srcPluralCount
,
644 gPluralCountOther
, countToPatterns
, err
);
649 TimeUnitFormat::setLocale(const Locale
& locale
, UErrorCode
& status
) {
650 if (setMeasureFormatLocale(locale
, status
)) {
657 TimeUnitFormat::setNumberFormat(const NumberFormat
& format
, UErrorCode
& status
){
658 if (U_FAILURE(status
)) {
661 adoptNumberFormat((NumberFormat
*)format
.clone(), status
);
666 TimeUnitFormat::deleteHash(Hashtable
* htable
) {
667 int32_t pos
= UHASH_FIRST
;
668 const UHashElement
* element
= NULL
;
670 while ( (element
= htable
->nextElement(pos
)) != NULL
) {
671 const UHashTok valueTok
= element
->value
;
672 const MessageFormat
** value
= (const MessageFormat
**)valueTok
.pointer
;
673 delete value
[UTMUTFMT_FULL_STYLE
];
674 delete value
[UTMUTFMT_ABBREVIATED_STYLE
];
684 TimeUnitFormat::copyHash(const Hashtable
* source
, Hashtable
* target
, UErrorCode
& status
) {
685 if ( U_FAILURE(status
) ) {
688 int32_t pos
= UHASH_FIRST
;
689 const UHashElement
* element
= NULL
;
691 while ( (element
= source
->nextElement(pos
)) != NULL
) {
692 const UHashTok keyTok
= element
->key
;
693 const UnicodeString
* key
= (UnicodeString
*)keyTok
.pointer
;
694 const UHashTok valueTok
= element
->value
;
695 const MessageFormat
** value
= (const MessageFormat
**)valueTok
.pointer
;
696 MessageFormat
** newVal
= (MessageFormat
**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT
*sizeof(MessageFormat
*));
697 newVal
[0] = (MessageFormat
*)value
[0]->clone();
698 newVal
[1] = (MessageFormat
*)value
[1]->clone();
699 target
->put(UnicodeString(*key
), newVal
, status
);
700 if ( U_FAILURE(status
) ) {
714 * set hash table value comparator
716 * @param val1 one value in comparison
717 * @param val2 the other value in comparison
718 * @return TRUE if 2 values are the same, FALSE otherwise
720 static UBool U_CALLCONV
tmutfmtHashTableValueComparator(UHashTok val1
, UHashTok val2
);
723 U_CALLCONV
tmutfmtHashTableValueComparator(UHashTok val1
, UHashTok val2
) {
724 const MessageFormat
** pattern1
= (const MessageFormat
**)val1
.pointer
;
725 const MessageFormat
** pattern2
= (const MessageFormat
**)val2
.pointer
;
726 return *pattern1
[0] == *pattern2
[0] && *pattern1
[1] == *pattern2
[1];
732 TimeUnitFormat::initHash(UErrorCode
& status
) {
733 if ( U_FAILURE(status
) ) {
737 if ( (hTable
= new Hashtable(TRUE
, status
)) == NULL
) {
738 status
= U_MEMORY_ALLOCATION_ERROR
;
741 if ( U_FAILURE(status
) ) {
745 hTable
->setValueComparator(tmutfmtHashTableValueComparator
);
751 TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField
,
752 UErrorCode
& status
) {
753 if (U_FAILURE(status
)) {
757 case TimeUnit::UTIMEUNIT_YEAR
:
758 return gTimeUnitYear
;
759 case TimeUnit::UTIMEUNIT_MONTH
:
760 return gTimeUnitMonth
;
761 case TimeUnit::UTIMEUNIT_DAY
:
763 case TimeUnit::UTIMEUNIT_WEEK
:
764 return gTimeUnitWeek
;
765 case TimeUnit::UTIMEUNIT_HOUR
:
766 return gTimeUnitHour
;
767 case TimeUnit::UTIMEUNIT_MINUTE
:
768 return gTimeUnitMinute
;
769 case TimeUnit::UTIMEUNIT_SECOND
:
770 return gTimeUnitSecond
;
772 status
= U_ILLEGAL_ARGUMENT_ERROR
;