1 /*
2 *******************************************************************************
3 * Copyright (C) 2008-2013, 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
TimeUnitFormat(UErrorCode & status)79 TimeUnitFormat::TimeUnitFormat(UErrorCode& status)
80 : fNumberFormat(NULL),
81 fPluralRules(NULL) {
82 create(Locale::getDefault(), UTMUTFMT_FULL_STYLE, status);
83 }
84
85
TimeUnitFormat(const Locale & locale,UErrorCode & status)86 TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status)
87 : fNumberFormat(NULL),
88 fPluralRules(NULL) {
89 create(locale, UTMUTFMT_FULL_STYLE, status);
90 }
91
92
TimeUnitFormat(const Locale & locale,UTimeUnitFormatStyle style,UErrorCode & status)93 TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status)
94 : fNumberFormat(NULL),
95 fPluralRules(NULL) {
96 create(locale, style, status);
97 }
98
99
TimeUnitFormat(const TimeUnitFormat & other)100 TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other)
101 : MeasureFormat(other),
102 fNumberFormat(NULL),
103 fPluralRules(NULL),
104 fStyle(UTMUTFMT_FULL_STYLE)
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
~TimeUnitFormat()115 TimeUnitFormat::~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
129 Format*
clone(void) const130 TimeUnitFormat::clone(void) const {
131 return new TimeUnitFormat(*this);
132 }
133
134
135 TimeUnitFormat&
operator =(const TimeUnitFormat & other)136 TimeUnitFormat::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
176 UBool
operator ==(const Format & other) const177 TimeUnitFormat::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
199 UnicodeString&
format(const Formattable & obj,UnicodeString & toAppendTo,FieldPosition & pos,UErrorCode & status) const200 TimeUnitFormat::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
237 void
parseObject(const UnicodeString & source,Formattable & result,ParsePosition & pos) const238 TimeUnitFormat::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;
271 for (UTimeUnitFormatStyle style = UTMUTFMT_FULL_STYLE; style < UTMUTFMT_FORMAT_STYLE_COUNT;
272 style = (UTimeUnitFormatStyle)(style + 1)) {
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
329 if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ZERO, 4)) {
330 resultNumber = 0;
331 } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ONE, 3)) {
332 resultNumber = 1;
333 } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_TWO, 3)) {
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 void
create(const Locale & locale,UTimeUnitFormatStyle style,UErrorCode & status)359 TimeUnitFormat::create(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) {
360 if (U_FAILURE(status)) {
361 return;
362 }
363 if (style < UTMUTFMT_FULL_STYLE || style >= UTMUTFMT_FORMAT_STYLE_COUNT) {
364 status = U_ILLEGAL_ARGUMENT_ERROR;
365 return;
366 }
367 fStyle = style;
368 fLocale = locale;
369 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
370 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
371 i = (TimeUnit::UTimeUnitFields)(i+1)) {
372 fTimeUnitToCountToPatterns[i] = NULL;
373 }
374 //TODO: format() and parseObj() are const member functions,
375 //so, can not do lazy initialization in C++.
376 //setup has to be done in constructors.
377 //and here, the behavior is not consistent with Java.
378 //In Java, create an empty instance does not setup locale as
379 //default locale. If it followed by setNumberFormat(),
380 //in format(), the locale will set up as the locale in fNumberFormat.
381 //But in C++, this sets the locale as the default locale.
382 setup(status);
383 }
384
385 void
setup(UErrorCode & err)386 TimeUnitFormat::setup(UErrorCode& err) {
387 initDataMembers(err);
388
389 UVector pluralCounts(0, uhash_compareUnicodeString, 6, err);
390 StringEnumeration* keywords = fPluralRules->getKeywords(err);
391 if (U_FAILURE(err)) {
392 return;
393 }
394 UnicodeString* pluralCount;
395 while ((pluralCount = const_cast<UnicodeString*>(keywords->snext(err))) != NULL) {
396 pluralCounts.addElement(pluralCount, err);
397 }
398 readFromCurrentLocale(UTMUTFMT_FULL_STYLE, gUnitsTag, pluralCounts, err);
399 checkConsistency(UTMUTFMT_FULL_STYLE, gUnitsTag, err);
400 readFromCurrentLocale(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, pluralCounts, err);
401 checkConsistency(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, err);
402 delete keywords;
403 }
404
405
406 void
initDataMembers(UErrorCode & err)407 TimeUnitFormat::initDataMembers(UErrorCode& err){
408 if (U_FAILURE(err)) {
409 return;
410 }
411 if (fNumberFormat == NULL) {
412 fNumberFormat = NumberFormat::createInstance(fLocale, err);
413 }
414 delete fPluralRules;
415 fPluralRules = PluralRules::forLocale(fLocale, err);
416 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
417 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
418 i = (TimeUnit::UTimeUnitFields)(i+1)) {
419 deleteHash(fTimeUnitToCountToPatterns[i]);
420 fTimeUnitToCountToPatterns[i] = NULL;
421 }
422 }
423
424 void
readFromCurrentLocale(UTimeUnitFormatStyle style,const char * key,const UVector & pluralCounts,UErrorCode & err)425 TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key,
426 const UVector& pluralCounts, UErrorCode& err) {
427 if (U_FAILURE(err)) {
428 return;
429 }
430 // fill timeUnitToCountToPatterns from resource file
431 // err is used to indicate wrong status except missing resource.
432 // status is an error code used in resource lookup.
433 // status does not affect "err".
434 UErrorCode status = U_ZERO_ERROR;
435 UResourceBundle *rb, *unitsRes;
436 rb = ures_open(NULL, fLocale.getName(), &status);
437 unitsRes = ures_getByKey(rb, key, NULL, &status);
438 unitsRes = ures_getByKey(unitsRes, "duration", unitsRes, &status);
439 if (U_FAILURE(status)) {
440 ures_close(unitsRes);
441 ures_close(rb);
442 return;
443 }
444 int32_t size = ures_getSize(unitsRes);
445 for ( int32_t index = 0; index < size; ++index) {
446 // resource of one time unit
447 UResourceBundle* oneTimeUnit = ures_getByIndex(unitsRes, index,
448 NULL, &status);
449 if (U_SUCCESS(status)) {
450 const char* timeUnitName = ures_getKey(oneTimeUnit);
451 if (timeUnitName == NULL) {
452 ures_close(oneTimeUnit);
453 continue;
454 }
455 UResourceBundle* countsToPatternRB = ures_getByKey(unitsRes,
456 timeUnitName,
457 NULL, &status);
458 if (countsToPatternRB == NULL || U_FAILURE(status)) {
459 ures_close(countsToPatternRB);
460 ures_close(oneTimeUnit);
461 continue;
462 }
463 TimeUnit::UTimeUnitFields timeUnitField = TimeUnit::UTIMEUNIT_FIELD_COUNT;
464 if ( uprv_strcmp(timeUnitName, gTimeUnitYear) == 0 ) {
465 timeUnitField = TimeUnit::UTIMEUNIT_YEAR;
466 } else if ( uprv_strcmp(timeUnitName, gTimeUnitMonth) == 0 ) {
467 timeUnitField = TimeUnit::UTIMEUNIT_MONTH;
468 } else if ( uprv_strcmp(timeUnitName, gTimeUnitDay) == 0 ) {
469 timeUnitField = TimeUnit::UTIMEUNIT_DAY;
470 } else if ( uprv_strcmp(timeUnitName, gTimeUnitHour) == 0 ) {
471 timeUnitField = TimeUnit::UTIMEUNIT_HOUR;
472 } else if ( uprv_strcmp(timeUnitName, gTimeUnitMinute) == 0 ) {
473 timeUnitField = TimeUnit::UTIMEUNIT_MINUTE;
474 } else if ( uprv_strcmp(timeUnitName, gTimeUnitSecond) == 0 ) {
475 timeUnitField = TimeUnit::UTIMEUNIT_SECOND;
476 } else if ( uprv_strcmp(timeUnitName, gTimeUnitWeek) == 0 ) {
477 timeUnitField = TimeUnit::UTIMEUNIT_WEEK;
478 } else {
479 ures_close(countsToPatternRB);
480 ures_close(oneTimeUnit);
481 continue;
482 }
483 Hashtable* countToPatterns = fTimeUnitToCountToPatterns[timeUnitField];
484 if (countToPatterns == NULL) {
485 countToPatterns = initHash(err);
486 if (U_FAILURE(err)) {
487 ures_close(countsToPatternRB);
488 ures_close(oneTimeUnit);
489 delete countToPatterns;
490 break;
491 }
492 }
493 int32_t count = ures_getSize(countsToPatternRB);
494 const char* pluralCount;
495 for ( int32_t pluralIndex = 0; pluralIndex < count; ++pluralIndex) {
496 // resource of count to pattern
497 UnicodeString pattern =
498 ures_getNextUnicodeString(countsToPatternRB, &pluralCount, &status);
499 if (U_FAILURE(status)) {
500 continue;
501 }
502 UnicodeString pluralCountUniStr(pluralCount, -1, US_INV);
503 if (!pluralCounts.contains(&pluralCountUniStr)) {
504 continue;
505 }
506 MessageFormat* messageFormat = new MessageFormat(pattern, fLocale, err);
507 if ( U_SUCCESS(err) ) {
508 if (fNumberFormat != NULL) {
509 messageFormat->setFormat(0, *fNumberFormat);
510 }
511 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(pluralCountUniStr);
512 if (formatters == NULL) {
513 formatters = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*));
514 formatters[UTMUTFMT_FULL_STYLE] = NULL;
515 formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
516 countToPatterns->put(pluralCountUniStr, formatters, err);
517 if (U_FAILURE(err)) {
518 uprv_free(formatters);
519 }
520 }
521 if (U_SUCCESS(err)) {
522 //delete formatters[style];
523 formatters[style] = messageFormat;
524 }
525 }
526 if (U_FAILURE(err)) {
527 ures_close(countsToPatternRB);
528 ures_close(oneTimeUnit);
529 ures_close(unitsRes);
530 ures_close(rb);
531 delete messageFormat;
532 delete countToPatterns;
533 return;
534 }
535 }
536 if (fTimeUnitToCountToPatterns[timeUnitField] == NULL) {
537 fTimeUnitToCountToPatterns[timeUnitField] = countToPatterns;
538 }
539 ures_close(countsToPatternRB);
540 }
541 ures_close(oneTimeUnit);
542 }
543 ures_close(unitsRes);
544 ures_close(rb);
545 }
546
547
548 void
checkConsistency(UTimeUnitFormatStyle style,const char * key,UErrorCode & err)549 TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style, const char* key, UErrorCode& err) {
550 if (U_FAILURE(err)) {
551 return;
552 }
553 // there should be patterns for each plural rule in each time unit.
554 // For each time unit,
555 // for each plural rule, following is unit pattern fall-back rule:
556 // ( for example: "one" hour )
557 // look for its unit pattern in its locale tree.
558 // if pattern is not found in its own locale, such as de_DE,
559 // look for the pattern in its parent, such as de,
560 // keep looking till found or till root.
561 // if the pattern is not found in root either,
562 // fallback to plural count "other",
563 // look for the pattern of "other" in the locale tree:
564 // "de_DE" to "de" to "root".
565 // If not found, fall back to value of
566 // static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h".
567 //
568 // Following is consistency check to create pattern for each
569 // plural rule in each time unit using above fall-back rule.
570 //
571 StringEnumeration* keywords = fPluralRules->getKeywords(err);
572 if (U_SUCCESS(err)) {
573 const UnicodeString* pluralCount;
574 while ((pluralCount = keywords->snext(err)) != NULL) {
575 if ( U_SUCCESS(err) ) {
576 for (int32_t i = 0; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; ++i) {
577 // for each time unit,
578 // get all the patterns for each plural rule in this locale.
579 Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
580 if ( countToPatterns == NULL ) {
581 countToPatterns = initHash(err);
582 if (U_FAILURE(err)) {
583 delete countToPatterns;
584 return;
585 }
586 fTimeUnitToCountToPatterns[i] = countToPatterns;
587 }
588 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(*pluralCount);
589 if( formatters == NULL || formatters[style] == NULL ) {
590 // look through parents
591 const char* localeName = fLocale.getName();
592 CharString pluralCountChars;
593 pluralCountChars.appendInvariantChars(*pluralCount, err);
594 searchInLocaleChain(style, key, localeName,
595 (TimeUnit::UTimeUnitFields)i,
596 *pluralCount, pluralCountChars.data(),
597 countToPatterns, err);
598 }
599 }
600 }
601 }
602 }
603 delete keywords;
604 }
605
606
607
608 // srcPluralCount is the original plural count on which the pattern is
609 // searched for.
610 // searchPluralCount is the fallback plural count.
611 // For example, to search for pattern for ""one" hour",
612 // "one" is the srcPluralCount,
613 // if the pattern is not found even in root, fallback to
614 // using patterns of plural count "other",
615 // then, "other" is the searchPluralCount.
616 void
searchInLocaleChain(UTimeUnitFormatStyle style,const char * key,const char * localeName,TimeUnit::UTimeUnitFields srcTimeUnitField,const UnicodeString & srcPluralCount,const char * searchPluralCount,Hashtable * countToPatterns,UErrorCode & err)617 TimeUnitFormat::searchInLocaleChain(UTimeUnitFormatStyle style, const char* key, const char* localeName,
618 TimeUnit::UTimeUnitFields srcTimeUnitField,
619 const UnicodeString& srcPluralCount,
620 const char* searchPluralCount,
621 Hashtable* countToPatterns,
622 UErrorCode& err) {
623 if (U_FAILURE(err)) {
624 return;
625 }
626 UErrorCode status = U_ZERO_ERROR;
627 char parentLocale[ULOC_FULLNAME_CAPACITY];
628 uprv_strcpy(parentLocale, localeName);
629 int32_t locNameLen;
630 U_ASSERT(countToPatterns != NULL);
631 while ((locNameLen = uloc_getParent(parentLocale, parentLocale,
632 ULOC_FULLNAME_CAPACITY, &status)) >= 0){
633 // look for pattern for srcPluralCount in locale tree
634 UResourceBundle *rb, *unitsRes, *countsToPatternRB;
635 rb = ures_open(NULL, parentLocale, &status);
636 unitsRes = ures_getByKey(rb, key, NULL, &status);
637 const char* timeUnitName = getTimeUnitName(srcTimeUnitField, status);
638 countsToPatternRB = ures_getByKey(unitsRes, timeUnitName, NULL, &status);
639 const UChar* pattern;
640 int32_t ptLength;
641 pattern = ures_getStringByKeyWithFallback(countsToPatternRB, searchPluralCount, &ptLength, &status);
642 if (U_SUCCESS(status)) {
643 //found
644 MessageFormat* messageFormat = new MessageFormat(UnicodeString(TRUE, pattern, ptLength), fLocale, err);
645 if (U_SUCCESS(err)) {
646 if (fNumberFormat != NULL) {
647 messageFormat->setFormat(0, *fNumberFormat);
648 }
649 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
650 if (formatters == NULL) {
651 formatters = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*));
652 formatters[UTMUTFMT_FULL_STYLE] = NULL;
653 formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
654 countToPatterns->put(srcPluralCount, formatters, err);
655 if (U_FAILURE(err)) {
656 uprv_free(formatters);
657 delete messageFormat;
658 }
659 }
660 if (U_SUCCESS(err)) {
661 //delete formatters[style];
662 formatters[style] = messageFormat;
663 }
664 } else {
665 delete messageFormat;
666 }
667 ures_close(countsToPatternRB);
668 ures_close(unitsRes);
669 ures_close(rb);
670 return;
671 }
672 ures_close(countsToPatternRB);
673 ures_close(unitsRes);
674 ures_close(rb);
675 status = U_ZERO_ERROR;
676 if ( locNameLen ==0 ) {
677 break;
678 }
679 }
680
681 // if no unitsShort resource was found even after fallback to root locale
682 // then search the units resource fallback from the current level to root
683 if ( locNameLen == 0 && uprv_strcmp(key, gShortUnitsTag) == 0) {
684 #ifdef TMUTFMT_DEBUG
685 std::cout << "loop into searchInLocaleChain since Short-Long-Alternative \n";
686 #endif
687 char pLocale[ULOC_FULLNAME_CAPACITY];
688 uprv_strcpy(pLocale, localeName);
689 // Add an underscore at the tail of locale name,
690 // so that searchInLocaleChain will check the current locale before falling back
691 uprv_strcat(pLocale, "_");
692 searchInLocaleChain(style, gUnitsTag, pLocale, srcTimeUnitField, srcPluralCount,
693 searchPluralCount, countToPatterns, err);
694 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
695 if (formatters != NULL && formatters[style] != NULL) {
696 return;
697 }
698 }
699
700 // if not found the pattern for this plural count at all,
701 // fall-back to plural count "other"
702 if ( uprv_strcmp(searchPluralCount, gPluralCountOther) == 0 ) {
703 // set default fall back the same as the resource in root
704 MessageFormat* messageFormat = NULL;
705 const UChar *pattern = NULL;
706 if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_SECOND ) {
707 pattern = DEFAULT_PATTERN_FOR_SECOND;
708 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MINUTE ) {
709 pattern = DEFAULT_PATTERN_FOR_MINUTE;
710 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_HOUR ) {
711 pattern = DEFAULT_PATTERN_FOR_HOUR;
712 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_WEEK ) {
713 pattern = DEFAULT_PATTERN_FOR_WEEK;
714 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_DAY ) {
715 pattern = DEFAULT_PATTERN_FOR_DAY;
716 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MONTH ) {
717 pattern = DEFAULT_PATTERN_FOR_MONTH;
718 } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_YEAR ) {
719 pattern = DEFAULT_PATTERN_FOR_YEAR;
720 }
721 if (pattern != NULL) {
722 messageFormat = new MessageFormat(UnicodeString(TRUE, pattern, -1), fLocale, err);
723 }
724 if (U_SUCCESS(err)) {
725 if (fNumberFormat != NULL && messageFormat != NULL) {
726 messageFormat->setFormat(0, *fNumberFormat);
727 }
728 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
729 if (formatters == NULL) {
730 formatters = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*));
731 formatters[UTMUTFMT_FULL_STYLE] = NULL;
732 formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL;
733 countToPatterns->put(srcPluralCount, formatters, err);
734 if (U_FAILURE(err)) {
735 uprv_free(formatters);
736 delete messageFormat;
737 }
738 }
739 if (U_SUCCESS(err)) {
740 //delete formatters[style];
741 formatters[style] = messageFormat;
742 }
743 } else {
744 delete messageFormat;
745 }
746 } else {
747 // fall back to rule "other", and search in parents
748 searchInLocaleChain(style, key, localeName, srcTimeUnitField, srcPluralCount,
749 gPluralCountOther, countToPatterns, err);
750 }
751 }
752
753 void
setLocale(const Locale & locale,UErrorCode & status)754 TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) {
755 if (U_SUCCESS(status) && fLocale != locale) {
756 fLocale = locale;
757 setup(status);
758 }
759 }
760
761
762 void
setNumberFormat(const NumberFormat & format,UErrorCode & status)763 TimeUnitFormat::setNumberFormat(const NumberFormat& format, UErrorCode& status){
764 if (U_FAILURE(status) || (fNumberFormat && format == *fNumberFormat)) {
765 return;
766 }
767 delete fNumberFormat;
768 fNumberFormat = (NumberFormat*)format.clone();
769 // reset the number formatter in the fTimeUnitToCountToPatterns map
770 for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
771 i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
772 i = (TimeUnit::UTimeUnitFields)(i+1)) {
773 int32_t pos = -1;
774 const UHashElement* elem = NULL;
775 while ((elem = fTimeUnitToCountToPatterns[i]->nextElement(pos)) != NULL){
776 const UHashTok keyTok = elem->value;
777 MessageFormat** pattern = (MessageFormat**)keyTok.pointer;
778
779 pattern[UTMUTFMT_FULL_STYLE]->setFormat(0, format);
780 pattern[UTMUTFMT_ABBREVIATED_STYLE]->setFormat(0, format);
781 }
782 }
783 }
784
785
786 void
deleteHash(Hashtable * htable)787 TimeUnitFormat::deleteHash(Hashtable* htable) {
788 int32_t pos = -1;
789 const UHashElement* element = NULL;
790 if ( htable ) {
791 while ( (element = htable->nextElement(pos)) != NULL ) {
792 const UHashTok valueTok = element->value;
793 const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
794 delete value[UTMUTFMT_FULL_STYLE];
795 delete value[UTMUTFMT_ABBREVIATED_STYLE];
796 //delete[] value;
797 uprv_free(value);
798 }
799 }
800 delete htable;
801 }
802
803
804 void
copyHash(const Hashtable * source,Hashtable * target,UErrorCode & status)805 TimeUnitFormat::copyHash(const Hashtable* source, Hashtable* target, UErrorCode& status) {
806 if ( U_FAILURE(status) ) {
807 return;
808 }
809 int32_t pos = -1;
810 const UHashElement* element = NULL;
811 if ( source ) {
812 while ( (element = source->nextElement(pos)) != NULL ) {
813 const UHashTok keyTok = element->key;
814 const UnicodeString* key = (UnicodeString*)keyTok.pointer;
815 const UHashTok valueTok = element->value;
816 const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
817 MessageFormat** newVal = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*));
818 newVal[0] = (MessageFormat*)value[0]->clone();
819 newVal[1] = (MessageFormat*)value[1]->clone();
820 target->put(UnicodeString(*key), newVal, status);
821 if ( U_FAILURE(status) ) {
822 delete newVal[0];
823 delete newVal[1];
824 uprv_free(newVal);
825 return;
826 }
827 }
828 }
829 }
830
831
832 U_CDECL_BEGIN
833
834 /**
835 * set hash table value comparator
836 *
837 * @param val1 one value in comparison
838 * @param val2 the other value in comparison
839 * @return TRUE if 2 values are the same, FALSE otherwise
840 */
841 static UBool U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2);
842
843 static UBool
tmutfmtHashTableValueComparator(UHashTok val1,UHashTok val2)844 U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2) {
845 const MessageFormat** pattern1 = (const MessageFormat**)val1.pointer;
846 const MessageFormat** pattern2 = (const MessageFormat**)val2.pointer;
847 return *pattern1[0] == *pattern2[0] && *pattern1[1] == *pattern2[1];
848 }
849
850 U_CDECL_END
851
852 Hashtable*
initHash(UErrorCode & status)853 TimeUnitFormat::initHash(UErrorCode& status) {
854 if ( U_FAILURE(status) ) {
855 return NULL;
856 }
857 Hashtable* hTable;
858 if ( (hTable = new Hashtable(TRUE, status)) == NULL ) {
859 status = U_MEMORY_ALLOCATION_ERROR;
860 return NULL;
861 }
862 if ( U_FAILURE(status) ) {
863 delete hTable;
864 return NULL;
865 }
866 hTable->setValueComparator(tmutfmtHashTableValueComparator);
867 return hTable;
868 }
869
870
871 const char*
getTimeUnitName(TimeUnit::UTimeUnitFields unitField,UErrorCode & status)872 TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField,
873 UErrorCode& status) {
874 if (U_FAILURE(status)) {
875 return NULL;
876 }
877 switch (unitField) {
878 case TimeUnit::UTIMEUNIT_YEAR:
879 return gTimeUnitYear;
880 case TimeUnit::UTIMEUNIT_MONTH:
881 return gTimeUnitMonth;
882 case TimeUnit::UTIMEUNIT_DAY:
883 return gTimeUnitDay;
884 case TimeUnit::UTIMEUNIT_WEEK:
885 return gTimeUnitWeek;
886 case TimeUnit::UTIMEUNIT_HOUR:
887 return gTimeUnitHour;
888 case TimeUnit::UTIMEUNIT_MINUTE:
889 return gTimeUnitMinute;
890 case TimeUnit::UTIMEUNIT_SECOND:
891 return gTimeUnitSecond;
892 default:
893 status = U_ILLEGAL_ARGUMENT_ERROR;
894 return NULL;
895 }
896 }
897
898 U_NAMESPACE_END
899
900 #endif
901