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