1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2008-2015, Google, International Business Machines Corporation
6 * and others. All Rights Reserved.
7 *******************************************************************************
10 #include "unicode/tmutfmt.h"
12 #if !UCONFIG_NO_FORMATTING
14 #include "unicode/decimfmt.h"
15 #include "unicode/localpointer.h"
16 #include "plurrule_impl.h"
24 #include "unicode/msgfmt.h"
27 #define LEFT_CURLY_BRACKET ((UChar)0x007B)
28 #define RIGHT_CURLY_BRACKET ((UChar)0x007D)
29 #define SPACE ((UChar)0x0020)
30 #define DIGIT_ZERO ((UChar)0x0030)
31 #define LOW_S ((UChar)0x0073)
32 #define LOW_M ((UChar)0x006D)
33 #define LOW_I ((UChar)0x0069)
34 #define LOW_N ((UChar)0x006E)
35 #define LOW_H ((UChar)0x0068)
36 #define LOW_W ((UChar)0x0077)
37 #define LOW_D ((UChar)0x0064)
38 #define LOW_Y ((UChar)0x0079)
39 #define LOW_Z ((UChar)0x007A)
40 #define LOW_E ((UChar)0x0065)
41 #define LOW_R ((UChar)0x0072)
42 #define LOW_O ((UChar)0x006F)
43 #define LOW_N ((UChar)0x006E)
44 #define LOW_T ((UChar)0x0074)
47 //TODO: define in compile time
48 //#define TMUTFMT_DEBUG 1
58 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat
)
60 static const char gUnitsTag
[] = "units";
61 static const char gShortUnitsTag
[] = "unitsShort";
62 static const char gTimeUnitYear
[] = "year";
63 static const char gTimeUnitMonth
[] = "month";
64 static const char gTimeUnitDay
[] = "day";
65 static const char gTimeUnitWeek
[] = "week";
66 static const char gTimeUnitHour
[] = "hour";
67 static const char gTimeUnitMinute
[] = "minute";
68 static const char gTimeUnitSecond
[] = "second";
69 static const char gPluralCountOther
[] = "other";
71 static const UChar DEFAULT_PATTERN_FOR_SECOND
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
, SPACE
, LOW_S
, 0};
72 static const UChar DEFAULT_PATTERN_FOR_MINUTE
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
, SPACE
, LOW_M
, LOW_I
, LOW_N
, 0};
73 static const UChar DEFAULT_PATTERN_FOR_HOUR
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
, SPACE
, LOW_H
, 0};
74 static const UChar DEFAULT_PATTERN_FOR_WEEK
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
, SPACE
, LOW_W
, 0};
75 static const UChar DEFAULT_PATTERN_FOR_DAY
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
, SPACE
, LOW_D
, 0};
76 static const UChar DEFAULT_PATTERN_FOR_MONTH
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
, SPACE
, LOW_M
, 0};
77 static const UChar DEFAULT_PATTERN_FOR_YEAR
[] = {LEFT_CURLY_BRACKET
, DIGIT_ZERO
, RIGHT_CURLY_BRACKET
, SPACE
, LOW_Y
, 0};
79 static const UChar PLURAL_COUNT_ZERO
[] = {LOW_Z
, LOW_E
, LOW_R
, LOW_O
, 0};
80 static const UChar PLURAL_COUNT_ONE
[] = {LOW_O
, LOW_N
, LOW_E
, 0};
81 static const UChar PLURAL_COUNT_TWO
[] = {LOW_T
, LOW_W
, LOW_O
, 0};
83 TimeUnitFormat::TimeUnitFormat(UErrorCode
& status
) {
84 initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE
, NULL
, status
);
85 create(UTMUTFMT_FULL_STYLE
, status
);
89 TimeUnitFormat::TimeUnitFormat(const Locale
& locale
, UErrorCode
& status
) {
90 initMeasureFormat(locale
, UMEASFMT_WIDTH_WIDE
, NULL
, status
);
91 create(UTMUTFMT_FULL_STYLE
, status
);
95 TimeUnitFormat::TimeUnitFormat(const Locale
& locale
, UTimeUnitFormatStyle style
, UErrorCode
& status
) {
97 case UTMUTFMT_FULL_STYLE
:
98 initMeasureFormat(locale
, UMEASFMT_WIDTH_WIDE
, NULL
, status
);
100 case UTMUTFMT_ABBREVIATED_STYLE
:
101 initMeasureFormat(locale
, UMEASFMT_WIDTH_SHORT
, NULL
, status
);
104 initMeasureFormat(locale
, UMEASFMT_WIDTH_WIDE
, NULL
, status
);
107 create(style
, status
);
110 TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat
& other
)
111 : MeasureFormat(other
),
114 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
115 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
116 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
117 UErrorCode status
= U_ZERO_ERROR
;
118 fTimeUnitToCountToPatterns
[i
] = initHash(status
);
119 if (U_SUCCESS(status
)) {
120 copyHash(other
.fTimeUnitToCountToPatterns
[i
], fTimeUnitToCountToPatterns
[i
], status
);
122 delete fTimeUnitToCountToPatterns
[i
];
123 fTimeUnitToCountToPatterns
[i
] = NULL
;
129 TimeUnitFormat::~TimeUnitFormat() {
130 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
131 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
132 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
133 deleteHash(fTimeUnitToCountToPatterns
[i
]);
134 fTimeUnitToCountToPatterns
[i
] = NULL
;
140 TimeUnitFormat::clone(void) const {
141 return new TimeUnitFormat(*this);
146 TimeUnitFormat::operator=(const TimeUnitFormat
& other
) {
147 if (this == &other
) {
150 MeasureFormat::operator=(other
);
151 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
152 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
153 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
154 deleteHash(fTimeUnitToCountToPatterns
[i
]);
155 fTimeUnitToCountToPatterns
[i
] = NULL
;
157 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
158 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
159 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
160 UErrorCode status
= U_ZERO_ERROR
;
161 fTimeUnitToCountToPatterns
[i
] = initHash(status
);
162 if (U_SUCCESS(status
)) {
163 copyHash(other
.fTimeUnitToCountToPatterns
[i
], fTimeUnitToCountToPatterns
[i
], status
);
165 delete fTimeUnitToCountToPatterns
[i
];
166 fTimeUnitToCountToPatterns
[i
] = NULL
;
169 fStyle
= other
.fStyle
;
174 TimeUnitFormat::parseObject(const UnicodeString
& source
,
176 ParsePosition
& pos
) const {
177 Formattable
resultNumber(0.0);
178 UBool withNumberFormat
= false;
179 TimeUnit::UTimeUnitFields resultTimeUnit
= TimeUnit::UTIMEUNIT_FIELD_COUNT
;
180 int32_t oldPos
= pos
.getIndex();
182 int32_t longestParseDistance
= 0;
183 UnicodeString
* countOfLongestMatch
= NULL
;
186 source
.extract(0, source
.length(), res
, "UTF-8");
187 std::cout
<< "parse source: " << res
<< "\n";
189 // parse by iterating through all available patterns
190 // and looking for the longest match.
191 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
192 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
193 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
194 Hashtable
* countToPatterns
= fTimeUnitToCountToPatterns
[i
];
195 int32_t elemPos
= UHASH_FIRST
;
196 const UHashElement
* elem
= NULL
;
197 while ((elem
= countToPatterns
->nextElement(elemPos
)) != NULL
){
198 const UHashTok keyTok
= elem
->key
;
199 UnicodeString
* count
= (UnicodeString
*)keyTok
.pointer
;
201 count
->extract(0, count
->length(), res
, "UTF-8");
202 std::cout
<< "parse plural count: " << res
<< "\n";
204 const UHashTok valueTok
= elem
->value
;
205 // the value is a pair of MessageFormat*
206 MessageFormat
** patterns
= (MessageFormat
**)valueTok
.pointer
;
207 for (UTimeUnitFormatStyle style
= UTMUTFMT_FULL_STYLE
; style
< UTMUTFMT_FORMAT_STYLE_COUNT
;
208 style
= (UTimeUnitFormatStyle
)(style
+ 1)) {
209 MessageFormat
* pattern
= patterns
[style
];
210 pos
.setErrorIndex(-1);
211 pos
.setIndex(oldPos
);
212 // see if we can parse
214 pattern
->parseObject(source
, parsed
, pos
);
215 if (pos
.getErrorIndex() != -1 || pos
.getIndex() == oldPos
) {
219 std::cout
<< "parsed.getType: " << parsed
.getType() << "\n";
221 Formattable
tmpNumber(0.0);
222 if (pattern
->getArgTypeCount() != 0) {
223 Formattable
& temp
= parsed
[0];
224 if (temp
.getType() == Formattable::kString
) {
225 UnicodeString tmpString
;
226 UErrorCode pStatus
= U_ZERO_ERROR
;
227 getNumberFormat().parse(temp
.getString(tmpString
), tmpNumber
, pStatus
);
228 if (U_FAILURE(pStatus
)) {
231 } else if (temp
.isNumeric()) {
237 int32_t parseDistance
= pos
.getIndex() - oldPos
;
238 if (parseDistance
> longestParseDistance
) {
239 if (pattern
->getArgTypeCount() != 0) {
240 resultNumber
= tmpNumber
;
241 withNumberFormat
= true;
243 withNumberFormat
= false;
246 newPos
= pos
.getIndex();
247 longestParseDistance
= parseDistance
;
248 countOfLongestMatch
= count
;
253 /* After find the longest match, parse the number.
254 * Result number could be null for the pattern without number pattern.
255 * such as unit pattern in Arabic.
256 * When result number is null, use plural rule to set the number.
258 if (withNumberFormat
== false && longestParseDistance
!= 0) {
259 // set the number using plurrual count
260 if (0 == countOfLongestMatch
->compare(PLURAL_COUNT_ZERO
, 4)) {
261 resultNumber
= Formattable(0.0);
262 } else if (0 == countOfLongestMatch
->compare(PLURAL_COUNT_ONE
, 3)) {
263 resultNumber
= Formattable(1.0);
264 } else if (0 == countOfLongestMatch
->compare(PLURAL_COUNT_TWO
, 3)) {
265 resultNumber
= Formattable(2.0);
267 // should not happen.
268 // TODO: how to handle?
269 resultNumber
= Formattable(3.0);
272 if (longestParseDistance
== 0) {
273 pos
.setIndex(oldPos
);
274 pos
.setErrorIndex(0);
276 UErrorCode status
= U_ZERO_ERROR
;
277 LocalPointer
<TimeUnitAmount
> tmutamt(new TimeUnitAmount(resultNumber
, resultTimeUnit
, status
), status
);
278 if (U_SUCCESS(status
)) {
279 result
.adoptObject(tmutamt
.orphan());
280 pos
.setIndex(newPos
);
281 pos
.setErrorIndex(-1);
283 pos
.setIndex(oldPos
);
284 pos
.setErrorIndex(0);
290 TimeUnitFormat::create(UTimeUnitFormatStyle style
, UErrorCode
& status
) {
291 // fTimeUnitToCountToPatterns[] must have its elements initialized to NULL first
292 // before checking for failure status.
293 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
294 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
295 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
296 fTimeUnitToCountToPatterns
[i
] = NULL
;
299 if (U_FAILURE(status
)) {
302 if (style
< UTMUTFMT_FULL_STYLE
|| style
>= UTMUTFMT_FORMAT_STYLE_COUNT
) {
303 status
= U_ILLEGAL_ARGUMENT_ERROR
;
308 //TODO: format() and parseObj() are const member functions,
309 //so, can not do lazy initialization in C++.
310 //setup has to be done in constructors.
311 //and here, the behavior is not consistent with Java.
312 //In Java, create an empty instance does not setup locale as
313 //default locale. If it followed by setNumberFormat(),
314 //in format(), the locale will set up as the locale in fNumberFormat.
315 //But in C++, this sets the locale as the default locale.
320 TimeUnitFormat::setup(UErrorCode
& err
) {
321 initDataMembers(err
);
323 UVector
pluralCounts(0, uhash_compareUnicodeString
, 6, err
);
324 LocalPointer
<StringEnumeration
> keywords(getPluralRules().getKeywords(err
), err
);
325 if (U_FAILURE(err
)) {
328 UnicodeString
* pluralCount
;
329 while ((pluralCount
= const_cast<UnicodeString
*>(keywords
->snext(err
))) != NULL
) {
330 pluralCounts
.addElement(pluralCount
, err
);
332 readFromCurrentLocale(UTMUTFMT_FULL_STYLE
, gUnitsTag
, pluralCounts
, err
);
333 checkConsistency(UTMUTFMT_FULL_STYLE
, gUnitsTag
, err
);
334 readFromCurrentLocale(UTMUTFMT_ABBREVIATED_STYLE
, gShortUnitsTag
, pluralCounts
, err
);
335 checkConsistency(UTMUTFMT_ABBREVIATED_STYLE
, gShortUnitsTag
, err
);
340 TimeUnitFormat::initDataMembers(UErrorCode
& err
){
341 if (U_FAILURE(err
)) {
344 for (TimeUnit::UTimeUnitFields i
= TimeUnit::UTIMEUNIT_YEAR
;
345 i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
;
346 i
= (TimeUnit::UTimeUnitFields
)(i
+1)) {
347 deleteHash(fTimeUnitToCountToPatterns
[i
]);
348 fTimeUnitToCountToPatterns
[i
] = NULL
;
352 struct TimeUnitFormatReadSink
: public ResourceSink
{
353 TimeUnitFormat
*timeUnitFormatObj
;
354 const UVector
&pluralCounts
;
355 UTimeUnitFormatStyle style
;
358 TimeUnitFormatReadSink(TimeUnitFormat
*timeUnitFormatObj
,
359 const UVector
&pluralCounts
, UTimeUnitFormatStyle style
) :
360 timeUnitFormatObj(timeUnitFormatObj
), pluralCounts(pluralCounts
),
361 style(style
), beenHere(FALSE
){}
363 virtual ~TimeUnitFormatReadSink();
365 virtual void put(const char *key
, ResourceValue
&value
, UBool
, UErrorCode
&errorCode
) {
366 // Skip all put() calls except the first one -- discard all fallback data.
373 ResourceTable units
= value
.getTable(errorCode
);
374 if (U_FAILURE(errorCode
)) { return; }
376 for (int32_t i
= 0; units
.getKeyAndValue(i
, key
, value
); ++i
) {
377 const char* timeUnitName
= key
;
378 if (timeUnitName
== NULL
) {
382 TimeUnit::UTimeUnitFields timeUnitField
= TimeUnit::UTIMEUNIT_FIELD_COUNT
;
383 if ( uprv_strcmp(timeUnitName
, gTimeUnitYear
) == 0 ) {
384 timeUnitField
= TimeUnit::UTIMEUNIT_YEAR
;
385 } else if ( uprv_strcmp(timeUnitName
, gTimeUnitMonth
) == 0 ) {
386 timeUnitField
= TimeUnit::UTIMEUNIT_MONTH
;
387 } else if ( uprv_strcmp(timeUnitName
, gTimeUnitDay
) == 0 ) {
388 timeUnitField
= TimeUnit::UTIMEUNIT_DAY
;
389 } else if ( uprv_strcmp(timeUnitName
, gTimeUnitHour
) == 0 ) {
390 timeUnitField
= TimeUnit::UTIMEUNIT_HOUR
;
391 } else if ( uprv_strcmp(timeUnitName
, gTimeUnitMinute
) == 0 ) {
392 timeUnitField
= TimeUnit::UTIMEUNIT_MINUTE
;
393 } else if ( uprv_strcmp(timeUnitName
, gTimeUnitSecond
) == 0 ) {
394 timeUnitField
= TimeUnit::UTIMEUNIT_SECOND
;
395 } else if ( uprv_strcmp(timeUnitName
, gTimeUnitWeek
) == 0 ) {
396 timeUnitField
= TimeUnit::UTIMEUNIT_WEEK
;
400 LocalPointer
<Hashtable
> localCountToPatterns
;
401 Hashtable
*countToPatterns
=
402 timeUnitFormatObj
->fTimeUnitToCountToPatterns
[timeUnitField
];
403 if (countToPatterns
== NULL
) {
404 localCountToPatterns
.adoptInsteadAndCheckErrorCode(
405 timeUnitFormatObj
->initHash(errorCode
), errorCode
);
406 countToPatterns
= localCountToPatterns
.getAlias();
407 if (U_FAILURE(errorCode
)) {
412 ResourceTable countsToPatternTable
= value
.getTable(errorCode
);
413 if (U_FAILURE(errorCode
)) {
416 for (int32_t j
= 0; countsToPatternTable
.getKeyAndValue(j
, key
, value
); ++j
) {
417 errorCode
= U_ZERO_ERROR
;
418 UnicodeString pattern
= value
.getUnicodeString(errorCode
);
419 if (U_FAILURE(errorCode
)) {
422 UnicodeString
pluralCountUniStr(key
, -1, US_INV
);
423 if (!pluralCounts
.contains(&pluralCountUniStr
)) {
426 LocalPointer
<MessageFormat
> messageFormat(new MessageFormat(
427 pattern
, timeUnitFormatObj
->getLocale(errorCode
), errorCode
), errorCode
);
428 if (U_FAILURE(errorCode
)) {
431 MessageFormat
** formatters
=
432 (MessageFormat
**)countToPatterns
->get(pluralCountUniStr
);
433 if (formatters
== NULL
) {
434 LocalMemory
<MessageFormat
*> localFormatters(
435 (MessageFormat
**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT
*sizeof(MessageFormat
*)));
436 if (localFormatters
.isNull()) {
437 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
440 localFormatters
[UTMUTFMT_FULL_STYLE
] = NULL
;
441 localFormatters
[UTMUTFMT_ABBREVIATED_STYLE
] = NULL
;
442 countToPatterns
->put(pluralCountUniStr
, localFormatters
.getAlias(), errorCode
);
443 if (U_FAILURE(errorCode
)) {
446 formatters
= localFormatters
.orphan();
448 formatters
[style
] = messageFormat
.orphan();
451 if (timeUnitFormatObj
->fTimeUnitToCountToPatterns
[timeUnitField
] == NULL
) {
452 timeUnitFormatObj
->fTimeUnitToCountToPatterns
[timeUnitField
] = localCountToPatterns
.orphan();
459 TimeUnitFormatReadSink::~TimeUnitFormatReadSink() {}
462 TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style
, const char* key
,
463 const UVector
& pluralCounts
, UErrorCode
& err
) {
464 if (U_FAILURE(err
)) {
467 // fill timeUnitToCountToPatterns from resource file
468 // err is used to indicate wrong status except missing resource.
469 // status is an error code used in resource lookup.
470 // status does not affect "err".
471 UErrorCode status
= U_ZERO_ERROR
;
472 LocalUResourceBundlePointer
rb(ures_open(U_ICUDATA_UNIT
, getLocaleID(status
), &status
));
474 LocalUResourceBundlePointer
unitsRes(ures_getByKey(rb
.getAlias(), key
, NULL
, &status
));
475 ures_getByKey(unitsRes
.getAlias(), "duration", unitsRes
.getAlias(), &status
);
476 if (U_FAILURE(status
)) {
480 TimeUnitFormatReadSink
sink(this, pluralCounts
, style
);
481 ures_getAllItemsWithFallback(unitsRes
.getAlias(), "", sink
, status
);
485 TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style
, const char* key
, UErrorCode
& err
) {
486 if (U_FAILURE(err
)) {
489 // there should be patterns for each plural rule in each time unit.
490 // For each time unit,
491 // for each plural rule, following is unit pattern fall-back rule:
492 // ( for example: "one" hour )
493 // look for its unit pattern in its locale tree.
494 // if pattern is not found in its own locale, such as de_DE,
495 // look for the pattern in its parent, such as de,
496 // keep looking till found or till root.
497 // if the pattern is not found in root either,
498 // fallback to plural count "other",
499 // look for the pattern of "other" in the locale tree:
500 // "de_DE" to "de" to "root".
501 // If not found, fall back to value of
502 // static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h".
504 // Following is consistency check to create pattern for each
505 // plural rule in each time unit using above fall-back rule.
507 LocalPointer
<StringEnumeration
> keywords(
508 getPluralRules().getKeywords(err
), err
);
509 const UnicodeString
* pluralCount
;
510 while (U_SUCCESS(err
) && (pluralCount
= keywords
->snext(err
)) != NULL
) {
511 for (int32_t i
= 0; i
< TimeUnit::UTIMEUNIT_FIELD_COUNT
; ++i
) {
512 // for each time unit,
513 // get all the patterns for each plural rule in this locale.
514 Hashtable
* countToPatterns
= fTimeUnitToCountToPatterns
[i
];
515 if ( countToPatterns
== NULL
) {
516 fTimeUnitToCountToPatterns
[i
] = countToPatterns
= initHash(err
);
517 if (U_FAILURE(err
)) {
521 MessageFormat
** formatters
= (MessageFormat
**)countToPatterns
->get(*pluralCount
);
522 if( formatters
== NULL
|| formatters
[style
] == NULL
) {
523 // look through parents
524 const char* localeName
= getLocaleID(err
);
525 CharString pluralCountChars
;
526 pluralCountChars
.appendInvariantChars(*pluralCount
, err
);
527 searchInLocaleChain(style
, key
, localeName
,
528 (TimeUnit::UTimeUnitFields
)i
,
529 *pluralCount
, pluralCountChars
.data(),
530 countToPatterns
, err
);
532 // TODO: what to do with U_FAILURE(err) at this point.
533 // As is, the outer loop continues to run, but does nothing.
540 // srcPluralCount is the original plural count on which the pattern is
542 // searchPluralCount is the fallback plural count.
543 // For example, to search for pattern for ""one" hour",
544 // "one" is the srcPluralCount,
545 // if the pattern is not found even in root, fallback to
546 // using patterns of plural count "other",
547 // then, "other" is the searchPluralCount.
549 TimeUnitFormat::searchInLocaleChain(UTimeUnitFormatStyle style
, const char* key
, const char* localeName
,
550 TimeUnit::UTimeUnitFields srcTimeUnitField
,
551 const UnicodeString
& srcPluralCount
,
552 const char* searchPluralCount
,
553 Hashtable
* countToPatterns
,
555 if (U_FAILURE(err
)) {
558 UErrorCode status
= U_ZERO_ERROR
;
559 char parentLocale
[ULOC_FULLNAME_CAPACITY
];
560 uprv_strcpy(parentLocale
, localeName
);
562 U_ASSERT(countToPatterns
!= NULL
);
563 while ((locNameLen
= uloc_getParent(parentLocale
, parentLocale
,
564 ULOC_FULLNAME_CAPACITY
, &status
)) >= 0){
565 // look for pattern for srcPluralCount in locale tree
566 LocalUResourceBundlePointer
rb(ures_open(U_ICUDATA_UNIT
, parentLocale
, &status
));
567 LocalUResourceBundlePointer
unitsRes(ures_getByKey(rb
.getAlias(), key
, NULL
, &status
));
568 const char* timeUnitName
= getTimeUnitName(srcTimeUnitField
, status
);
569 LocalUResourceBundlePointer
countsToPatternRB(ures_getByKey(unitsRes
.getAlias(), timeUnitName
, NULL
, &status
));
570 const UChar
* pattern
;
572 pattern
= ures_getStringByKeyWithFallback(countsToPatternRB
.getAlias(), searchPluralCount
, &ptLength
, &status
);
573 if (U_SUCCESS(status
)) {
575 LocalPointer
<MessageFormat
> messageFormat(
576 new MessageFormat(UnicodeString(TRUE
, pattern
, ptLength
), getLocale(err
), err
), err
);
577 if (U_FAILURE(err
)) {
580 MessageFormat
** formatters
= (MessageFormat
**)countToPatterns
->get(srcPluralCount
);
581 if (formatters
== NULL
) {
582 LocalMemory
<MessageFormat
*> localFormatters(
583 (MessageFormat
**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT
*sizeof(MessageFormat
*)));
584 formatters
= localFormatters
.getAlias();
585 localFormatters
[UTMUTFMT_FULL_STYLE
] = NULL
;
586 localFormatters
[UTMUTFMT_ABBREVIATED_STYLE
] = NULL
;
587 countToPatterns
->put(srcPluralCount
, localFormatters
.orphan(), err
);
588 if (U_FAILURE(err
)) {
592 //delete formatters[style];
593 formatters
[style
] = messageFormat
.orphan();
596 status
= U_ZERO_ERROR
;
597 if (locNameLen
== 0) {
602 // if no unitsShort resource was found even after fallback to root locale
603 // then search the units resource fallback from the current level to root
604 if ( locNameLen
== 0 && uprv_strcmp(key
, gShortUnitsTag
) == 0) {
606 std::cout
<< "loop into searchInLocaleChain since Short-Long-Alternative \n";
608 CharString
pLocale(localeName
, -1, err
);
609 // Add an underscore at the tail of locale name,
610 // so that searchInLocaleChain will check the current locale before falling back
611 pLocale
.append('_', err
);
612 searchInLocaleChain(style
, gUnitsTag
, pLocale
.data(), srcTimeUnitField
, srcPluralCount
,
613 searchPluralCount
, countToPatterns
, err
);
614 if (U_FAILURE(err
)) {
617 MessageFormat
** formatters
= (MessageFormat
**)countToPatterns
->get(srcPluralCount
);
618 if (formatters
!= NULL
&& formatters
[style
] != NULL
) {
623 // if not found the pattern for this plural count at all,
624 // fall-back to plural count "other"
625 if ( uprv_strcmp(searchPluralCount
, gPluralCountOther
) == 0 ) {
626 // set default fall back the same as the resource in root
627 LocalPointer
<MessageFormat
> messageFormat
;
628 const UChar
*pattern
= NULL
;
629 if ( srcTimeUnitField
== TimeUnit::UTIMEUNIT_SECOND
) {
630 pattern
= DEFAULT_PATTERN_FOR_SECOND
;
631 } else if ( srcTimeUnitField
== TimeUnit::UTIMEUNIT_MINUTE
) {
632 pattern
= DEFAULT_PATTERN_FOR_MINUTE
;
633 } else if ( srcTimeUnitField
== TimeUnit::UTIMEUNIT_HOUR
) {
634 pattern
= DEFAULT_PATTERN_FOR_HOUR
;
635 } else if ( srcTimeUnitField
== TimeUnit::UTIMEUNIT_WEEK
) {
636 pattern
= DEFAULT_PATTERN_FOR_WEEK
;
637 } else if ( srcTimeUnitField
== TimeUnit::UTIMEUNIT_DAY
) {
638 pattern
= DEFAULT_PATTERN_FOR_DAY
;
639 } else if ( srcTimeUnitField
== TimeUnit::UTIMEUNIT_MONTH
) {
640 pattern
= DEFAULT_PATTERN_FOR_MONTH
;
641 } else if ( srcTimeUnitField
== TimeUnit::UTIMEUNIT_YEAR
) {
642 pattern
= DEFAULT_PATTERN_FOR_YEAR
;
644 if (pattern
!= NULL
) {
645 messageFormat
.adoptInsteadAndCheckErrorCode(
646 new MessageFormat(UnicodeString(TRUE
, pattern
, -1), getLocale(err
), err
), err
);
648 if (U_FAILURE(err
)) {
651 MessageFormat
** formatters
= (MessageFormat
**)countToPatterns
->get(srcPluralCount
);
652 if (formatters
== NULL
) {
653 LocalMemory
<MessageFormat
*> localFormatters (
654 (MessageFormat
**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT
*sizeof(MessageFormat
*)));
655 if (localFormatters
.isNull()) {
656 err
= U_MEMORY_ALLOCATION_ERROR
;
659 formatters
= localFormatters
.getAlias();
660 formatters
[UTMUTFMT_FULL_STYLE
] = NULL
;
661 formatters
[UTMUTFMT_ABBREVIATED_STYLE
] = NULL
;
662 countToPatterns
->put(srcPluralCount
, localFormatters
.orphan(), err
);
664 if (U_SUCCESS(err
)) {
665 //delete formatters[style];
666 formatters
[style
] = messageFormat
.orphan();
669 // fall back to rule "other", and search in parents
670 searchInLocaleChain(style
, key
, localeName
, srcTimeUnitField
, srcPluralCount
,
671 gPluralCountOther
, countToPatterns
, err
);
676 TimeUnitFormat::setLocale(const Locale
& locale
, UErrorCode
& status
) {
677 if (setMeasureFormatLocale(locale
, status
)) {
684 TimeUnitFormat::setNumberFormat(const NumberFormat
& format
, UErrorCode
& status
){
685 if (U_FAILURE(status
)) {
688 adoptNumberFormat((NumberFormat
*)format
.clone(), status
);
693 TimeUnitFormat::deleteHash(Hashtable
* htable
) {
694 int32_t pos
= UHASH_FIRST
;
695 const UHashElement
* element
= NULL
;
697 while ( (element
= htable
->nextElement(pos
)) != NULL
) {
698 const UHashTok valueTok
= element
->value
;
699 const MessageFormat
** value
= (const MessageFormat
**)valueTok
.pointer
;
700 delete value
[UTMUTFMT_FULL_STYLE
];
701 delete value
[UTMUTFMT_ABBREVIATED_STYLE
];
711 TimeUnitFormat::copyHash(const Hashtable
* source
, Hashtable
* target
, UErrorCode
& status
) {
712 if ( U_FAILURE(status
) ) {
715 int32_t pos
= UHASH_FIRST
;
716 const UHashElement
* element
= NULL
;
718 while ( (element
= source
->nextElement(pos
)) != NULL
) {
719 const UHashTok keyTok
= element
->key
;
720 const UnicodeString
* key
= (UnicodeString
*)keyTok
.pointer
;
721 const UHashTok valueTok
= element
->value
;
722 const MessageFormat
** value
= (const MessageFormat
**)valueTok
.pointer
;
723 MessageFormat
** newVal
= (MessageFormat
**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT
*sizeof(MessageFormat
*));
724 newVal
[0] = (MessageFormat
*)value
[0]->clone();
725 newVal
[1] = (MessageFormat
*)value
[1]->clone();
726 target
->put(UnicodeString(*key
), newVal
, status
);
727 if ( U_FAILURE(status
) ) {
741 * set hash table value comparator
743 * @param val1 one value in comparison
744 * @param val2 the other value in comparison
745 * @return TRUE if 2 values are the same, FALSE otherwise
747 static UBool U_CALLCONV
tmutfmtHashTableValueComparator(UHashTok val1
, UHashTok val2
);
750 U_CALLCONV
tmutfmtHashTableValueComparator(UHashTok val1
, UHashTok val2
) {
751 const MessageFormat
** pattern1
= (const MessageFormat
**)val1
.pointer
;
752 const MessageFormat
** pattern2
= (const MessageFormat
**)val2
.pointer
;
753 return *pattern1
[0] == *pattern2
[0] && *pattern1
[1] == *pattern2
[1];
759 TimeUnitFormat::initHash(UErrorCode
& status
) {
760 if ( U_FAILURE(status
) ) {
764 if ( (hTable
= new Hashtable(TRUE
, status
)) == NULL
) {
765 status
= U_MEMORY_ALLOCATION_ERROR
;
768 if ( U_FAILURE(status
) ) {
772 hTable
->setValueComparator(tmutfmtHashTableValueComparator
);
778 TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField
,
779 UErrorCode
& status
) {
780 if (U_FAILURE(status
)) {
784 case TimeUnit::UTIMEUNIT_YEAR
:
785 return gTimeUnitYear
;
786 case TimeUnit::UTIMEUNIT_MONTH
:
787 return gTimeUnitMonth
;
788 case TimeUnit::UTIMEUNIT_DAY
:
790 case TimeUnit::UTIMEUNIT_WEEK
:
791 return gTimeUnitWeek
;
792 case TimeUnit::UTIMEUNIT_HOUR
:
793 return gTimeUnitHour
;
794 case TimeUnit::UTIMEUNIT_MINUTE
:
795 return gTimeUnitMinute
;
796 case TimeUnit::UTIMEUNIT_SECOND
:
797 return gTimeUnitSecond
;
799 status
= U_ILLEGAL_ARGUMENT_ERROR
;