1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/tmutfmt.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,900 @@ 1.4 +/* 1.5 + ******************************************************************************* 1.6 + * Copyright (C) 2008-2013, Google, International Business Machines Corporation 1.7 + * and others. All Rights Reserved. 1.8 + ******************************************************************************* 1.9 + */ 1.10 + 1.11 +#include "utypeinfo.h" // for 'typeid' to work 1.12 + 1.13 +#include "unicode/tmutfmt.h" 1.14 + 1.15 +#if !UCONFIG_NO_FORMATTING 1.16 + 1.17 +#include "uvector.h" 1.18 +#include "charstr.h" 1.19 +#include "cmemory.h" 1.20 +#include "cstring.h" 1.21 +#include "hash.h" 1.22 +#include "uresimp.h" 1.23 +#include "unicode/msgfmt.h" 1.24 +#include "uassert.h" 1.25 + 1.26 +#define LEFT_CURLY_BRACKET ((UChar)0x007B) 1.27 +#define RIGHT_CURLY_BRACKET ((UChar)0x007D) 1.28 +#define SPACE ((UChar)0x0020) 1.29 +#define DIGIT_ZERO ((UChar)0x0030) 1.30 +#define LOW_S ((UChar)0x0073) 1.31 +#define LOW_M ((UChar)0x006D) 1.32 +#define LOW_I ((UChar)0x0069) 1.33 +#define LOW_N ((UChar)0x006E) 1.34 +#define LOW_H ((UChar)0x0068) 1.35 +#define LOW_W ((UChar)0x0077) 1.36 +#define LOW_D ((UChar)0x0064) 1.37 +#define LOW_Y ((UChar)0x0079) 1.38 +#define LOW_Z ((UChar)0x007A) 1.39 +#define LOW_E ((UChar)0x0065) 1.40 +#define LOW_R ((UChar)0x0072) 1.41 +#define LOW_O ((UChar)0x006F) 1.42 +#define LOW_N ((UChar)0x006E) 1.43 +#define LOW_T ((UChar)0x0074) 1.44 + 1.45 + 1.46 +//TODO: define in compile time 1.47 +//#define TMUTFMT_DEBUG 1 1.48 + 1.49 +#ifdef TMUTFMT_DEBUG 1.50 +#include <iostream> 1.51 +#endif 1.52 + 1.53 +U_NAMESPACE_BEGIN 1.54 + 1.55 + 1.56 + 1.57 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat) 1.58 + 1.59 +static const char gUnitsTag[] = "units"; 1.60 +static const char gShortUnitsTag[] = "unitsShort"; 1.61 +static const char gTimeUnitYear[] = "year"; 1.62 +static const char gTimeUnitMonth[] = "month"; 1.63 +static const char gTimeUnitDay[] = "day"; 1.64 +static const char gTimeUnitWeek[] = "week"; 1.65 +static const char gTimeUnitHour[] = "hour"; 1.66 +static const char gTimeUnitMinute[] = "minute"; 1.67 +static const char gTimeUnitSecond[] = "second"; 1.68 +static const char gPluralCountOther[] = "other"; 1.69 + 1.70 +static const UChar DEFAULT_PATTERN_FOR_SECOND[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_S, 0}; 1.71 +static const UChar DEFAULT_PATTERN_FOR_MINUTE[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, LOW_I, LOW_N, 0}; 1.72 +static const UChar DEFAULT_PATTERN_FOR_HOUR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_H, 0}; 1.73 +static const UChar DEFAULT_PATTERN_FOR_WEEK[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_W, 0}; 1.74 +static const UChar DEFAULT_PATTERN_FOR_DAY[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_D, 0}; 1.75 +static const UChar DEFAULT_PATTERN_FOR_MONTH[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, 0}; 1.76 +static const UChar DEFAULT_PATTERN_FOR_YEAR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_Y, 0}; 1.77 + 1.78 +static const UChar PLURAL_COUNT_ZERO[] = {LOW_Z, LOW_E, LOW_R, LOW_O, 0}; 1.79 +static const UChar PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0}; 1.80 +static const UChar PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0}; 1.81 + 1.82 +TimeUnitFormat::TimeUnitFormat(UErrorCode& status) 1.83 +: fNumberFormat(NULL), 1.84 + fPluralRules(NULL) { 1.85 + create(Locale::getDefault(), UTMUTFMT_FULL_STYLE, status); 1.86 +} 1.87 + 1.88 + 1.89 +TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) 1.90 +: fNumberFormat(NULL), 1.91 + fPluralRules(NULL) { 1.92 + create(locale, UTMUTFMT_FULL_STYLE, status); 1.93 +} 1.94 + 1.95 + 1.96 +TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) 1.97 +: fNumberFormat(NULL), 1.98 + fPluralRules(NULL) { 1.99 + create(locale, style, status); 1.100 +} 1.101 + 1.102 + 1.103 +TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other) 1.104 +: MeasureFormat(other), 1.105 + fNumberFormat(NULL), 1.106 + fPluralRules(NULL), 1.107 + fStyle(UTMUTFMT_FULL_STYLE) 1.108 +{ 1.109 + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 1.110 + i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 1.111 + i = (TimeUnit::UTimeUnitFields)(i+1)) { 1.112 + fTimeUnitToCountToPatterns[i] = NULL; 1.113 + } 1.114 + *this = other; 1.115 +} 1.116 + 1.117 + 1.118 +TimeUnitFormat::~TimeUnitFormat() { 1.119 + delete fNumberFormat; 1.120 + fNumberFormat = NULL; 1.121 + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 1.122 + i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 1.123 + i = (TimeUnit::UTimeUnitFields)(i+1)) { 1.124 + deleteHash(fTimeUnitToCountToPatterns[i]); 1.125 + fTimeUnitToCountToPatterns[i] = NULL; 1.126 + } 1.127 + delete fPluralRules; 1.128 + fPluralRules = NULL; 1.129 +} 1.130 + 1.131 + 1.132 +Format* 1.133 +TimeUnitFormat::clone(void) const { 1.134 + return new TimeUnitFormat(*this); 1.135 +} 1.136 + 1.137 + 1.138 +TimeUnitFormat& 1.139 +TimeUnitFormat::operator=(const TimeUnitFormat& other) { 1.140 + if (this == &other) { 1.141 + return *this; 1.142 + } 1.143 + delete fNumberFormat; 1.144 + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 1.145 + i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 1.146 + i = (TimeUnit::UTimeUnitFields)(i+1)) { 1.147 + deleteHash(fTimeUnitToCountToPatterns[i]); 1.148 + fTimeUnitToCountToPatterns[i] = NULL; 1.149 + } 1.150 + delete fPluralRules; 1.151 + if (other.fNumberFormat) { 1.152 + fNumberFormat = (NumberFormat*)other.fNumberFormat->clone(); 1.153 + } else { 1.154 + fNumberFormat = NULL; 1.155 + } 1.156 + fLocale = other.fLocale; 1.157 + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 1.158 + i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 1.159 + i = (TimeUnit::UTimeUnitFields)(i+1)) { 1.160 + UErrorCode status = U_ZERO_ERROR; 1.161 + fTimeUnitToCountToPatterns[i] = initHash(status); 1.162 + if (U_SUCCESS(status)) { 1.163 + copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status); 1.164 + } else { 1.165 + delete fTimeUnitToCountToPatterns[i]; 1.166 + fTimeUnitToCountToPatterns[i] = NULL; 1.167 + } 1.168 + } 1.169 + if (other.fPluralRules) { 1.170 + fPluralRules = (PluralRules*)other.fPluralRules->clone(); 1.171 + } else { 1.172 + fPluralRules = NULL; 1.173 + } 1.174 + fStyle = other.fStyle; 1.175 + return *this; 1.176 +} 1.177 + 1.178 + 1.179 +UBool 1.180 +TimeUnitFormat::operator==(const Format& other) const { 1.181 + if (typeid(*this) == typeid(other)) { 1.182 + TimeUnitFormat* fmt = (TimeUnitFormat*)&other; 1.183 + UBool ret = ( ((fNumberFormat && fmt->fNumberFormat && *fNumberFormat == *fmt->fNumberFormat) 1.184 + || fNumberFormat == fmt->fNumberFormat ) 1.185 + && fLocale == fmt->fLocale 1.186 + && ((fPluralRules && fmt->fPluralRules && *fPluralRules == *fmt->fPluralRules) 1.187 + || fPluralRules == fmt->fPluralRules) 1.188 + && fStyle == fmt->fStyle); 1.189 + if (ret) { 1.190 + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 1.191 + i < TimeUnit::UTIMEUNIT_FIELD_COUNT && ret; 1.192 + i = (TimeUnit::UTimeUnitFields)(i+1)) { 1.193 + ret = fTimeUnitToCountToPatterns[i]->equals(*(fmt->fTimeUnitToCountToPatterns[i])); 1.194 + } 1.195 + } 1.196 + return ret; 1.197 + } 1.198 + return false; 1.199 +} 1.200 + 1.201 + 1.202 +UnicodeString& 1.203 +TimeUnitFormat::format(const Formattable& obj, UnicodeString& toAppendTo, 1.204 + FieldPosition& pos, UErrorCode& status) const { 1.205 + if (U_FAILURE(status)) { 1.206 + return toAppendTo; 1.207 + } 1.208 + if (obj.getType() == Formattable::kObject) { 1.209 + const UObject* formatObj = obj.getObject(); 1.210 + const TimeUnitAmount* amount = dynamic_cast<const TimeUnitAmount*>(formatObj); 1.211 + if (amount != NULL){ 1.212 + Hashtable* countToPattern = fTimeUnitToCountToPatterns[amount->getTimeUnitField()]; 1.213 + double number; 1.214 + const Formattable& amtNumber = amount->getNumber(); 1.215 + if (amtNumber.getType() == Formattable::kDouble) { 1.216 + number = amtNumber.getDouble(); 1.217 + } else if (amtNumber.getType() == Formattable::kLong) { 1.218 + number = amtNumber.getLong(); 1.219 + } else { 1.220 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.221 + return toAppendTo; 1.222 + } 1.223 + UnicodeString count = fPluralRules->select(number); 1.224 +#ifdef TMUTFMT_DEBUG 1.225 + char result[1000]; 1.226 + count.extract(0, count.length(), result, "UTF-8"); 1.227 + std::cout << "number: " << number << "; format plural count: " << result << "\n"; 1.228 +#endif 1.229 + MessageFormat* pattern = ((MessageFormat**)countToPattern->get(count))[fStyle]; 1.230 + Formattable formattable[1]; 1.231 + formattable[0].setDouble(number); 1.232 + return pattern->format(formattable, 1, toAppendTo, pos, status); 1.233 + } 1.234 + } 1.235 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.236 + return toAppendTo; 1.237 +} 1.238 + 1.239 + 1.240 +void 1.241 +TimeUnitFormat::parseObject(const UnicodeString& source, 1.242 + Formattable& result, 1.243 + ParsePosition& pos) const { 1.244 + double resultNumber = -1; 1.245 + UBool withNumberFormat = false; 1.246 + TimeUnit::UTimeUnitFields resultTimeUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT; 1.247 + int32_t oldPos = pos.getIndex(); 1.248 + int32_t newPos = -1; 1.249 + int32_t longestParseDistance = 0; 1.250 + UnicodeString* countOfLongestMatch = NULL; 1.251 +#ifdef TMUTFMT_DEBUG 1.252 + char res[1000]; 1.253 + source.extract(0, source.length(), res, "UTF-8"); 1.254 + std::cout << "parse source: " << res << "\n"; 1.255 +#endif 1.256 + // parse by iterating through all available patterns 1.257 + // and looking for the longest match. 1.258 + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 1.259 + i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 1.260 + i = (TimeUnit::UTimeUnitFields)(i+1)) { 1.261 + Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i]; 1.262 + int32_t elemPos = -1; 1.263 + const UHashElement* elem = NULL; 1.264 + while ((elem = countToPatterns->nextElement(elemPos)) != NULL){ 1.265 + const UHashTok keyTok = elem->key; 1.266 + UnicodeString* count = (UnicodeString*)keyTok.pointer; 1.267 +#ifdef TMUTFMT_DEBUG 1.268 + count->extract(0, count->length(), res, "UTF-8"); 1.269 + std::cout << "parse plural count: " << res << "\n"; 1.270 +#endif 1.271 + const UHashTok valueTok = elem->value; 1.272 + // the value is a pair of MessageFormat* 1.273 + MessageFormat** patterns = (MessageFormat**)valueTok.pointer; 1.274 + for (UTimeUnitFormatStyle style = UTMUTFMT_FULL_STYLE; style < UTMUTFMT_FORMAT_STYLE_COUNT; 1.275 + style = (UTimeUnitFormatStyle)(style + 1)) { 1.276 + MessageFormat* pattern = patterns[style]; 1.277 + pos.setErrorIndex(-1); 1.278 + pos.setIndex(oldPos); 1.279 + // see if we can parse 1.280 + Formattable parsed; 1.281 + pattern->parseObject(source, parsed, pos); 1.282 + if (pos.getErrorIndex() != -1 || pos.getIndex() == oldPos) { 1.283 + continue; 1.284 + } 1.285 + #ifdef TMUTFMT_DEBUG 1.286 + std::cout << "parsed.getType: " << parsed.getType() << "\n"; 1.287 + #endif 1.288 + double tmpNumber = 0; 1.289 + if (pattern->getArgTypeCount() != 0) { 1.290 + // pattern with Number as beginning, such as "{0} d". 1.291 + // check to make sure that the timeUnit is consistent 1.292 + Formattable& temp = parsed[0]; 1.293 + if (temp.getType() == Formattable::kDouble) { 1.294 + tmpNumber = temp.getDouble(); 1.295 + } else if (temp.getType() == Formattable::kLong) { 1.296 + tmpNumber = temp.getLong(); 1.297 + } else { 1.298 + continue; 1.299 + } 1.300 + UnicodeString select = fPluralRules->select(tmpNumber); 1.301 + #ifdef TMUTFMT_DEBUG 1.302 + select.extract(0, select.length(), res, "UTF-8"); 1.303 + std::cout << "parse plural select count: " << res << "\n"; 1.304 + #endif 1.305 + if (*count != select) { 1.306 + continue; 1.307 + } 1.308 + } 1.309 + int32_t parseDistance = pos.getIndex() - oldPos; 1.310 + if (parseDistance > longestParseDistance) { 1.311 + if (pattern->getArgTypeCount() != 0) { 1.312 + resultNumber = tmpNumber; 1.313 + withNumberFormat = true; 1.314 + } else { 1.315 + withNumberFormat = false; 1.316 + } 1.317 + resultTimeUnit = i; 1.318 + newPos = pos.getIndex(); 1.319 + longestParseDistance = parseDistance; 1.320 + countOfLongestMatch = count; 1.321 + } 1.322 + } 1.323 + } 1.324 + } 1.325 + /* After find the longest match, parse the number. 1.326 + * Result number could be null for the pattern without number pattern. 1.327 + * such as unit pattern in Arabic. 1.328 + * When result number is null, use plural rule to set the number. 1.329 + */ 1.330 + if (withNumberFormat == false && longestParseDistance != 0) { 1.331 + // set the number using plurrual count 1.332 + if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ZERO, 4)) { 1.333 + resultNumber = 0; 1.334 + } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ONE, 3)) { 1.335 + resultNumber = 1; 1.336 + } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_TWO, 3)) { 1.337 + resultNumber = 2; 1.338 + } else { 1.339 + // should not happen. 1.340 + // TODO: how to handle? 1.341 + resultNumber = 3; 1.342 + } 1.343 + } 1.344 + if (longestParseDistance == 0) { 1.345 + pos.setIndex(oldPos); 1.346 + pos.setErrorIndex(0); 1.347 + } else { 1.348 + UErrorCode status = U_ZERO_ERROR; 1.349 + TimeUnitAmount* tmutamt = new TimeUnitAmount(resultNumber, resultTimeUnit, status); 1.350 + if (U_SUCCESS(status)) { 1.351 + result.adoptObject(tmutamt); 1.352 + pos.setIndex(newPos); 1.353 + pos.setErrorIndex(-1); 1.354 + } else { 1.355 + pos.setIndex(oldPos); 1.356 + pos.setErrorIndex(0); 1.357 + } 1.358 + } 1.359 +} 1.360 + 1.361 +void 1.362 +TimeUnitFormat::create(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) { 1.363 + if (U_FAILURE(status)) { 1.364 + return; 1.365 + } 1.366 + if (style < UTMUTFMT_FULL_STYLE || style >= UTMUTFMT_FORMAT_STYLE_COUNT) { 1.367 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.368 + return; 1.369 + } 1.370 + fStyle = style; 1.371 + fLocale = locale; 1.372 + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 1.373 + i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 1.374 + i = (TimeUnit::UTimeUnitFields)(i+1)) { 1.375 + fTimeUnitToCountToPatterns[i] = NULL; 1.376 + } 1.377 + //TODO: format() and parseObj() are const member functions, 1.378 + //so, can not do lazy initialization in C++. 1.379 + //setup has to be done in constructors. 1.380 + //and here, the behavior is not consistent with Java. 1.381 + //In Java, create an empty instance does not setup locale as 1.382 + //default locale. If it followed by setNumberFormat(), 1.383 + //in format(), the locale will set up as the locale in fNumberFormat. 1.384 + //But in C++, this sets the locale as the default locale. 1.385 + setup(status); 1.386 +} 1.387 + 1.388 +void 1.389 +TimeUnitFormat::setup(UErrorCode& err) { 1.390 + initDataMembers(err); 1.391 + 1.392 + UVector pluralCounts(0, uhash_compareUnicodeString, 6, err); 1.393 + StringEnumeration* keywords = fPluralRules->getKeywords(err); 1.394 + if (U_FAILURE(err)) { 1.395 + return; 1.396 + } 1.397 + UnicodeString* pluralCount; 1.398 + while ((pluralCount = const_cast<UnicodeString*>(keywords->snext(err))) != NULL) { 1.399 + pluralCounts.addElement(pluralCount, err); 1.400 + } 1.401 + readFromCurrentLocale(UTMUTFMT_FULL_STYLE, gUnitsTag, pluralCounts, err); 1.402 + checkConsistency(UTMUTFMT_FULL_STYLE, gUnitsTag, err); 1.403 + readFromCurrentLocale(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, pluralCounts, err); 1.404 + checkConsistency(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, err); 1.405 + delete keywords; 1.406 +} 1.407 + 1.408 + 1.409 +void 1.410 +TimeUnitFormat::initDataMembers(UErrorCode& err){ 1.411 + if (U_FAILURE(err)) { 1.412 + return; 1.413 + } 1.414 + if (fNumberFormat == NULL) { 1.415 + fNumberFormat = NumberFormat::createInstance(fLocale, err); 1.416 + } 1.417 + delete fPluralRules; 1.418 + fPluralRules = PluralRules::forLocale(fLocale, err); 1.419 + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 1.420 + i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 1.421 + i = (TimeUnit::UTimeUnitFields)(i+1)) { 1.422 + deleteHash(fTimeUnitToCountToPatterns[i]); 1.423 + fTimeUnitToCountToPatterns[i] = NULL; 1.424 + } 1.425 +} 1.426 + 1.427 +void 1.428 +TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key, 1.429 + const UVector& pluralCounts, UErrorCode& err) { 1.430 + if (U_FAILURE(err)) { 1.431 + return; 1.432 + } 1.433 + // fill timeUnitToCountToPatterns from resource file 1.434 + // err is used to indicate wrong status except missing resource. 1.435 + // status is an error code used in resource lookup. 1.436 + // status does not affect "err". 1.437 + UErrorCode status = U_ZERO_ERROR; 1.438 + UResourceBundle *rb, *unitsRes; 1.439 + rb = ures_open(NULL, fLocale.getName(), &status); 1.440 + unitsRes = ures_getByKey(rb, key, NULL, &status); 1.441 + unitsRes = ures_getByKey(unitsRes, "duration", unitsRes, &status); 1.442 + if (U_FAILURE(status)) { 1.443 + ures_close(unitsRes); 1.444 + ures_close(rb); 1.445 + return; 1.446 + } 1.447 + int32_t size = ures_getSize(unitsRes); 1.448 + for ( int32_t index = 0; index < size; ++index) { 1.449 + // resource of one time unit 1.450 + UResourceBundle* oneTimeUnit = ures_getByIndex(unitsRes, index, 1.451 + NULL, &status); 1.452 + if (U_SUCCESS(status)) { 1.453 + const char* timeUnitName = ures_getKey(oneTimeUnit); 1.454 + if (timeUnitName == NULL) { 1.455 + ures_close(oneTimeUnit); 1.456 + continue; 1.457 + } 1.458 + UResourceBundle* countsToPatternRB = ures_getByKey(unitsRes, 1.459 + timeUnitName, 1.460 + NULL, &status); 1.461 + if (countsToPatternRB == NULL || U_FAILURE(status)) { 1.462 + ures_close(countsToPatternRB); 1.463 + ures_close(oneTimeUnit); 1.464 + continue; 1.465 + } 1.466 + TimeUnit::UTimeUnitFields timeUnitField = TimeUnit::UTIMEUNIT_FIELD_COUNT; 1.467 + if ( uprv_strcmp(timeUnitName, gTimeUnitYear) == 0 ) { 1.468 + timeUnitField = TimeUnit::UTIMEUNIT_YEAR; 1.469 + } else if ( uprv_strcmp(timeUnitName, gTimeUnitMonth) == 0 ) { 1.470 + timeUnitField = TimeUnit::UTIMEUNIT_MONTH; 1.471 + } else if ( uprv_strcmp(timeUnitName, gTimeUnitDay) == 0 ) { 1.472 + timeUnitField = TimeUnit::UTIMEUNIT_DAY; 1.473 + } else if ( uprv_strcmp(timeUnitName, gTimeUnitHour) == 0 ) { 1.474 + timeUnitField = TimeUnit::UTIMEUNIT_HOUR; 1.475 + } else if ( uprv_strcmp(timeUnitName, gTimeUnitMinute) == 0 ) { 1.476 + timeUnitField = TimeUnit::UTIMEUNIT_MINUTE; 1.477 + } else if ( uprv_strcmp(timeUnitName, gTimeUnitSecond) == 0 ) { 1.478 + timeUnitField = TimeUnit::UTIMEUNIT_SECOND; 1.479 + } else if ( uprv_strcmp(timeUnitName, gTimeUnitWeek) == 0 ) { 1.480 + timeUnitField = TimeUnit::UTIMEUNIT_WEEK; 1.481 + } else { 1.482 + ures_close(countsToPatternRB); 1.483 + ures_close(oneTimeUnit); 1.484 + continue; 1.485 + } 1.486 + Hashtable* countToPatterns = fTimeUnitToCountToPatterns[timeUnitField]; 1.487 + if (countToPatterns == NULL) { 1.488 + countToPatterns = initHash(err); 1.489 + if (U_FAILURE(err)) { 1.490 + ures_close(countsToPatternRB); 1.491 + ures_close(oneTimeUnit); 1.492 + delete countToPatterns; 1.493 + break; 1.494 + } 1.495 + } 1.496 + int32_t count = ures_getSize(countsToPatternRB); 1.497 + const char* pluralCount; 1.498 + for ( int32_t pluralIndex = 0; pluralIndex < count; ++pluralIndex) { 1.499 + // resource of count to pattern 1.500 + UnicodeString pattern = 1.501 + ures_getNextUnicodeString(countsToPatternRB, &pluralCount, &status); 1.502 + if (U_FAILURE(status)) { 1.503 + continue; 1.504 + } 1.505 + UnicodeString pluralCountUniStr(pluralCount, -1, US_INV); 1.506 + if (!pluralCounts.contains(&pluralCountUniStr)) { 1.507 + continue; 1.508 + } 1.509 + MessageFormat* messageFormat = new MessageFormat(pattern, fLocale, err); 1.510 + if ( U_SUCCESS(err) ) { 1.511 + if (fNumberFormat != NULL) { 1.512 + messageFormat->setFormat(0, *fNumberFormat); 1.513 + } 1.514 + MessageFormat** formatters = (MessageFormat**)countToPatterns->get(pluralCountUniStr); 1.515 + if (formatters == NULL) { 1.516 + formatters = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)); 1.517 + formatters[UTMUTFMT_FULL_STYLE] = NULL; 1.518 + formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL; 1.519 + countToPatterns->put(pluralCountUniStr, formatters, err); 1.520 + if (U_FAILURE(err)) { 1.521 + uprv_free(formatters); 1.522 + } 1.523 + } 1.524 + if (U_SUCCESS(err)) { 1.525 + //delete formatters[style]; 1.526 + formatters[style] = messageFormat; 1.527 + } 1.528 + } 1.529 + if (U_FAILURE(err)) { 1.530 + ures_close(countsToPatternRB); 1.531 + ures_close(oneTimeUnit); 1.532 + ures_close(unitsRes); 1.533 + ures_close(rb); 1.534 + delete messageFormat; 1.535 + delete countToPatterns; 1.536 + return; 1.537 + } 1.538 + } 1.539 + if (fTimeUnitToCountToPatterns[timeUnitField] == NULL) { 1.540 + fTimeUnitToCountToPatterns[timeUnitField] = countToPatterns; 1.541 + } 1.542 + ures_close(countsToPatternRB); 1.543 + } 1.544 + ures_close(oneTimeUnit); 1.545 + } 1.546 + ures_close(unitsRes); 1.547 + ures_close(rb); 1.548 +} 1.549 + 1.550 + 1.551 +void 1.552 +TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style, const char* key, UErrorCode& err) { 1.553 + if (U_FAILURE(err)) { 1.554 + return; 1.555 + } 1.556 + // there should be patterns for each plural rule in each time unit. 1.557 + // For each time unit, 1.558 + // for each plural rule, following is unit pattern fall-back rule: 1.559 + // ( for example: "one" hour ) 1.560 + // look for its unit pattern in its locale tree. 1.561 + // if pattern is not found in its own locale, such as de_DE, 1.562 + // look for the pattern in its parent, such as de, 1.563 + // keep looking till found or till root. 1.564 + // if the pattern is not found in root either, 1.565 + // fallback to plural count "other", 1.566 + // look for the pattern of "other" in the locale tree: 1.567 + // "de_DE" to "de" to "root". 1.568 + // If not found, fall back to value of 1.569 + // static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h". 1.570 + // 1.571 + // Following is consistency check to create pattern for each 1.572 + // plural rule in each time unit using above fall-back rule. 1.573 + // 1.574 + StringEnumeration* keywords = fPluralRules->getKeywords(err); 1.575 + if (U_SUCCESS(err)) { 1.576 + const UnicodeString* pluralCount; 1.577 + while ((pluralCount = keywords->snext(err)) != NULL) { 1.578 + if ( U_SUCCESS(err) ) { 1.579 + for (int32_t i = 0; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; ++i) { 1.580 + // for each time unit, 1.581 + // get all the patterns for each plural rule in this locale. 1.582 + Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i]; 1.583 + if ( countToPatterns == NULL ) { 1.584 + countToPatterns = initHash(err); 1.585 + if (U_FAILURE(err)) { 1.586 + delete countToPatterns; 1.587 + return; 1.588 + } 1.589 + fTimeUnitToCountToPatterns[i] = countToPatterns; 1.590 + } 1.591 + MessageFormat** formatters = (MessageFormat**)countToPatterns->get(*pluralCount); 1.592 + if( formatters == NULL || formatters[style] == NULL ) { 1.593 + // look through parents 1.594 + const char* localeName = fLocale.getName(); 1.595 + CharString pluralCountChars; 1.596 + pluralCountChars.appendInvariantChars(*pluralCount, err); 1.597 + searchInLocaleChain(style, key, localeName, 1.598 + (TimeUnit::UTimeUnitFields)i, 1.599 + *pluralCount, pluralCountChars.data(), 1.600 + countToPatterns, err); 1.601 + } 1.602 + } 1.603 + } 1.604 + } 1.605 + } 1.606 + delete keywords; 1.607 +} 1.608 + 1.609 + 1.610 + 1.611 +// srcPluralCount is the original plural count on which the pattern is 1.612 +// searched for. 1.613 +// searchPluralCount is the fallback plural count. 1.614 +// For example, to search for pattern for ""one" hour", 1.615 +// "one" is the srcPluralCount, 1.616 +// if the pattern is not found even in root, fallback to 1.617 +// using patterns of plural count "other", 1.618 +// then, "other" is the searchPluralCount. 1.619 +void 1.620 +TimeUnitFormat::searchInLocaleChain(UTimeUnitFormatStyle style, const char* key, const char* localeName, 1.621 + TimeUnit::UTimeUnitFields srcTimeUnitField, 1.622 + const UnicodeString& srcPluralCount, 1.623 + const char* searchPluralCount, 1.624 + Hashtable* countToPatterns, 1.625 + UErrorCode& err) { 1.626 + if (U_FAILURE(err)) { 1.627 + return; 1.628 + } 1.629 + UErrorCode status = U_ZERO_ERROR; 1.630 + char parentLocale[ULOC_FULLNAME_CAPACITY]; 1.631 + uprv_strcpy(parentLocale, localeName); 1.632 + int32_t locNameLen; 1.633 + U_ASSERT(countToPatterns != NULL); 1.634 + while ((locNameLen = uloc_getParent(parentLocale, parentLocale, 1.635 + ULOC_FULLNAME_CAPACITY, &status)) >= 0){ 1.636 + // look for pattern for srcPluralCount in locale tree 1.637 + UResourceBundle *rb, *unitsRes, *countsToPatternRB; 1.638 + rb = ures_open(NULL, parentLocale, &status); 1.639 + unitsRes = ures_getByKey(rb, key, NULL, &status); 1.640 + const char* timeUnitName = getTimeUnitName(srcTimeUnitField, status); 1.641 + countsToPatternRB = ures_getByKey(unitsRes, timeUnitName, NULL, &status); 1.642 + const UChar* pattern; 1.643 + int32_t ptLength; 1.644 + pattern = ures_getStringByKeyWithFallback(countsToPatternRB, searchPluralCount, &ptLength, &status); 1.645 + if (U_SUCCESS(status)) { 1.646 + //found 1.647 + MessageFormat* messageFormat = new MessageFormat(UnicodeString(TRUE, pattern, ptLength), fLocale, err); 1.648 + if (U_SUCCESS(err)) { 1.649 + if (fNumberFormat != NULL) { 1.650 + messageFormat->setFormat(0, *fNumberFormat); 1.651 + } 1.652 + MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount); 1.653 + if (formatters == NULL) { 1.654 + formatters = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)); 1.655 + formatters[UTMUTFMT_FULL_STYLE] = NULL; 1.656 + formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL; 1.657 + countToPatterns->put(srcPluralCount, formatters, err); 1.658 + if (U_FAILURE(err)) { 1.659 + uprv_free(formatters); 1.660 + delete messageFormat; 1.661 + } 1.662 + } 1.663 + if (U_SUCCESS(err)) { 1.664 + //delete formatters[style]; 1.665 + formatters[style] = messageFormat; 1.666 + } 1.667 + } else { 1.668 + delete messageFormat; 1.669 + } 1.670 + ures_close(countsToPatternRB); 1.671 + ures_close(unitsRes); 1.672 + ures_close(rb); 1.673 + return; 1.674 + } 1.675 + ures_close(countsToPatternRB); 1.676 + ures_close(unitsRes); 1.677 + ures_close(rb); 1.678 + status = U_ZERO_ERROR; 1.679 + if ( locNameLen ==0 ) { 1.680 + break; 1.681 + } 1.682 + } 1.683 + 1.684 + // if no unitsShort resource was found even after fallback to root locale 1.685 + // then search the units resource fallback from the current level to root 1.686 + if ( locNameLen == 0 && uprv_strcmp(key, gShortUnitsTag) == 0) { 1.687 +#ifdef TMUTFMT_DEBUG 1.688 + std::cout << "loop into searchInLocaleChain since Short-Long-Alternative \n"; 1.689 +#endif 1.690 + char pLocale[ULOC_FULLNAME_CAPACITY]; 1.691 + uprv_strcpy(pLocale, localeName); 1.692 + // Add an underscore at the tail of locale name, 1.693 + // so that searchInLocaleChain will check the current locale before falling back 1.694 + uprv_strcat(pLocale, "_"); 1.695 + searchInLocaleChain(style, gUnitsTag, pLocale, srcTimeUnitField, srcPluralCount, 1.696 + searchPluralCount, countToPatterns, err); 1.697 + MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount); 1.698 + if (formatters != NULL && formatters[style] != NULL) { 1.699 + return; 1.700 + } 1.701 + } 1.702 + 1.703 + // if not found the pattern for this plural count at all, 1.704 + // fall-back to plural count "other" 1.705 + if ( uprv_strcmp(searchPluralCount, gPluralCountOther) == 0 ) { 1.706 + // set default fall back the same as the resource in root 1.707 + MessageFormat* messageFormat = NULL; 1.708 + const UChar *pattern = NULL; 1.709 + if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_SECOND ) { 1.710 + pattern = DEFAULT_PATTERN_FOR_SECOND; 1.711 + } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MINUTE ) { 1.712 + pattern = DEFAULT_PATTERN_FOR_MINUTE; 1.713 + } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_HOUR ) { 1.714 + pattern = DEFAULT_PATTERN_FOR_HOUR; 1.715 + } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_WEEK ) { 1.716 + pattern = DEFAULT_PATTERN_FOR_WEEK; 1.717 + } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_DAY ) { 1.718 + pattern = DEFAULT_PATTERN_FOR_DAY; 1.719 + } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MONTH ) { 1.720 + pattern = DEFAULT_PATTERN_FOR_MONTH; 1.721 + } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_YEAR ) { 1.722 + pattern = DEFAULT_PATTERN_FOR_YEAR; 1.723 + } 1.724 + if (pattern != NULL) { 1.725 + messageFormat = new MessageFormat(UnicodeString(TRUE, pattern, -1), fLocale, err); 1.726 + } 1.727 + if (U_SUCCESS(err)) { 1.728 + if (fNumberFormat != NULL && messageFormat != NULL) { 1.729 + messageFormat->setFormat(0, *fNumberFormat); 1.730 + } 1.731 + MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount); 1.732 + if (formatters == NULL) { 1.733 + formatters = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)); 1.734 + formatters[UTMUTFMT_FULL_STYLE] = NULL; 1.735 + formatters[UTMUTFMT_ABBREVIATED_STYLE] = NULL; 1.736 + countToPatterns->put(srcPluralCount, formatters, err); 1.737 + if (U_FAILURE(err)) { 1.738 + uprv_free(formatters); 1.739 + delete messageFormat; 1.740 + } 1.741 + } 1.742 + if (U_SUCCESS(err)) { 1.743 + //delete formatters[style]; 1.744 + formatters[style] = messageFormat; 1.745 + } 1.746 + } else { 1.747 + delete messageFormat; 1.748 + } 1.749 + } else { 1.750 + // fall back to rule "other", and search in parents 1.751 + searchInLocaleChain(style, key, localeName, srcTimeUnitField, srcPluralCount, 1.752 + gPluralCountOther, countToPatterns, err); 1.753 + } 1.754 +} 1.755 + 1.756 +void 1.757 +TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) { 1.758 + if (U_SUCCESS(status) && fLocale != locale) { 1.759 + fLocale = locale; 1.760 + setup(status); 1.761 + } 1.762 +} 1.763 + 1.764 + 1.765 +void 1.766 +TimeUnitFormat::setNumberFormat(const NumberFormat& format, UErrorCode& status){ 1.767 + if (U_FAILURE(status) || (fNumberFormat && format == *fNumberFormat)) { 1.768 + return; 1.769 + } 1.770 + delete fNumberFormat; 1.771 + fNumberFormat = (NumberFormat*)format.clone(); 1.772 + // reset the number formatter in the fTimeUnitToCountToPatterns map 1.773 + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; 1.774 + i < TimeUnit::UTIMEUNIT_FIELD_COUNT; 1.775 + i = (TimeUnit::UTimeUnitFields)(i+1)) { 1.776 + int32_t pos = -1; 1.777 + const UHashElement* elem = NULL; 1.778 + while ((elem = fTimeUnitToCountToPatterns[i]->nextElement(pos)) != NULL){ 1.779 + const UHashTok keyTok = elem->value; 1.780 + MessageFormat** pattern = (MessageFormat**)keyTok.pointer; 1.781 + 1.782 + pattern[UTMUTFMT_FULL_STYLE]->setFormat(0, format); 1.783 + pattern[UTMUTFMT_ABBREVIATED_STYLE]->setFormat(0, format); 1.784 + } 1.785 + } 1.786 +} 1.787 + 1.788 + 1.789 +void 1.790 +TimeUnitFormat::deleteHash(Hashtable* htable) { 1.791 + int32_t pos = -1; 1.792 + const UHashElement* element = NULL; 1.793 + if ( htable ) { 1.794 + while ( (element = htable->nextElement(pos)) != NULL ) { 1.795 + const UHashTok valueTok = element->value; 1.796 + const MessageFormat** value = (const MessageFormat**)valueTok.pointer; 1.797 + delete value[UTMUTFMT_FULL_STYLE]; 1.798 + delete value[UTMUTFMT_ABBREVIATED_STYLE]; 1.799 + //delete[] value; 1.800 + uprv_free(value); 1.801 + } 1.802 + } 1.803 + delete htable; 1.804 +} 1.805 + 1.806 + 1.807 +void 1.808 +TimeUnitFormat::copyHash(const Hashtable* source, Hashtable* target, UErrorCode& status) { 1.809 + if ( U_FAILURE(status) ) { 1.810 + return; 1.811 + } 1.812 + int32_t pos = -1; 1.813 + const UHashElement* element = NULL; 1.814 + if ( source ) { 1.815 + while ( (element = source->nextElement(pos)) != NULL ) { 1.816 + const UHashTok keyTok = element->key; 1.817 + const UnicodeString* key = (UnicodeString*)keyTok.pointer; 1.818 + const UHashTok valueTok = element->value; 1.819 + const MessageFormat** value = (const MessageFormat**)valueTok.pointer; 1.820 + MessageFormat** newVal = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)); 1.821 + newVal[0] = (MessageFormat*)value[0]->clone(); 1.822 + newVal[1] = (MessageFormat*)value[1]->clone(); 1.823 + target->put(UnicodeString(*key), newVal, status); 1.824 + if ( U_FAILURE(status) ) { 1.825 + delete newVal[0]; 1.826 + delete newVal[1]; 1.827 + uprv_free(newVal); 1.828 + return; 1.829 + } 1.830 + } 1.831 + } 1.832 +} 1.833 + 1.834 + 1.835 +U_CDECL_BEGIN 1.836 + 1.837 +/** 1.838 + * set hash table value comparator 1.839 + * 1.840 + * @param val1 one value in comparison 1.841 + * @param val2 the other value in comparison 1.842 + * @return TRUE if 2 values are the same, FALSE otherwise 1.843 + */ 1.844 +static UBool U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2); 1.845 + 1.846 +static UBool 1.847 +U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2) { 1.848 + const MessageFormat** pattern1 = (const MessageFormat**)val1.pointer; 1.849 + const MessageFormat** pattern2 = (const MessageFormat**)val2.pointer; 1.850 + return *pattern1[0] == *pattern2[0] && *pattern1[1] == *pattern2[1]; 1.851 +} 1.852 + 1.853 +U_CDECL_END 1.854 + 1.855 +Hashtable* 1.856 +TimeUnitFormat::initHash(UErrorCode& status) { 1.857 + if ( U_FAILURE(status) ) { 1.858 + return NULL; 1.859 + } 1.860 + Hashtable* hTable; 1.861 + if ( (hTable = new Hashtable(TRUE, status)) == NULL ) { 1.862 + status = U_MEMORY_ALLOCATION_ERROR; 1.863 + return NULL; 1.864 + } 1.865 + if ( U_FAILURE(status) ) { 1.866 + delete hTable; 1.867 + return NULL; 1.868 + } 1.869 + hTable->setValueComparator(tmutfmtHashTableValueComparator); 1.870 + return hTable; 1.871 +} 1.872 + 1.873 + 1.874 +const char* 1.875 +TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField, 1.876 + UErrorCode& status) { 1.877 + if (U_FAILURE(status)) { 1.878 + return NULL; 1.879 + } 1.880 + switch (unitField) { 1.881 + case TimeUnit::UTIMEUNIT_YEAR: 1.882 + return gTimeUnitYear; 1.883 + case TimeUnit::UTIMEUNIT_MONTH: 1.884 + return gTimeUnitMonth; 1.885 + case TimeUnit::UTIMEUNIT_DAY: 1.886 + return gTimeUnitDay; 1.887 + case TimeUnit::UTIMEUNIT_WEEK: 1.888 + return gTimeUnitWeek; 1.889 + case TimeUnit::UTIMEUNIT_HOUR: 1.890 + return gTimeUnitHour; 1.891 + case TimeUnit::UTIMEUNIT_MINUTE: 1.892 + return gTimeUnitMinute; 1.893 + case TimeUnit::UTIMEUNIT_SECOND: 1.894 + return gTimeUnitSecond; 1.895 + default: 1.896 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.897 + return NULL; 1.898 + } 1.899 +} 1.900 + 1.901 +U_NAMESPACE_END 1.902 + 1.903 +#endif