]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/tmutfmt.cpp
ICU-491.11.3.tar.gz
[apple/icu.git] / icuSources / i18n / tmutfmt.cpp
CommitLineData
729e4ab9
A
1/*
2 *******************************************************************************
3 * Copyright (C) 2008-2011, Google, International Business Machines Corporation
4388f060 4 * and others. All Rights Reserved.
729e4ab9
A
5 *******************************************************************************
6 */
7
8#include <typeinfo> // for 'typeid' to work
9
10#include "unicode/tmutfmt.h"
11
12#if !UCONFIG_NO_FORMATTING
13
4388f060 14#include "charstr.h"
729e4ab9
A
15#include "cmemory.h"
16#include "cstring.h"
17#include "hash.h"
18#include "uresimp.h"
19#include "unicode/msgfmt.h"
4388f060 20#include "uassert.h"
729e4ab9
A
21
22#define LEFT_CURLY_BRACKET ((UChar)0x007B)
23#define RIGHT_CURLY_BRACKET ((UChar)0x007D)
24#define SPACE ((UChar)0x0020)
25#define DIGIT_ZERO ((UChar)0x0030)
26#define LOW_S ((UChar)0x0073)
27#define LOW_M ((UChar)0x006D)
28#define LOW_I ((UChar)0x0069)
29#define LOW_N ((UChar)0x006E)
30#define LOW_H ((UChar)0x0068)
31#define LOW_W ((UChar)0x0077)
32#define LOW_D ((UChar)0x0064)
33#define LOW_Y ((UChar)0x0079)
34#define LOW_Z ((UChar)0x007A)
35#define LOW_E ((UChar)0x0065)
36#define LOW_R ((UChar)0x0072)
37#define LOW_O ((UChar)0x006F)
38#define LOW_N ((UChar)0x006E)
39#define LOW_T ((UChar)0x0074)
40
41
42//TODO: define in compile time
43//#define TMUTFMT_DEBUG 1
44
45#ifdef TMUTFMT_DEBUG
46#include <iostream>
47#endif
48
49U_NAMESPACE_BEGIN
50
51
52
53UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat)
54
55static const char gUnitsTag[] = "units";
56static const char gShortUnitsTag[] = "unitsShort";
57static const char gTimeUnitYear[] = "year";
58static const char gTimeUnitMonth[] = "month";
59static const char gTimeUnitDay[] = "day";
60static const char gTimeUnitWeek[] = "week";
61static const char gTimeUnitHour[] = "hour";
62static const char gTimeUnitMinute[] = "minute";
63static const char gTimeUnitSecond[] = "second";
64static const char gPluralCountOther[] = "other";
65
66static const UChar DEFAULT_PATTERN_FOR_SECOND[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_S, 0};
67static const UChar DEFAULT_PATTERN_FOR_MINUTE[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, LOW_I, LOW_N, 0};
68static const UChar DEFAULT_PATTERN_FOR_HOUR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_H, 0};
69static const UChar DEFAULT_PATTERN_FOR_WEEK[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_W, 0};
70static const UChar DEFAULT_PATTERN_FOR_DAY[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_D, 0};
71static const UChar DEFAULT_PATTERN_FOR_MONTH[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, 0};
72static const UChar DEFAULT_PATTERN_FOR_YEAR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_Y, 0};
73
74static const UChar PLURAL_COUNT_ZERO[] = {LOW_Z, LOW_E, LOW_R, LOW_O, 0};
75static const UChar PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0};
76static const UChar PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0};
77
78
79TimeUnitFormat::TimeUnitFormat(UErrorCode& status)
80: fNumberFormat(NULL),
81 fPluralRules(NULL) {
4388f060 82 create(Locale::getDefault(), UTMUTFMT_FULL_STYLE, status);
729e4ab9
A
83}
84
85
86TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status)
87: fNumberFormat(NULL),
88 fPluralRules(NULL) {
4388f060 89 create(locale, UTMUTFMT_FULL_STYLE, status);
729e4ab9
A
90}
91
92
4388f060 93TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status)
729e4ab9
A
94: fNumberFormat(NULL),
95 fPluralRules(NULL) {
96 create(locale, style, status);
97}
98
99
100TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other)
101: MeasureFormat(other),
102 fNumberFormat(NULL),
103 fPluralRules(NULL),
4388f060 104 fStyle(UTMUTFMT_FULL_STYLE)
729e4ab9
A
105{
106 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
107 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
108 i = (TimeUnit::UTimeUnitFields)(i+1)) {
109 fTimeUnitToCountToPatterns[i] = NULL;
110 }
111 *this = other;
112}
113
114
115TimeUnitFormat::~TimeUnitFormat() {
116 delete fNumberFormat;
117 fNumberFormat = NULL;
118 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
119 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
120 i = (TimeUnit::UTimeUnitFields)(i+1)) {
121 deleteHash(fTimeUnitToCountToPatterns[i]);
122 fTimeUnitToCountToPatterns[i] = NULL;
123 }
124 delete fPluralRules;
125 fPluralRules = NULL;
126}
127
128
129Format*
130TimeUnitFormat::clone(void) const {
131 return new TimeUnitFormat(*this);
132}
133
134
135TimeUnitFormat&
136TimeUnitFormat::operator=(const TimeUnitFormat& other) {
137 if (this == &other) {
138 return *this;
139 }
140 delete fNumberFormat;
141 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
142 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
143 i = (TimeUnit::UTimeUnitFields)(i+1)) {
144 deleteHash(fTimeUnitToCountToPatterns[i]);
145 fTimeUnitToCountToPatterns[i] = NULL;
146 }
147 delete fPluralRules;
148 if (other.fNumberFormat) {
149 fNumberFormat = (NumberFormat*)other.fNumberFormat->clone();
150 } else {
151 fNumberFormat = NULL;
152 }
153 fLocale = other.fLocale;
154 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
155 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
156 i = (TimeUnit::UTimeUnitFields)(i+1)) {
157 UErrorCode status = U_ZERO_ERROR;
158 fTimeUnitToCountToPatterns[i] = initHash(status);
159 if (U_SUCCESS(status)) {
160 copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
161 } else {
162 delete fTimeUnitToCountToPatterns[i];
163 fTimeUnitToCountToPatterns[i] = NULL;
164 }
165 }
166 if (other.fPluralRules) {
167 fPluralRules = (PluralRules*)other.fPluralRules->clone();
168 } else {
169 fPluralRules = NULL;
170 }
171 fStyle = other.fStyle;
172 return *this;
173}
174
175
176UBool
177TimeUnitFormat::operator==(const Format& other) const {
178 if (typeid(*this) == typeid(other)) {
179 TimeUnitFormat* fmt = (TimeUnitFormat*)&other;
180 UBool ret = ( ((fNumberFormat && fmt->fNumberFormat && *fNumberFormat == *fmt->fNumberFormat)
181 || fNumberFormat == fmt->fNumberFormat )
182 && fLocale == fmt->fLocale
183 && ((fPluralRules && fmt->fPluralRules && *fPluralRules == *fmt->fPluralRules)
184 || fPluralRules == fmt->fPluralRules)
185 && fStyle == fmt->fStyle);
186 if (ret) {
187 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
188 i < TimeUnit::UTIMEUNIT_FIELD_COUNT && ret;
189 i = (TimeUnit::UTimeUnitFields)(i+1)) {
190 ret = fTimeUnitToCountToPatterns[i]->equals(*(fmt->fTimeUnitToCountToPatterns[i]));
191 }
192 }
193 return ret;
194 }
195 return false;
196}
197
198
199UnicodeString&
200TimeUnitFormat::format(const Formattable& obj, UnicodeString& toAppendTo,
201 FieldPosition& pos, UErrorCode& status) const {
202 if (U_FAILURE(status)) {
203 return toAppendTo;
204 }
205 if (obj.getType() == Formattable::kObject) {
206 const UObject* formatObj = obj.getObject();
207 const TimeUnitAmount* amount = dynamic_cast<const TimeUnitAmount*>(formatObj);
208 if (amount != NULL){
209 Hashtable* countToPattern = fTimeUnitToCountToPatterns[amount->getTimeUnitField()];
210 double number;
211 const Formattable& amtNumber = amount->getNumber();
212 if (amtNumber.getType() == Formattable::kDouble) {
213 number = amtNumber.getDouble();
214 } else if (amtNumber.getType() == Formattable::kLong) {
215 number = amtNumber.getLong();
216 } else {
217 status = U_ILLEGAL_ARGUMENT_ERROR;
218 return toAppendTo;
219 }
220 UnicodeString count = fPluralRules->select(number);
221#ifdef TMUTFMT_DEBUG
222 char result[1000];
223 count.extract(0, count.length(), result, "UTF-8");
224 std::cout << "number: " << number << "; format plural count: " << result << "\n";
225#endif
226 MessageFormat* pattern = ((MessageFormat**)countToPattern->get(count))[fStyle];
227 Formattable formattable[1];
228 formattable[0].setDouble(number);
229 return pattern->format(formattable, 1, toAppendTo, pos, status);
230 }
231 }
232 status = U_ILLEGAL_ARGUMENT_ERROR;
233 return toAppendTo;
234}
235
236
237void
238TimeUnitFormat::parseObject(const UnicodeString& source,
239 Formattable& result,
240 ParsePosition& pos) const {
241 double resultNumber = -1;
242 UBool withNumberFormat = false;
243 TimeUnit::UTimeUnitFields resultTimeUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT;
244 int32_t oldPos = pos.getIndex();
245 int32_t newPos = -1;
246 int32_t longestParseDistance = 0;
247 UnicodeString* countOfLongestMatch = NULL;
248#ifdef TMUTFMT_DEBUG
249 char res[1000];
250 source.extract(0, source.length(), res, "UTF-8");
251 std::cout << "parse source: " << res << "\n";
252#endif
253 // parse by iterating through all available patterns
254 // and looking for the longest match.
255 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
256 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
257 i = (TimeUnit::UTimeUnitFields)(i+1)) {
258 Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
259 int32_t elemPos = -1;
260 const UHashElement* elem = NULL;
261 while ((elem = countToPatterns->nextElement(elemPos)) != NULL){
262 const UHashTok keyTok = elem->key;
263 UnicodeString* count = (UnicodeString*)keyTok.pointer;
264#ifdef TMUTFMT_DEBUG
265 count->extract(0, count->length(), res, "UTF-8");
266 std::cout << "parse plural count: " << res << "\n";
267#endif
268 const UHashTok valueTok = elem->value;
269 // the value is a pair of MessageFormat*
270 MessageFormat** patterns = (MessageFormat**)valueTok.pointer;
4388f060
A
271 for (UTimeUnitFormatStyle style = UTMUTFMT_FULL_STYLE; style < UTMUTFMT_FORMAT_STYLE_COUNT;
272 style = (UTimeUnitFormatStyle)(style + 1)) {
729e4ab9
A
273 MessageFormat* pattern = patterns[style];
274 pos.setErrorIndex(-1);
275 pos.setIndex(oldPos);
276 // see if we can parse
277 Formattable parsed;
278 pattern->parseObject(source, parsed, pos);
279 if (pos.getErrorIndex() != -1 || pos.getIndex() == oldPos) {
280 continue;
281 }
282 #ifdef TMUTFMT_DEBUG
283 std::cout << "parsed.getType: " << parsed.getType() << "\n";
284 #endif
285 double tmpNumber = 0;
286 if (pattern->getArgTypeCount() != 0) {
287 // pattern with Number as beginning, such as "{0} d".
288 // check to make sure that the timeUnit is consistent
289 Formattable& temp = parsed[0];
290 if (temp.getType() == Formattable::kDouble) {
291 tmpNumber = temp.getDouble();
292 } else if (temp.getType() == Formattable::kLong) {
293 tmpNumber = temp.getLong();
294 } else {
295 continue;
296 }
297 UnicodeString select = fPluralRules->select(tmpNumber);
298 #ifdef TMUTFMT_DEBUG
299 select.extract(0, select.length(), res, "UTF-8");
300 std::cout << "parse plural select count: " << res << "\n";
301 #endif
302 if (*count != select) {
303 continue;
304 }
305 }
306 int32_t parseDistance = pos.getIndex() - oldPos;
307 if (parseDistance > longestParseDistance) {
308 if (pattern->getArgTypeCount() != 0) {
309 resultNumber = tmpNumber;
310 withNumberFormat = true;
311 } else {
312 withNumberFormat = false;
313 }
314 resultTimeUnit = i;
315 newPos = pos.getIndex();
316 longestParseDistance = parseDistance;
317 countOfLongestMatch = count;
318 }
319 }
320 }
321 }
322 /* After find the longest match, parse the number.
323 * Result number could be null for the pattern without number pattern.
324 * such as unit pattern in Arabic.
325 * When result number is null, use plural rule to set the number.
326 */
327 if (withNumberFormat == false && longestParseDistance != 0) {
328 // set the number using plurrual count
4388f060 329 if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ZERO, 4)) {
729e4ab9 330 resultNumber = 0;
4388f060 331 } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ONE, 3)) {
729e4ab9 332 resultNumber = 1;
4388f060 333 } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_TWO, 3)) {
729e4ab9
A
334 resultNumber = 2;
335 } else {
336 // should not happen.
337 // TODO: how to handle?
338 resultNumber = 3;
339 }
340 }
341 if (longestParseDistance == 0) {
342 pos.setIndex(oldPos);
343 pos.setErrorIndex(0);
344 } else {
345 UErrorCode status = U_ZERO_ERROR;
346 TimeUnitAmount* tmutamt = new TimeUnitAmount(resultNumber, resultTimeUnit, status);
347 if (U_SUCCESS(status)) {
348 result.adoptObject(tmutamt);
349 pos.setIndex(newPos);
350 pos.setErrorIndex(-1);
351 } else {
352 pos.setIndex(oldPos);
353 pos.setErrorIndex(0);
354 }
355 }
356}
357
358
359void
4388f060 360TimeUnitFormat::create(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) {
729e4ab9
A
361 if (U_FAILURE(status)) {
362 return;
363 }
4388f060 364 if (style < UTMUTFMT_FULL_STYLE || style > UTMUTFMT_ABBREVIATED_STYLE) {
729e4ab9
A
365 status = U_ILLEGAL_ARGUMENT_ERROR;
366 return;
367 }
368 fStyle = style;
369 fLocale = locale;
370 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
371 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
372 i = (TimeUnit::UTimeUnitFields)(i+1)) {
373 fTimeUnitToCountToPatterns[i] = NULL;
374 }
375 //TODO: format() and parseObj() are const member functions,
376 //so, can not do lazy initialization in C++.
377 //setup has to be done in constructors.
378 //and here, the behavior is not consistent with Java.
379 //In Java, create an empty instance does not setup locale as
380 //default locale. If it followed by setNumberFormat(),
381 //in format(), the locale will set up as the locale in fNumberFormat.
382 //But in C++, this sets the locale as the default locale.
383 setup(status);
384}
385
386void
387TimeUnitFormat::setup(UErrorCode& err) {
388 initDataMembers(err);
4388f060
A
389 readFromCurrentLocale(UTMUTFMT_FULL_STYLE, gUnitsTag, err);
390 checkConsistency(UTMUTFMT_FULL_STYLE, gUnitsTag, err);
391 readFromCurrentLocale(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, err);
392 checkConsistency(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, err);
729e4ab9
A
393}
394
395
396void
397TimeUnitFormat::initDataMembers(UErrorCode& err){
398 if (U_FAILURE(err)) {
399 return;
400 }
401 if (fNumberFormat == NULL) {
402 fNumberFormat = NumberFormat::createInstance(fLocale, err);
403 }
404 delete fPluralRules;
405 fPluralRules = PluralRules::forLocale(fLocale, err);
406 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
407 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
408 i = (TimeUnit::UTimeUnitFields)(i+1)) {
409 deleteHash(fTimeUnitToCountToPatterns[i]);
410 fTimeUnitToCountToPatterns[i] = NULL;
411 }
412}
413
414
415
416
417void
4388f060 418TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key, UErrorCode& err) {
729e4ab9
A
419 if (U_FAILURE(err)) {
420 return;
421 }
422 // fill timeUnitToCountToPatterns from resource file
423 // err is used to indicate wrong status except missing resource.
424 // status is an error code used in resource lookup.
425 // status does not affect "err".
426 UErrorCode status = U_ZERO_ERROR;
427 UResourceBundle *rb, *unitsRes;
428 rb = ures_open(NULL, fLocale.getName(), &status);
429 unitsRes = ures_getByKey(rb, key, NULL, &status);
430 if (U_FAILURE(status)) {
431 ures_close(unitsRes);
432 ures_close(rb);
433 return;
434 }
435 int32_t size = ures_getSize(unitsRes);
436 for ( int32_t index = 0; index < size; ++index) {
437 // resource of one time unit
438 UResourceBundle* oneTimeUnit = ures_getByIndex(unitsRes, index,
439 NULL, &status);
440 if (U_SUCCESS(status)) {
441 const char* timeUnitName = ures_getKey(oneTimeUnit);
442 if (timeUnitName == NULL) {
443 ures_close(oneTimeUnit);
444 continue;
445 }
446 UResourceBundle* countsToPatternRB = ures_getByKey(unitsRes,
447 timeUnitName,
448 NULL, &status);
449 if (countsToPatternRB == NULL || U_FAILURE(status)) {
450 ures_close(countsToPatternRB);
451 ures_close(oneTimeUnit);
452 continue;
453 }
454 TimeUnit::UTimeUnitFields timeUnitField = TimeUnit::UTIMEUNIT_FIELD_COUNT;
455 if ( uprv_strcmp(timeUnitName, gTimeUnitYear) == 0 ) {
456 timeUnitField = TimeUnit::UTIMEUNIT_YEAR;
457 } else if ( uprv_strcmp(timeUnitName, gTimeUnitMonth) == 0 ) {
458 timeUnitField = TimeUnit::UTIMEUNIT_MONTH;
459 } else if ( uprv_strcmp(timeUnitName, gTimeUnitDay) == 0 ) {
460 timeUnitField = TimeUnit::UTIMEUNIT_DAY;
461 } else if ( uprv_strcmp(timeUnitName, gTimeUnitHour) == 0 ) {
462 timeUnitField = TimeUnit::UTIMEUNIT_HOUR;
463 } else if ( uprv_strcmp(timeUnitName, gTimeUnitMinute) == 0 ) {
464 timeUnitField = TimeUnit::UTIMEUNIT_MINUTE;
465 } else if ( uprv_strcmp(timeUnitName, gTimeUnitSecond) == 0 ) {
466 timeUnitField = TimeUnit::UTIMEUNIT_SECOND;
467 } else if ( uprv_strcmp(timeUnitName, gTimeUnitWeek) == 0 ) {
468 timeUnitField = TimeUnit::UTIMEUNIT_WEEK;
469 } else {
470 ures_close(countsToPatternRB);
471 ures_close(oneTimeUnit);
472 continue;
473 }
474 Hashtable* countToPatterns = fTimeUnitToCountToPatterns[timeUnitField];
475 if (countToPatterns == NULL) {
476 countToPatterns = initHash(err);
477 if (U_FAILURE(err)) {
478 ures_close(countsToPatternRB);
479 ures_close(oneTimeUnit);
480 delete countToPatterns;
481 break;
482 }
483 }
484 int32_t count = ures_getSize(countsToPatternRB);
729e4ab9 485 const char* pluralCount;
729e4ab9
A
486 for ( int32_t pluralIndex = 0; pluralIndex < count; ++pluralIndex) {
487 // resource of count to pattern
4388f060
A
488 UnicodeString pattern =
489 ures_getNextUnicodeString(countsToPatternRB, &pluralCount, &status);
729e4ab9
A
490 if (U_FAILURE(status)) {
491 continue;
492 }
493 MessageFormat* messageFormat = new MessageFormat(pattern, fLocale, err);
494 if ( U_SUCCESS(err) ) {
495 if (fNumberFormat != NULL) {
496 messageFormat->setFormat(0, *fNumberFormat);
497 }
4388f060
A
498 UnicodeString pluralCountUniStr(pluralCount, -1, US_INV);
499 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(pluralCountUniStr);
729e4ab9 500 if (formatters == NULL) {
4388f060
A
501 formatters = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*));
502 formatters[UTMUTFMT_FULL_STYLE] = NULL;
503 formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
504 countToPatterns->put(pluralCountUniStr, formatters, err);
729e4ab9
A
505 if (U_FAILURE(err)) {
506 uprv_free(formatters);
507 }
508 }
509 if (U_SUCCESS(err)) {
510 //delete formatters[style];
511 formatters[style] = messageFormat;
512 }
513 }
514 if (U_FAILURE(err)) {
515 ures_close(countsToPatternRB);
516 ures_close(oneTimeUnit);
517 ures_close(unitsRes);
518 ures_close(rb);
519 delete messageFormat;
520 delete countToPatterns;
521 return;
522 }
523 }
524 if (fTimeUnitToCountToPatterns[timeUnitField] == NULL) {
525 fTimeUnitToCountToPatterns[timeUnitField] = countToPatterns;
526 }
527 ures_close(countsToPatternRB);
528 }
529 ures_close(oneTimeUnit);
530 }
531 ures_close(unitsRes);
532 ures_close(rb);
533}
534
535
536void
4388f060 537TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style, const char* key, UErrorCode& err) {
729e4ab9
A
538 if (U_FAILURE(err)) {
539 return;
540 }
541 // there should be patterns for each plural rule in each time unit.
542 // For each time unit,
543 // for each plural rule, following is unit pattern fall-back rule:
544 // ( for example: "one" hour )
545 // look for its unit pattern in its locale tree.
546 // if pattern is not found in its own locale, such as de_DE,
547 // look for the pattern in its parent, such as de,
548 // keep looking till found or till root.
549 // if the pattern is not found in root either,
550 // fallback to plural count "other",
551 // look for the pattern of "other" in the locale tree:
552 // "de_DE" to "de" to "root".
553 // If not found, fall back to value of
554 // static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h".
555 //
556 // Following is consistency check to create pattern for each
557 // plural rule in each time unit using above fall-back rule.
558 //
559 StringEnumeration* keywords = fPluralRules->getKeywords(err);
560 if (U_SUCCESS(err)) {
4388f060
A
561 const UnicodeString* pluralCount;
562 while ((pluralCount = keywords->snext(err)) != NULL) {
729e4ab9
A
563 if ( U_SUCCESS(err) ) {
564 for (int32_t i = 0; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; ++i) {
565 // for each time unit,
566 // get all the patterns for each plural rule in this locale.
567 Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
568 if ( countToPatterns == NULL ) {
569 countToPatterns = initHash(err);
570 if (U_FAILURE(err)) {
571 delete countToPatterns;
572 return;
573 }
574 fTimeUnitToCountToPatterns[i] = countToPatterns;
575 }
4388f060 576 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(*pluralCount);
729e4ab9
A
577 if( formatters == NULL || formatters[style] == NULL ) {
578 // look through parents
579 const char* localeName = fLocale.getName();
4388f060
A
580 CharString pluralCountChars;
581 pluralCountChars.appendInvariantChars(*pluralCount, err);
729e4ab9
A
582 searchInLocaleChain(style, key, localeName,
583 (TimeUnit::UTimeUnitFields)i,
4388f060 584 *pluralCount, pluralCountChars.data(),
729e4ab9
A
585 countToPatterns, err);
586 }
587 }
588 }
589 }
590 }
591 delete keywords;
592}
593
594
595
596// srcPluralCount is the original plural count on which the pattern is
597// searched for.
598// searchPluralCount is the fallback plural count.
599// For example, to search for pattern for ""one" hour",
600// "one" is the srcPluralCount,
601// if the pattern is not found even in root, fallback to
602// using patterns of plural count "other",
603// then, "other" is the searchPluralCount.
604void
4388f060 605TimeUnitFormat::searchInLocaleChain(UTimeUnitFormatStyle style, const char* key, const char* localeName,
729e4ab9 606 TimeUnit::UTimeUnitFields srcTimeUnitField,
4388f060 607 const UnicodeString& srcPluralCount,
729e4ab9
A
608 const char* searchPluralCount,
609 Hashtable* countToPatterns,
610 UErrorCode& err) {
611 if (U_FAILURE(err)) {
612 return;
613 }
614 UErrorCode status = U_ZERO_ERROR;
615 char parentLocale[ULOC_FULLNAME_CAPACITY];
616 uprv_strcpy(parentLocale, localeName);
617 int32_t locNameLen;
4388f060 618 U_ASSERT(countToPatterns != NULL);
729e4ab9
A
619 while ((locNameLen = uloc_getParent(parentLocale, parentLocale,
620 ULOC_FULLNAME_CAPACITY, &status)) >= 0){
621 // look for pattern for srcPluralCount in locale tree
622 UResourceBundle *rb, *unitsRes, *countsToPatternRB;
623 rb = ures_open(NULL, parentLocale, &status);
624 unitsRes = ures_getByKey(rb, key, NULL, &status);
625 const char* timeUnitName = getTimeUnitName(srcTimeUnitField, status);
626 countsToPatternRB = ures_getByKey(unitsRes, timeUnitName, NULL, &status);
627 const UChar* pattern;
628 int32_t ptLength;
629 pattern = ures_getStringByKeyWithFallback(countsToPatternRB, searchPluralCount, &ptLength, &status);
630 if (U_SUCCESS(status)) {
631 //found
4388f060 632 MessageFormat* messageFormat = new MessageFormat(UnicodeString(TRUE, pattern, ptLength), fLocale, err);
729e4ab9
A
633 if (U_SUCCESS(err)) {
634 if (fNumberFormat != NULL) {
635 messageFormat->setFormat(0, *fNumberFormat);
636 }
637 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
638 if (formatters == NULL) {
4388f060
A
639 formatters = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*));
640 formatters[UTMUTFMT_FULL_STYLE] = NULL;
641 formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
729e4ab9
A
642 countToPatterns->put(srcPluralCount, formatters, err);
643 if (U_FAILURE(err)) {
644 uprv_free(formatters);
645 delete messageFormat;
646 }
647 }
648 if (U_SUCCESS(err)) {
649 //delete formatters[style];
650 formatters[style] = messageFormat;
651 }
652 } else {
653 delete messageFormat;
654 }
655 ures_close(countsToPatternRB);
656 ures_close(unitsRes);
657 ures_close(rb);
658 return;
659 }
660 ures_close(countsToPatternRB);
661 ures_close(unitsRes);
662 ures_close(rb);
663 status = U_ZERO_ERROR;
664 if ( locNameLen ==0 ) {
665 break;
666 }
667 }
668
669 // if no unitsShort resource was found even after fallback to root locale
670 // then search the units resource fallback from the current level to root
671 if ( locNameLen == 0 && uprv_strcmp(key, gShortUnitsTag) == 0) {
672#ifdef TMUTFMT_DEBUG
673 std::cout << "loop into searchInLocaleChain since Short-Long-Alternative \n";
674#endif
675 char pLocale[ULOC_FULLNAME_CAPACITY];
676 uprv_strcpy(pLocale, localeName);
677 // Add an underscore at the tail of locale name,
678 // so that searchInLocaleChain will check the current locale before falling back
679 uprv_strcat(pLocale, "_");
680 searchInLocaleChain(style, gUnitsTag, pLocale, srcTimeUnitField, srcPluralCount,
681 searchPluralCount, countToPatterns, err);
4388f060
A
682 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
683 if (formatters != NULL && formatters[style] != NULL) {
684 return;
729e4ab9
A
685 }
686 }
687
688 // if not found the pattern for this plural count at all,
689 // fall-back to plural count "other"
690 if ( uprv_strcmp(searchPluralCount, gPluralCountOther) == 0 ) {
691 // set default fall back the same as the resource in root
692 MessageFormat* messageFormat = NULL;
4388f060 693 const UChar *pattern = NULL;
729e4ab9 694 if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_SECOND ) {
4388f060 695 pattern = DEFAULT_PATTERN_FOR_SECOND;
729e4ab9 696 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MINUTE ) {
4388f060 697 pattern = DEFAULT_PATTERN_FOR_MINUTE;
729e4ab9 698 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_HOUR ) {
4388f060 699 pattern = DEFAULT_PATTERN_FOR_HOUR;
729e4ab9 700 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_WEEK ) {
4388f060 701 pattern = DEFAULT_PATTERN_FOR_WEEK;
729e4ab9 702 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_DAY ) {
4388f060 703 pattern = DEFAULT_PATTERN_FOR_DAY;
729e4ab9 704 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MONTH ) {
4388f060 705 pattern = DEFAULT_PATTERN_FOR_MONTH;
729e4ab9 706 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_YEAR ) {
4388f060
A
707 pattern = DEFAULT_PATTERN_FOR_YEAR;
708 }
709 if (pattern != NULL) {
710 messageFormat = new MessageFormat(UnicodeString(TRUE, pattern, -1), fLocale, err);
729e4ab9
A
711 }
712 if (U_SUCCESS(err)) {
713 if (fNumberFormat != NULL && messageFormat != NULL) {
714 messageFormat->setFormat(0, *fNumberFormat);
715 }
716 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
717 if (formatters == NULL) {
4388f060
A
718 formatters = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*));
719 formatters[UTMUTFMT_FULL_STYLE] = NULL;
720 formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
729e4ab9
A
721 countToPatterns->put(srcPluralCount, formatters, err);
722 if (U_FAILURE(err)) {
723 uprv_free(formatters);
724 delete messageFormat;
725 }
726 }
727 if (U_SUCCESS(err)) {
728 //delete formatters[style];
729 formatters[style] = messageFormat;
730 }
731 } else {
732 delete messageFormat;
733 }
734 } else {
735 // fall back to rule "other", and search in parents
736 searchInLocaleChain(style, key, localeName, srcTimeUnitField, srcPluralCount,
737 gPluralCountOther, countToPatterns, err);
738 }
739}
740
741void
742TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) {
743 if (U_SUCCESS(status) && fLocale != locale) {
744 fLocale = locale;
745 setup(status);
746 }
747}
748
749
750void
751TimeUnitFormat::setNumberFormat(const NumberFormat& format, UErrorCode& status){
752 if (U_FAILURE(status) || (fNumberFormat && format == *fNumberFormat)) {
753 return;
754 }
755 delete fNumberFormat;
756 fNumberFormat = (NumberFormat*)format.clone();
757 // reset the number formatter in the fTimeUnitToCountToPatterns map
758 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
759 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
760 i = (TimeUnit::UTimeUnitFields)(i+1)) {
761 int32_t pos = -1;
762 const UHashElement* elem = NULL;
763 while ((elem = fTimeUnitToCountToPatterns[i]->nextElement(pos)) != NULL){
764 const UHashTok keyTok = elem->value;
765 MessageFormat** pattern = (MessageFormat**)keyTok.pointer;
4388f060
A
766 pattern[UTMUTFMT_FULL_STYLE]->setFormat(0, format);
767 pattern[UTMUTFMT_ABBREVIATED_STYLE]->setFormat(0, format);
729e4ab9
A
768 }
769 }
770}
771
772
773void
774TimeUnitFormat::deleteHash(Hashtable* htable) {
775 int32_t pos = -1;
776 const UHashElement* element = NULL;
777 if ( htable ) {
778 while ( (element = htable->nextElement(pos)) != NULL ) {
779 const UHashTok valueTok = element->value;
780 const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
4388f060
A
781 delete value[UTMUTFMT_FULL_STYLE];
782 delete value[UTMUTFMT_ABBREVIATED_STYLE];
729e4ab9
A
783 //delete[] value;
784 uprv_free(value);
785 }
786 }
787 delete htable;
788}
789
790
791void
792TimeUnitFormat::copyHash(const Hashtable* source, Hashtable* target, UErrorCode& status) {
793 if ( U_FAILURE(status) ) {
794 return;
795 }
796 int32_t pos = -1;
797 const UHashElement* element = NULL;
798 if ( source ) {
799 while ( (element = source->nextElement(pos)) != NULL ) {
800 const UHashTok keyTok = element->key;
801 const UnicodeString* key = (UnicodeString*)keyTok.pointer;
802 const UHashTok valueTok = element->value;
803 const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
4388f060 804 MessageFormat** newVal = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*));
729e4ab9
A
805 newVal[0] = (MessageFormat*)value[0]->clone();
806 newVal[1] = (MessageFormat*)value[1]->clone();
807 target->put(UnicodeString(*key), newVal, status);
808 if ( U_FAILURE(status) ) {
809 delete newVal[0];
810 delete newVal[1];
811 uprv_free(newVal);
812 return;
813 }
814 }
815 }
816}
817
818
819U_CDECL_BEGIN
820
821/**
822 * set hash table value comparator
823 *
824 * @param val1 one value in comparison
825 * @param val2 the other value in comparison
826 * @return TRUE if 2 values are the same, FALSE otherwise
827 */
828static UBool U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2);
829
830static UBool
831U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2) {
832 const MessageFormat** pattern1 = (const MessageFormat**)val1.pointer;
833 const MessageFormat** pattern2 = (const MessageFormat**)val2.pointer;
834 return *pattern1[0] == *pattern2[0] && *pattern1[1] == *pattern2[1];
835}
836
837U_CDECL_END
838
839Hashtable*
840TimeUnitFormat::initHash(UErrorCode& status) {
841 if ( U_FAILURE(status) ) {
842 return NULL;
843 }
844 Hashtable* hTable;
845 if ( (hTable = new Hashtable(TRUE, status)) == NULL ) {
846 status = U_MEMORY_ALLOCATION_ERROR;
847 return NULL;
848 }
849 if ( U_FAILURE(status) ) {
850 delete hTable;
851 return NULL;
852 }
853 hTable->setValueComparator(tmutfmtHashTableValueComparator);
854 return hTable;
855}
856
857
858const char*
859TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField,
860 UErrorCode& status) {
861 if (U_FAILURE(status)) {
862 return NULL;
863 }
864 switch (unitField) {
865 case TimeUnit::UTIMEUNIT_YEAR:
866 return gTimeUnitYear;
867 case TimeUnit::UTIMEUNIT_MONTH:
868 return gTimeUnitMonth;
869 case TimeUnit::UTIMEUNIT_DAY:
870 return gTimeUnitDay;
871 case TimeUnit::UTIMEUNIT_WEEK:
872 return gTimeUnitWeek;
873 case TimeUnit::UTIMEUNIT_HOUR:
874 return gTimeUnitHour;
875 case TimeUnit::UTIMEUNIT_MINUTE:
876 return gTimeUnitMinute;
877 case TimeUnit::UTIMEUNIT_SECOND:
878 return gTimeUnitSecond;
879 default:
880 status = U_ILLEGAL_ARGUMENT_ERROR;
881 return NULL;
882 }
883}
884
885U_NAMESPACE_END
886
887#endif