]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/tmutfmt.cpp
ICU-57149.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / tmutfmt.cpp
1 /*
2 *******************************************************************************
3 * Copyright (C) 2008-2015, Google, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 *******************************************************************************
6 */
7
8 #include "unicode/tmutfmt.h"
9
10 #if !UCONFIG_NO_FORMATTING
11
12 #include "unicode/decimfmt.h"
13 #include "unicode/localpointer.h"
14 #include "plurrule_impl.h"
15 #include "uvector.h"
16 #include "charstr.h"
17 #include "cmemory.h"
18 #include "cstring.h"
19 #include "hash.h"
20 #include "uresimp.h"
21 #include "ureslocs.h"
22 #include "unicode/msgfmt.h"
23 #include "uassert.h"
24
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)
43
44
45 //TODO: define in compile time
46 //#define TMUTFMT_DEBUG 1
47
48 #ifdef TMUTFMT_DEBUG
49 #include <iostream>
50 #endif
51
52 U_NAMESPACE_BEGIN
53
54
55
56 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat)
57
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";
68
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};
76
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};
80
81 TimeUnitFormat::TimeUnitFormat(UErrorCode& status) {
82 initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE, NULL, status);
83 create(UTMUTFMT_FULL_STYLE, status);
84 }
85
86
87 TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) {
88 initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
89 create(UTMUTFMT_FULL_STYLE, status);
90 }
91
92
93 TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) {
94 switch (style) {
95 case UTMUTFMT_FULL_STYLE:
96 initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
97 break;
98 case UTMUTFMT_ABBREVIATED_STYLE:
99 initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, NULL, status);
100 break;
101 default:
102 initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
103 break;
104 }
105 create(style, status);
106 }
107
108 TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other)
109 : MeasureFormat(other),
110 fStyle(other.fStyle)
111 {
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);
119 } else {
120 delete fTimeUnitToCountToPatterns[i];
121 fTimeUnitToCountToPatterns[i] = NULL;
122 }
123 }
124 }
125
126
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;
133 }
134 }
135
136
137 Format*
138 TimeUnitFormat::clone(void) const {
139 return new TimeUnitFormat(*this);
140 }
141
142
143 TimeUnitFormat&
144 TimeUnitFormat::operator=(const TimeUnitFormat& other) {
145 if (this == &other) {
146 return *this;
147 }
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;
154 }
155 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
156 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
157 i = (TimeUnit::UTimeUnitFields)(i+1)) {
158 UErrorCode status = U_ZERO_ERROR;
159 fTimeUnitToCountToPatterns[i] = initHash(status);
160 if (U_SUCCESS(status)) {
161 copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
162 } else {
163 delete fTimeUnitToCountToPatterns[i];
164 fTimeUnitToCountToPatterns[i] = NULL;
165 }
166 }
167 fStyle = other.fStyle;
168 return *this;
169 }
170
171 void
172 TimeUnitFormat::parseObject(const UnicodeString& source,
173 Formattable& result,
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();
179 int32_t newPos = -1;
180 int32_t longestParseDistance = 0;
181 UnicodeString* countOfLongestMatch = NULL;
182 #ifdef TMUTFMT_DEBUG
183 char res[1000];
184 source.extract(0, source.length(), res, "UTF-8");
185 std::cout << "parse source: " << res << "\n";
186 #endif
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;
198 #ifdef TMUTFMT_DEBUG
199 count->extract(0, count->length(), res, "UTF-8");
200 std::cout << "parse plural count: " << res << "\n";
201 #endif
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
211 Formattable parsed;
212 pattern->parseObject(source, parsed, pos);
213 if (pos.getErrorIndex() != -1 || pos.getIndex() == oldPos) {
214 continue;
215 }
216 #ifdef TMUTFMT_DEBUG
217 std::cout << "parsed.getType: " << parsed.getType() << "\n";
218 #endif
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)) {
227 continue;
228 }
229 } else if (temp.isNumeric()) {
230 tmpNumber = temp;
231 } else {
232 continue;
233 }
234 }
235 int32_t parseDistance = pos.getIndex() - oldPos;
236 if (parseDistance > longestParseDistance) {
237 if (pattern->getArgTypeCount() != 0) {
238 resultNumber = tmpNumber;
239 withNumberFormat = true;
240 } else {
241 withNumberFormat = false;
242 }
243 resultTimeUnit = i;
244 newPos = pos.getIndex();
245 longestParseDistance = parseDistance;
246 countOfLongestMatch = count;
247 }
248 }
249 }
250 }
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.
255 */
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);
264 } else {
265 // should not happen.
266 // TODO: how to handle?
267 resultNumber = Formattable(3.0);
268 }
269 }
270 if (longestParseDistance == 0) {
271 pos.setIndex(oldPos);
272 pos.setErrorIndex(0);
273 } else {
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);
280 } else {
281 pos.setIndex(oldPos);
282 pos.setErrorIndex(0);
283 }
284 }
285 }
286
287 void
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;
295 }
296
297 if (U_FAILURE(status)) {
298 return;
299 }
300 if (style < UTMUTFMT_FULL_STYLE || style >= UTMUTFMT_FORMAT_STYLE_COUNT) {
301 status = U_ILLEGAL_ARGUMENT_ERROR;
302 return;
303 }
304 fStyle = style;
305
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.
314 setup(status);
315 }
316
317 void
318 TimeUnitFormat::setup(UErrorCode& err) {
319 initDataMembers(err);
320
321 UVector pluralCounts(0, uhash_compareUnicodeString, 6, err);
322 LocalPointer<StringEnumeration> keywords(getPluralRules().getKeywords(err), err);
323 if (U_FAILURE(err)) {
324 return;
325 }
326 UnicodeString* pluralCount;
327 while ((pluralCount = const_cast<UnicodeString*>(keywords->snext(err))) != NULL) {
328 pluralCounts.addElement(pluralCount, err);
329 }
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);
334 }
335
336
337 void
338 TimeUnitFormat::initDataMembers(UErrorCode& err){
339 if (U_FAILURE(err)) {
340 return;
341 }
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;
347 }
348 }
349
350 void
351 TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key,
352 const UVector& pluralCounts, UErrorCode& err) {
353 if (U_FAILURE(err)) {
354 return;
355 }
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));
362
363 LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, NULL, &status));
364 ures_getByKey(unitsRes.getAlias(), "duration", unitsRes.getAlias(), &status);
365 if (U_FAILURE(status)) {
366 return;
367 }
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)) {
375 continue;
376 }
377 const char* timeUnitName = ures_getKey(oneTimeUnit.getAlias());
378 if (timeUnitName == NULL) {
379 continue;
380 }
381 LocalUResourceBundlePointer countsToPatternRB(
382 ures_getByKey(unitsRes.getAlias(), timeUnitName, NULL, &status));
383 if (countsToPatternRB.isNull() || U_FAILURE(status)) {
384 continue;
385 }
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;
401 } else {
402 continue;
403 }
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)) {
410 return;
411 }
412 }
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)) {
421 continue;
422 }
423 UnicodeString pluralCountUniStr(pluralCount, -1, US_INV);
424 if (!pluralCounts.contains(&pluralCountUniStr)) {
425 continue;
426 }
427 LocalPointer<MessageFormat> messageFormat(new MessageFormat(pattern, getLocale(err), err), err);
428 if (U_FAILURE(err)) {
429 return;
430 }
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;
437 return;
438 }
439 localFormatters[UTMUTFMT_FULL_STYLE] = NULL;
440 localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
441 countToPatterns->put(pluralCountUniStr, localFormatters.getAlias(), err);
442 if (U_FAILURE(err)) {
443 return;
444 }
445 formatters = localFormatters.orphan();
446 }
447 //delete formatters[style];
448 formatters[style] = messageFormat.orphan();
449 }
450 if (fTimeUnitToCountToPatterns[timeUnitField] == NULL) {
451 fTimeUnitToCountToPatterns[timeUnitField] = localCountToPatterns.orphan();
452 }
453 }
454 }
455
456
457 void
458 TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style, const char* key, UErrorCode& err) {
459 if (U_FAILURE(err)) {
460 return;
461 }
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".
476 //
477 // Following is consistency check to create pattern for each
478 // plural rule in each time unit using above fall-back rule.
479 //
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)) {
491 return;
492 }
493 }
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);
504 }
505 // TODO: what to do with U_FAILURE(err) at this point.
506 // As is, the outer loop continues to run, but does nothing.
507 }
508 }
509 }
510
511
512
513 // srcPluralCount is the original plural count on which the pattern is
514 // searched for.
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.
521 void
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,
527 UErrorCode& err) {
528 if (U_FAILURE(err)) {
529 return;
530 }
531 UErrorCode status = U_ZERO_ERROR;
532 char parentLocale[ULOC_FULLNAME_CAPACITY];
533 uprv_strcpy(parentLocale, localeName);
534 int32_t locNameLen;
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;
544 int32_t ptLength;
545 pattern = ures_getStringByKeyWithFallback(countsToPatternRB.getAlias(), searchPluralCount, &ptLength, &status);
546 if (U_SUCCESS(status)) {
547 //found
548 LocalPointer<MessageFormat> messageFormat(
549 new MessageFormat(UnicodeString(TRUE, pattern, ptLength), getLocale(err), err), err);
550 if (U_FAILURE(err)) {
551 return;
552 }
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)) {
562 return;
563 }
564 }
565 //delete formatters[style];
566 formatters[style] = messageFormat.orphan();
567 return;
568 }
569 status = U_ZERO_ERROR;
570 if (locNameLen == 0) {
571 break;
572 }
573 }
574
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) {
578 #ifdef TMUTFMT_DEBUG
579 std::cout << "loop into searchInLocaleChain since Short-Long-Alternative \n";
580 #endif
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)) {
588 return;
589 }
590 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
591 if (formatters != NULL && formatters[style] != NULL) {
592 return;
593 }
594 }
595
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;
616 }
617 if (pattern != NULL) {
618 messageFormat.adoptInsteadAndCheckErrorCode(
619 new MessageFormat(UnicodeString(TRUE, pattern, -1), getLocale(err), err), err);
620 }
621 if (U_FAILURE(err)) {
622 return;
623 }
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;
630 return;
631 }
632 formatters = localFormatters.getAlias();
633 formatters[UTMUTFMT_FULL_STYLE] = NULL;
634 formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
635 countToPatterns->put(srcPluralCount, localFormatters.orphan(), err);
636 }
637 if (U_SUCCESS(err)) {
638 //delete formatters[style];
639 formatters[style] = messageFormat.orphan();
640 }
641 } else {
642 // fall back to rule "other", and search in parents
643 searchInLocaleChain(style, key, localeName, srcTimeUnitField, srcPluralCount,
644 gPluralCountOther, countToPatterns, err);
645 }
646 }
647
648 void
649 TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) {
650 if (setMeasureFormatLocale(locale, status)) {
651 setup(status);
652 }
653 }
654
655
656 void
657 TimeUnitFormat::setNumberFormat(const NumberFormat& format, UErrorCode& status){
658 if (U_FAILURE(status)) {
659 return;
660 }
661 adoptNumberFormat((NumberFormat *)format.clone(), status);
662 }
663
664
665 void
666 TimeUnitFormat::deleteHash(Hashtable* htable) {
667 int32_t pos = UHASH_FIRST;
668 const UHashElement* element = NULL;
669 if ( htable ) {
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];
675 //delete[] value;
676 uprv_free(value);
677 }
678 }
679 delete htable;
680 }
681
682
683 void
684 TimeUnitFormat::copyHash(const Hashtable* source, Hashtable* target, UErrorCode& status) {
685 if ( U_FAILURE(status) ) {
686 return;
687 }
688 int32_t pos = UHASH_FIRST;
689 const UHashElement* element = NULL;
690 if ( source ) {
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) ) {
701 delete newVal[0];
702 delete newVal[1];
703 uprv_free(newVal);
704 return;
705 }
706 }
707 }
708 }
709
710
711 U_CDECL_BEGIN
712
713 /**
714 * set hash table value comparator
715 *
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
719 */
720 static UBool U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2);
721
722 static UBool
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];
727 }
728
729 U_CDECL_END
730
731 Hashtable*
732 TimeUnitFormat::initHash(UErrorCode& status) {
733 if ( U_FAILURE(status) ) {
734 return NULL;
735 }
736 Hashtable* hTable;
737 if ( (hTable = new Hashtable(TRUE, status)) == NULL ) {
738 status = U_MEMORY_ALLOCATION_ERROR;
739 return NULL;
740 }
741 if ( U_FAILURE(status) ) {
742 delete hTable;
743 return NULL;
744 }
745 hTable->setValueComparator(tmutfmtHashTableValueComparator);
746 return hTable;
747 }
748
749
750 const char*
751 TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField,
752 UErrorCode& status) {
753 if (U_FAILURE(status)) {
754 return NULL;
755 }
756 switch (unitField) {
757 case TimeUnit::UTIMEUNIT_YEAR:
758 return gTimeUnitYear;
759 case TimeUnit::UTIMEUNIT_MONTH:
760 return gTimeUnitMonth;
761 case TimeUnit::UTIMEUNIT_DAY:
762 return gTimeUnitDay;
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;
771 default:
772 status = U_ILLEGAL_ARGUMENT_ERROR;
773 return NULL;
774 }
775 }
776
777 U_NAMESPACE_END
778
779 #endif