| Index: icu46/source/i18n/tmutfmt.cpp
|
| ===================================================================
|
| --- icu46/source/i18n/tmutfmt.cpp (revision 0)
|
| +++ icu46/source/i18n/tmutfmt.cpp (revision 0)
|
| @@ -0,0 +1,874 @@
|
| +/*
|
| + *******************************************************************************
|
| + * Copyright (C) 2008-2010, Google, International Business Machines Corporation
|
| + * and others. All Rights Reserved.
|
| + *******************************************************************************
|
| + */
|
| +
|
| +#include <typeinfo> // for 'typeid' to work
|
| +
|
| +#include "unicode/tmutfmt.h"
|
| +
|
| +#if !UCONFIG_NO_FORMATTING
|
| +
|
| +#include "cmemory.h"
|
| +#include "cstring.h"
|
| +#include "hash.h"
|
| +#include "uresimp.h"
|
| +#include "unicode/msgfmt.h"
|
| +
|
| +#define LEFT_CURLY_BRACKET ((UChar)0x007B)
|
| +#define RIGHT_CURLY_BRACKET ((UChar)0x007D)
|
| +#define SPACE ((UChar)0x0020)
|
| +#define DIGIT_ZERO ((UChar)0x0030)
|
| +#define LOW_S ((UChar)0x0073)
|
| +#define LOW_M ((UChar)0x006D)
|
| +#define LOW_I ((UChar)0x0069)
|
| +#define LOW_N ((UChar)0x006E)
|
| +#define LOW_H ((UChar)0x0068)
|
| +#define LOW_W ((UChar)0x0077)
|
| +#define LOW_D ((UChar)0x0064)
|
| +#define LOW_Y ((UChar)0x0079)
|
| +#define LOW_Z ((UChar)0x007A)
|
| +#define LOW_E ((UChar)0x0065)
|
| +#define LOW_R ((UChar)0x0072)
|
| +#define LOW_O ((UChar)0x006F)
|
| +#define LOW_N ((UChar)0x006E)
|
| +#define LOW_T ((UChar)0x0074)
|
| +
|
| +
|
| +//TODO: define in compile time
|
| +//#define TMUTFMT_DEBUG 1
|
| +
|
| +#ifdef TMUTFMT_DEBUG
|
| +#include <iostream>
|
| +#endif
|
| +
|
| +U_NAMESPACE_BEGIN
|
| +
|
| +
|
| +
|
| +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat)
|
| +
|
| +static const char gUnitsTag[] = "units";
|
| +static const char gShortUnitsTag[] = "unitsShort";
|
| +static const char gTimeUnitYear[] = "year";
|
| +static const char gTimeUnitMonth[] = "month";
|
| +static const char gTimeUnitDay[] = "day";
|
| +static const char gTimeUnitWeek[] = "week";
|
| +static const char gTimeUnitHour[] = "hour";
|
| +static const char gTimeUnitMinute[] = "minute";
|
| +static const char gTimeUnitSecond[] = "second";
|
| +static const char gPluralCountOther[] = "other";
|
| +
|
| +static const UChar DEFAULT_PATTERN_FOR_SECOND[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_S, 0};
|
| +static const UChar DEFAULT_PATTERN_FOR_MINUTE[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, LOW_I, LOW_N, 0};
|
| +static const UChar DEFAULT_PATTERN_FOR_HOUR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_H, 0};
|
| +static const UChar DEFAULT_PATTERN_FOR_WEEK[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_W, 0};
|
| +static const UChar DEFAULT_PATTERN_FOR_DAY[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_D, 0};
|
| +static const UChar DEFAULT_PATTERN_FOR_MONTH[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, 0};
|
| +static const UChar DEFAULT_PATTERN_FOR_YEAR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_Y, 0};
|
| +
|
| +static const UChar PLURAL_COUNT_ZERO[] = {LOW_Z, LOW_E, LOW_R, LOW_O, 0};
|
| +static const UChar PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0};
|
| +static const UChar PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0};
|
| +
|
| +
|
| +TimeUnitFormat::TimeUnitFormat(UErrorCode& status)
|
| +: fNumberFormat(NULL),
|
| + fPluralRules(NULL) {
|
| + create(Locale::getDefault(), kFull, status);
|
| +}
|
| +
|
| +
|
| +TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status)
|
| +: fNumberFormat(NULL),
|
| + fPluralRules(NULL) {
|
| + create(locale, kFull, status);
|
| +}
|
| +
|
| +
|
| +TimeUnitFormat::TimeUnitFormat(const Locale& locale, EStyle style, UErrorCode& status)
|
| +: fNumberFormat(NULL),
|
| + fPluralRules(NULL) {
|
| + create(locale, style, status);
|
| +}
|
| +
|
| +
|
| +TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other)
|
| +: MeasureFormat(other),
|
| + fNumberFormat(NULL),
|
| + fPluralRules(NULL),
|
| + fStyle(kFull)
|
| +{
|
| + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
|
| + i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
| + i = (TimeUnit::UTimeUnitFields)(i+1)) {
|
| + fTimeUnitToCountToPatterns[i] = NULL;
|
| + }
|
| + *this = other;
|
| +}
|
| +
|
| +
|
| +TimeUnitFormat::~TimeUnitFormat() {
|
| + delete fNumberFormat;
|
| + fNumberFormat = NULL;
|
| + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
|
| + i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
| + i = (TimeUnit::UTimeUnitFields)(i+1)) {
|
| + deleteHash(fTimeUnitToCountToPatterns[i]);
|
| + fTimeUnitToCountToPatterns[i] = NULL;
|
| + }
|
| + delete fPluralRules;
|
| + fPluralRules = NULL;
|
| +}
|
| +
|
| +
|
| +Format*
|
| +TimeUnitFormat::clone(void) const {
|
| + return new TimeUnitFormat(*this);
|
| +}
|
| +
|
| +
|
| +TimeUnitFormat&
|
| +TimeUnitFormat::operator=(const TimeUnitFormat& other) {
|
| + if (this == &other) {
|
| + return *this;
|
| + }
|
| + delete fNumberFormat;
|
| + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
|
| + i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
| + i = (TimeUnit::UTimeUnitFields)(i+1)) {
|
| + deleteHash(fTimeUnitToCountToPatterns[i]);
|
| + fTimeUnitToCountToPatterns[i] = NULL;
|
| + }
|
| + delete fPluralRules;
|
| + if (other.fNumberFormat) {
|
| + fNumberFormat = (NumberFormat*)other.fNumberFormat->clone();
|
| + } else {
|
| + fNumberFormat = NULL;
|
| + }
|
| + fLocale = other.fLocale;
|
| + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
|
| + i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
| + i = (TimeUnit::UTimeUnitFields)(i+1)) {
|
| + UErrorCode status = U_ZERO_ERROR;
|
| + fTimeUnitToCountToPatterns[i] = initHash(status);
|
| + if (U_SUCCESS(status)) {
|
| + copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
|
| + } else {
|
| + delete fTimeUnitToCountToPatterns[i];
|
| + fTimeUnitToCountToPatterns[i] = NULL;
|
| + }
|
| + }
|
| + if (other.fPluralRules) {
|
| + fPluralRules = (PluralRules*)other.fPluralRules->clone();
|
| + } else {
|
| + fPluralRules = NULL;
|
| + }
|
| + fStyle = other.fStyle;
|
| + return *this;
|
| +}
|
| +
|
| +
|
| +UBool
|
| +TimeUnitFormat::operator==(const Format& other) const {
|
| + if (typeid(*this) == typeid(other)) {
|
| + TimeUnitFormat* fmt = (TimeUnitFormat*)&other;
|
| + UBool ret = ( ((fNumberFormat && fmt->fNumberFormat && *fNumberFormat == *fmt->fNumberFormat)
|
| + || fNumberFormat == fmt->fNumberFormat )
|
| + && fLocale == fmt->fLocale
|
| + && ((fPluralRules && fmt->fPluralRules && *fPluralRules == *fmt->fPluralRules)
|
| + || fPluralRules == fmt->fPluralRules)
|
| + && fStyle == fmt->fStyle);
|
| + if (ret) {
|
| + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
|
| + i < TimeUnit::UTIMEUNIT_FIELD_COUNT && ret;
|
| + i = (TimeUnit::UTimeUnitFields)(i+1)) {
|
| + ret = fTimeUnitToCountToPatterns[i]->equals(*(fmt->fTimeUnitToCountToPatterns[i]));
|
| + }
|
| + }
|
| + return ret;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +
|
| +UnicodeString&
|
| +TimeUnitFormat::format(const Formattable& obj, UnicodeString& toAppendTo,
|
| + FieldPosition& pos, UErrorCode& status) const {
|
| + if (U_FAILURE(status)) {
|
| + return toAppendTo;
|
| + }
|
| + if (obj.getType() == Formattable::kObject) {
|
| + const UObject* formatObj = obj.getObject();
|
| + const TimeUnitAmount* amount = dynamic_cast<const TimeUnitAmount*>(formatObj);
|
| + if (amount != NULL){
|
| + Hashtable* countToPattern = fTimeUnitToCountToPatterns[amount->getTimeUnitField()];
|
| + double number;
|
| + const Formattable& amtNumber = amount->getNumber();
|
| + if (amtNumber.getType() == Formattable::kDouble) {
|
| + number = amtNumber.getDouble();
|
| + } else if (amtNumber.getType() == Formattable::kLong) {
|
| + number = amtNumber.getLong();
|
| + } else {
|
| + status = U_ILLEGAL_ARGUMENT_ERROR;
|
| + return toAppendTo;
|
| + }
|
| + UnicodeString count = fPluralRules->select(number);
|
| +#ifdef TMUTFMT_DEBUG
|
| + char result[1000];
|
| + count.extract(0, count.length(), result, "UTF-8");
|
| + std::cout << "number: " << number << "; format plural count: " << result << "\n";
|
| +#endif
|
| + MessageFormat* pattern = ((MessageFormat**)countToPattern->get(count))[fStyle];
|
| + Formattable formattable[1];
|
| + formattable[0].setDouble(number);
|
| + return pattern->format(formattable, 1, toAppendTo, pos, status);
|
| + }
|
| + }
|
| + status = U_ILLEGAL_ARGUMENT_ERROR;
|
| + return toAppendTo;
|
| +}
|
| +
|
| +
|
| +void
|
| +TimeUnitFormat::parseObject(const UnicodeString& source,
|
| + Formattable& result,
|
| + ParsePosition& pos) const {
|
| + double resultNumber = -1;
|
| + UBool withNumberFormat = false;
|
| + TimeUnit::UTimeUnitFields resultTimeUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
| + int32_t oldPos = pos.getIndex();
|
| + int32_t newPos = -1;
|
| + int32_t longestParseDistance = 0;
|
| + UnicodeString* countOfLongestMatch = NULL;
|
| +#ifdef TMUTFMT_DEBUG
|
| + char res[1000];
|
| + source.extract(0, source.length(), res, "UTF-8");
|
| + std::cout << "parse source: " << res << "\n";
|
| +#endif
|
| + // parse by iterating through all available patterns
|
| + // and looking for the longest match.
|
| + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
|
| + i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
| + i = (TimeUnit::UTimeUnitFields)(i+1)) {
|
| + Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
|
| + int32_t elemPos = -1;
|
| + const UHashElement* elem = NULL;
|
| + while ((elem = countToPatterns->nextElement(elemPos)) != NULL){
|
| + const UHashTok keyTok = elem->key;
|
| + UnicodeString* count = (UnicodeString*)keyTok.pointer;
|
| +#ifdef TMUTFMT_DEBUG
|
| + count->extract(0, count->length(), res, "UTF-8");
|
| + std::cout << "parse plural count: " << res << "\n";
|
| +#endif
|
| + const UHashTok valueTok = elem->value;
|
| + // the value is a pair of MessageFormat*
|
| + MessageFormat** patterns = (MessageFormat**)valueTok.pointer;
|
| + for (EStyle style = kFull; style < kTotal; style = (EStyle)(style + 1)) {
|
| + MessageFormat* pattern = patterns[style];
|
| + pos.setErrorIndex(-1);
|
| + pos.setIndex(oldPos);
|
| + // see if we can parse
|
| + Formattable parsed;
|
| + pattern->parseObject(source, parsed, pos);
|
| + if (pos.getErrorIndex() != -1 || pos.getIndex() == oldPos) {
|
| + continue;
|
| + }
|
| + #ifdef TMUTFMT_DEBUG
|
| + std::cout << "parsed.getType: " << parsed.getType() << "\n";
|
| + #endif
|
| + double tmpNumber = 0;
|
| + if (pattern->getArgTypeCount() != 0) {
|
| + // pattern with Number as beginning, such as "{0} d".
|
| + // check to make sure that the timeUnit is consistent
|
| + Formattable& temp = parsed[0];
|
| + if (temp.getType() == Formattable::kDouble) {
|
| + tmpNumber = temp.getDouble();
|
| + } else if (temp.getType() == Formattable::kLong) {
|
| + tmpNumber = temp.getLong();
|
| + } else {
|
| + continue;
|
| + }
|
| + UnicodeString select = fPluralRules->select(tmpNumber);
|
| + #ifdef TMUTFMT_DEBUG
|
| + select.extract(0, select.length(), res, "UTF-8");
|
| + std::cout << "parse plural select count: " << res << "\n";
|
| + #endif
|
| + if (*count != select) {
|
| + continue;
|
| + }
|
| + }
|
| + int32_t parseDistance = pos.getIndex() - oldPos;
|
| + if (parseDistance > longestParseDistance) {
|
| + if (pattern->getArgTypeCount() != 0) {
|
| + resultNumber = tmpNumber;
|
| + withNumberFormat = true;
|
| + } else {
|
| + withNumberFormat = false;
|
| + }
|
| + resultTimeUnit = i;
|
| + newPos = pos.getIndex();
|
| + longestParseDistance = parseDistance;
|
| + countOfLongestMatch = count;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + /* After find the longest match, parse the number.
|
| + * Result number could be null for the pattern without number pattern.
|
| + * such as unit pattern in Arabic.
|
| + * When result number is null, use plural rule to set the number.
|
| + */
|
| + if (withNumberFormat == false && longestParseDistance != 0) {
|
| + // set the number using plurrual count
|
| + if ( *countOfLongestMatch == PLURAL_COUNT_ZERO ) {
|
| + resultNumber = 0;
|
| + } else if ( *countOfLongestMatch == PLURAL_COUNT_ONE ) {
|
| + resultNumber = 1;
|
| + } else if ( *countOfLongestMatch == PLURAL_COUNT_TWO ) {
|
| + resultNumber = 2;
|
| + } else {
|
| + // should not happen.
|
| + // TODO: how to handle?
|
| + resultNumber = 3;
|
| + }
|
| + }
|
| + if (longestParseDistance == 0) {
|
| + pos.setIndex(oldPos);
|
| + pos.setErrorIndex(0);
|
| + } else {
|
| + UErrorCode status = U_ZERO_ERROR;
|
| + TimeUnitAmount* tmutamt = new TimeUnitAmount(resultNumber, resultTimeUnit, status);
|
| + if (U_SUCCESS(status)) {
|
| + result.adoptObject(tmutamt);
|
| + pos.setIndex(newPos);
|
| + pos.setErrorIndex(-1);
|
| + } else {
|
| + pos.setIndex(oldPos);
|
| + pos.setErrorIndex(0);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void
|
| +TimeUnitFormat::create(const Locale& locale, EStyle style, UErrorCode& status) {
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + if (style < kFull || style > kAbbreviate) {
|
| + status = U_ILLEGAL_ARGUMENT_ERROR;
|
| + return;
|
| + }
|
| + fStyle = style;
|
| + fLocale = locale;
|
| + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
|
| + i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
| + i = (TimeUnit::UTimeUnitFields)(i+1)) {
|
| + fTimeUnitToCountToPatterns[i] = NULL;
|
| + }
|
| + //TODO: format() and parseObj() are const member functions,
|
| + //so, can not do lazy initialization in C++.
|
| + //setup has to be done in constructors.
|
| + //and here, the behavior is not consistent with Java.
|
| + //In Java, create an empty instance does not setup locale as
|
| + //default locale. If it followed by setNumberFormat(),
|
| + //in format(), the locale will set up as the locale in fNumberFormat.
|
| + //But in C++, this sets the locale as the default locale.
|
| + setup(status);
|
| +}
|
| +
|
| +void
|
| +TimeUnitFormat::setup(UErrorCode& err) {
|
| + initDataMembers(err);
|
| + readFromCurrentLocale(kFull, gUnitsTag, err);
|
| + checkConsistency(kFull, gUnitsTag, err);
|
| + readFromCurrentLocale(kAbbreviate, gShortUnitsTag, err);
|
| + checkConsistency(kAbbreviate, gShortUnitsTag, err);
|
| +}
|
| +
|
| +
|
| +void
|
| +TimeUnitFormat::initDataMembers(UErrorCode& err){
|
| + if (U_FAILURE(err)) {
|
| + return;
|
| + }
|
| + if (fNumberFormat == NULL) {
|
| + fNumberFormat = NumberFormat::createInstance(fLocale, err);
|
| + }
|
| + delete fPluralRules;
|
| + fPluralRules = PluralRules::forLocale(fLocale, err);
|
| + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
|
| + i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
| + i = (TimeUnit::UTimeUnitFields)(i+1)) {
|
| + deleteHash(fTimeUnitToCountToPatterns[i]);
|
| + fTimeUnitToCountToPatterns[i] = NULL;
|
| + }
|
| +}
|
| +
|
| +
|
| +
|
| +
|
| +void
|
| +TimeUnitFormat::readFromCurrentLocale(EStyle style, const char* key, UErrorCode& err) {
|
| + if (U_FAILURE(err)) {
|
| + return;
|
| + }
|
| + // fill timeUnitToCountToPatterns from resource file
|
| + // err is used to indicate wrong status except missing resource.
|
| + // status is an error code used in resource lookup.
|
| + // status does not affect "err".
|
| + UErrorCode status = U_ZERO_ERROR;
|
| + UResourceBundle *rb, *unitsRes;
|
| + rb = ures_open(NULL, fLocale.getName(), &status);
|
| + unitsRes = ures_getByKey(rb, key, NULL, &status);
|
| + if (U_FAILURE(status)) {
|
| + ures_close(unitsRes);
|
| + ures_close(rb);
|
| + return;
|
| + }
|
| + int32_t size = ures_getSize(unitsRes);
|
| + for ( int32_t index = 0; index < size; ++index) {
|
| + // resource of one time unit
|
| + UResourceBundle* oneTimeUnit = ures_getByIndex(unitsRes, index,
|
| + NULL, &status);
|
| + if (U_SUCCESS(status)) {
|
| + const char* timeUnitName = ures_getKey(oneTimeUnit);
|
| + if (timeUnitName == NULL) {
|
| + ures_close(oneTimeUnit);
|
| + continue;
|
| + }
|
| + UResourceBundle* countsToPatternRB = ures_getByKey(unitsRes,
|
| + timeUnitName,
|
| + NULL, &status);
|
| + if (countsToPatternRB == NULL || U_FAILURE(status)) {
|
| + ures_close(countsToPatternRB);
|
| + ures_close(oneTimeUnit);
|
| + continue;
|
| + }
|
| + TimeUnit::UTimeUnitFields timeUnitField = TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
| + if ( uprv_strcmp(timeUnitName, gTimeUnitYear) == 0 ) {
|
| + timeUnitField = TimeUnit::UTIMEUNIT_YEAR;
|
| + } else if ( uprv_strcmp(timeUnitName, gTimeUnitMonth) == 0 ) {
|
| + timeUnitField = TimeUnit::UTIMEUNIT_MONTH;
|
| + } else if ( uprv_strcmp(timeUnitName, gTimeUnitDay) == 0 ) {
|
| + timeUnitField = TimeUnit::UTIMEUNIT_DAY;
|
| + } else if ( uprv_strcmp(timeUnitName, gTimeUnitHour) == 0 ) {
|
| + timeUnitField = TimeUnit::UTIMEUNIT_HOUR;
|
| + } else if ( uprv_strcmp(timeUnitName, gTimeUnitMinute) == 0 ) {
|
| + timeUnitField = TimeUnit::UTIMEUNIT_MINUTE;
|
| + } else if ( uprv_strcmp(timeUnitName, gTimeUnitSecond) == 0 ) {
|
| + timeUnitField = TimeUnit::UTIMEUNIT_SECOND;
|
| + } else if ( uprv_strcmp(timeUnitName, gTimeUnitWeek) == 0 ) {
|
| + timeUnitField = TimeUnit::UTIMEUNIT_WEEK;
|
| + } else {
|
| + ures_close(countsToPatternRB);
|
| + ures_close(oneTimeUnit);
|
| + continue;
|
| + }
|
| + Hashtable* countToPatterns = fTimeUnitToCountToPatterns[timeUnitField];
|
| + if (countToPatterns == NULL) {
|
| + countToPatterns = initHash(err);
|
| + if (U_FAILURE(err)) {
|
| + ures_close(countsToPatternRB);
|
| + ures_close(oneTimeUnit);
|
| + delete countToPatterns;
|
| + break;
|
| + }
|
| + }
|
| + int32_t count = ures_getSize(countsToPatternRB);
|
| + const UChar* pattern;
|
| + const char* pluralCount;
|
| + int32_t ptLength;
|
| + for ( int32_t pluralIndex = 0; pluralIndex < count; ++pluralIndex) {
|
| + // resource of count to pattern
|
| + pattern = ures_getNextString(countsToPatternRB, &ptLength,
|
| + &pluralCount, &status);
|
| + if (U_FAILURE(status)) {
|
| + continue;
|
| + }
|
| + MessageFormat* messageFormat = new MessageFormat(pattern, fLocale, err);
|
| + if ( U_SUCCESS(err) ) {
|
| + if (fNumberFormat != NULL) {
|
| + messageFormat->setFormat(0, *fNumberFormat);
|
| + }
|
| + MessageFormat** formatters = (MessageFormat**)countToPatterns->get(pluralCount);
|
| + if (formatters == NULL) {
|
| + formatters = (MessageFormat**)uprv_malloc(kTotal*sizeof(MessageFormat*));
|
| + formatters[kFull] = NULL;
|
| + formatters[kAbbreviate] = NULL;
|
| + countToPatterns->put(pluralCount, formatters, err);
|
| + if (U_FAILURE(err)) {
|
| + uprv_free(formatters);
|
| + }
|
| + }
|
| + if (U_SUCCESS(err)) {
|
| + //delete formatters[style];
|
| + formatters[style] = messageFormat;
|
| + }
|
| + }
|
| + if (U_FAILURE(err)) {
|
| + ures_close(countsToPatternRB);
|
| + ures_close(oneTimeUnit);
|
| + ures_close(unitsRes);
|
| + ures_close(rb);
|
| + delete messageFormat;
|
| + delete countToPatterns;
|
| + return;
|
| + }
|
| + }
|
| + if (fTimeUnitToCountToPatterns[timeUnitField] == NULL) {
|
| + fTimeUnitToCountToPatterns[timeUnitField] = countToPatterns;
|
| + }
|
| + ures_close(countsToPatternRB);
|
| + }
|
| + ures_close(oneTimeUnit);
|
| + }
|
| + ures_close(unitsRes);
|
| + ures_close(rb);
|
| +}
|
| +
|
| +
|
| +void
|
| +TimeUnitFormat::checkConsistency(EStyle style, const char* key, UErrorCode& err) {
|
| + if (U_FAILURE(err)) {
|
| + return;
|
| + }
|
| + // there should be patterns for each plural rule in each time unit.
|
| + // For each time unit,
|
| + // for each plural rule, following is unit pattern fall-back rule:
|
| + // ( for example: "one" hour )
|
| + // look for its unit pattern in its locale tree.
|
| + // if pattern is not found in its own locale, such as de_DE,
|
| + // look for the pattern in its parent, such as de,
|
| + // keep looking till found or till root.
|
| + // if the pattern is not found in root either,
|
| + // fallback to plural count "other",
|
| + // look for the pattern of "other" in the locale tree:
|
| + // "de_DE" to "de" to "root".
|
| + // If not found, fall back to value of
|
| + // static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h".
|
| + //
|
| + // Following is consistency check to create pattern for each
|
| + // plural rule in each time unit using above fall-back rule.
|
| + //
|
| + StringEnumeration* keywords = fPluralRules->getKeywords(err);
|
| + if (U_SUCCESS(err)) {
|
| + const char* pluralCount;
|
| + while ((pluralCount = keywords->next(NULL, err)) != NULL) {
|
| + if ( U_SUCCESS(err) ) {
|
| + for (int32_t i = 0; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; ++i) {
|
| + // for each time unit,
|
| + // get all the patterns for each plural rule in this locale.
|
| + Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
|
| + if ( countToPatterns == NULL ) {
|
| + countToPatterns = initHash(err);
|
| + if (U_FAILURE(err)) {
|
| + delete countToPatterns;
|
| + return;
|
| + }
|
| + fTimeUnitToCountToPatterns[i] = countToPatterns;
|
| + }
|
| + MessageFormat** formatters = (MessageFormat**)countToPatterns->get(pluralCount);
|
| + if( formatters == NULL || formatters[style] == NULL ) {
|
| + // look through parents
|
| + const char* localeName = fLocale.getName();
|
| + searchInLocaleChain(style, key, localeName,
|
| + (TimeUnit::UTimeUnitFields)i,
|
| + pluralCount, pluralCount,
|
| + countToPatterns, err);
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + delete keywords;
|
| +}
|
| +
|
| +
|
| +
|
| +// srcPluralCount is the original plural count on which the pattern is
|
| +// searched for.
|
| +// searchPluralCount is the fallback plural count.
|
| +// For example, to search for pattern for ""one" hour",
|
| +// "one" is the srcPluralCount,
|
| +// if the pattern is not found even in root, fallback to
|
| +// using patterns of plural count "other",
|
| +// then, "other" is the searchPluralCount.
|
| +void
|
| +TimeUnitFormat::searchInLocaleChain(EStyle style, const char* key, const char* localeName,
|
| + TimeUnit::UTimeUnitFields srcTimeUnitField,
|
| + const char* srcPluralCount,
|
| + const char* searchPluralCount,
|
| + Hashtable* countToPatterns,
|
| + UErrorCode& err) {
|
| + if (U_FAILURE(err)) {
|
| + return;
|
| + }
|
| + UErrorCode status = U_ZERO_ERROR;
|
| + char parentLocale[ULOC_FULLNAME_CAPACITY];
|
| + uprv_strcpy(parentLocale, localeName);
|
| + int32_t locNameLen;
|
| + while ((locNameLen = uloc_getParent(parentLocale, parentLocale,
|
| + ULOC_FULLNAME_CAPACITY, &status)) >= 0){
|
| + // look for pattern for srcPluralCount in locale tree
|
| + UResourceBundle *rb, *unitsRes, *countsToPatternRB;
|
| + rb = ures_open(NULL, parentLocale, &status);
|
| + unitsRes = ures_getByKey(rb, key, NULL, &status);
|
| + const char* timeUnitName = getTimeUnitName(srcTimeUnitField, status);
|
| + countsToPatternRB = ures_getByKey(unitsRes, timeUnitName, NULL, &status);
|
| + const UChar* pattern;
|
| + int32_t ptLength;
|
| + pattern = ures_getStringByKeyWithFallback(countsToPatternRB, searchPluralCount, &ptLength, &status);
|
| + if (U_SUCCESS(status)) {
|
| + //found
|
| + MessageFormat* messageFormat = new MessageFormat(pattern, fLocale, err);
|
| + if (U_SUCCESS(err)) {
|
| + if (fNumberFormat != NULL) {
|
| + messageFormat->setFormat(0, *fNumberFormat);
|
| + }
|
| + MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
|
| + if (formatters == NULL) {
|
| + formatters = (MessageFormat**)uprv_malloc(kTotal*sizeof(MessageFormat*));
|
| + formatters[kFull] = NULL;
|
| + formatters[kAbbreviate] = NULL;
|
| + countToPatterns->put(srcPluralCount, formatters, err);
|
| + if (U_FAILURE(err)) {
|
| + uprv_free(formatters);
|
| + delete messageFormat;
|
| + }
|
| + }
|
| + if (U_SUCCESS(err)) {
|
| + //delete formatters[style];
|
| + formatters[style] = messageFormat;
|
| + }
|
| + } else {
|
| + delete messageFormat;
|
| + }
|
| + ures_close(countsToPatternRB);
|
| + ures_close(unitsRes);
|
| + ures_close(rb);
|
| + return;
|
| + }
|
| + ures_close(countsToPatternRB);
|
| + ures_close(unitsRes);
|
| + ures_close(rb);
|
| + status = U_ZERO_ERROR;
|
| + if ( locNameLen ==0 ) {
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // if no unitsShort resource was found even after fallback to root locale
|
| + // then search the units resource fallback from the current level to root
|
| + if ( locNameLen == 0 && uprv_strcmp(key, gShortUnitsTag) == 0) {
|
| +#ifdef TMUTFMT_DEBUG
|
| + std::cout << "loop into searchInLocaleChain since Short-Long-Alternative \n";
|
| +#endif
|
| + char pLocale[ULOC_FULLNAME_CAPACITY];
|
| + uprv_strcpy(pLocale, localeName);
|
| + // Add an underscore at the tail of locale name,
|
| + // so that searchInLocaleChain will check the current locale before falling back
|
| + uprv_strcat(pLocale, "_");
|
| + searchInLocaleChain(style, gUnitsTag, pLocale, srcTimeUnitField, srcPluralCount,
|
| + searchPluralCount, countToPatterns, err);
|
| + if (countToPatterns != NULL) {
|
| + MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
|
| + if (formatters != NULL && formatters[style] != NULL) return;
|
| + }
|
| + }
|
| +
|
| + // if not found the pattern for this plural count at all,
|
| + // fall-back to plural count "other"
|
| + if ( uprv_strcmp(searchPluralCount, gPluralCountOther) == 0 ) {
|
| + // set default fall back the same as the resource in root
|
| + MessageFormat* messageFormat = NULL;
|
| + if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_SECOND ) {
|
| + messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_SECOND, fLocale, err);
|
| + } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MINUTE ) {
|
| + messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_MINUTE, fLocale, err);
|
| + } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_HOUR ) {
|
| + messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_HOUR, fLocale, err);
|
| + } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_WEEK ) {
|
| + messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_WEEK, fLocale, err);
|
| + } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_DAY ) {
|
| + messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_DAY, fLocale, err);
|
| + } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MONTH ) {
|
| + messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_MONTH, fLocale, err);
|
| + } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_YEAR ) {
|
| + messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_YEAR, fLocale, err);
|
| + }
|
| + if (U_SUCCESS(err)) {
|
| + if (fNumberFormat != NULL && messageFormat != NULL) {
|
| + messageFormat->setFormat(0, *fNumberFormat);
|
| + }
|
| + MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
|
| + if (formatters == NULL) {
|
| + formatters = (MessageFormat**)uprv_malloc(kTotal*sizeof(MessageFormat*));
|
| + formatters[kFull] = NULL;
|
| + formatters[kAbbreviate] = NULL;
|
| + countToPatterns->put(srcPluralCount, formatters, err);
|
| + if (U_FAILURE(err)) {
|
| + uprv_free(formatters);
|
| + delete messageFormat;
|
| + }
|
| + }
|
| + if (U_SUCCESS(err)) {
|
| + //delete formatters[style];
|
| + formatters[style] = messageFormat;
|
| + }
|
| + } else {
|
| + delete messageFormat;
|
| + }
|
| + } else {
|
| + // fall back to rule "other", and search in parents
|
| + searchInLocaleChain(style, key, localeName, srcTimeUnitField, srcPluralCount,
|
| + gPluralCountOther, countToPatterns, err);
|
| + }
|
| +}
|
| +
|
| +void
|
| +TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) {
|
| + if (U_SUCCESS(status) && fLocale != locale) {
|
| + fLocale = locale;
|
| + setup(status);
|
| + }
|
| +}
|
| +
|
| +
|
| +void
|
| +TimeUnitFormat::setNumberFormat(const NumberFormat& format, UErrorCode& status){
|
| + if (U_FAILURE(status) || (fNumberFormat && format == *fNumberFormat)) {
|
| + return;
|
| + }
|
| + delete fNumberFormat;
|
| + fNumberFormat = (NumberFormat*)format.clone();
|
| + // reset the number formatter in the fTimeUnitToCountToPatterns map
|
| + for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
|
| + i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
|
| + i = (TimeUnit::UTimeUnitFields)(i+1)) {
|
| + int32_t pos = -1;
|
| + const UHashElement* elem = NULL;
|
| + while ((elem = fTimeUnitToCountToPatterns[i]->nextElement(pos)) != NULL){
|
| + const UHashTok keyTok = elem->value;
|
| + MessageFormat** pattern = (MessageFormat**)keyTok.pointer;
|
| + pattern[kFull]->setFormat(0, format);
|
| + pattern[kAbbreviate]->setFormat(0, format);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void
|
| +TimeUnitFormat::deleteHash(Hashtable* htable) {
|
| + int32_t pos = -1;
|
| + const UHashElement* element = NULL;
|
| + if ( htable ) {
|
| + while ( (element = htable->nextElement(pos)) != NULL ) {
|
| + const UHashTok valueTok = element->value;
|
| + const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
|
| + delete value[kFull];
|
| + delete value[kAbbreviate];
|
| + //delete[] value;
|
| + uprv_free(value);
|
| + }
|
| + }
|
| + delete htable;
|
| +}
|
| +
|
| +
|
| +void
|
| +TimeUnitFormat::copyHash(const Hashtable* source, Hashtable* target, UErrorCode& status) {
|
| + if ( U_FAILURE(status) ) {
|
| + return;
|
| + }
|
| + int32_t pos = -1;
|
| + const UHashElement* element = NULL;
|
| + if ( source ) {
|
| + while ( (element = source->nextElement(pos)) != NULL ) {
|
| + const UHashTok keyTok = element->key;
|
| + const UnicodeString* key = (UnicodeString*)keyTok.pointer;
|
| + const UHashTok valueTok = element->value;
|
| + const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
|
| + MessageFormat** newVal = (MessageFormat**)uprv_malloc(kTotal*sizeof(MessageFormat*));
|
| + newVal[0] = (MessageFormat*)value[0]->clone();
|
| + newVal[1] = (MessageFormat*)value[1]->clone();
|
| + target->put(UnicodeString(*key), newVal, status);
|
| + if ( U_FAILURE(status) ) {
|
| + delete newVal[0];
|
| + delete newVal[1];
|
| + uprv_free(newVal);
|
| + return;
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +U_CDECL_BEGIN
|
| +
|
| +/**
|
| + * set hash table value comparator
|
| + *
|
| + * @param val1 one value in comparison
|
| + * @param val2 the other value in comparison
|
| + * @return TRUE if 2 values are the same, FALSE otherwise
|
| + */
|
| +static UBool U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2);
|
| +
|
| +static UBool
|
| +U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2) {
|
| + const MessageFormat** pattern1 = (const MessageFormat**)val1.pointer;
|
| + const MessageFormat** pattern2 = (const MessageFormat**)val2.pointer;
|
| + return *pattern1[0] == *pattern2[0] && *pattern1[1] == *pattern2[1];
|
| +}
|
| +
|
| +U_CDECL_END
|
| +
|
| +Hashtable*
|
| +TimeUnitFormat::initHash(UErrorCode& status) {
|
| + if ( U_FAILURE(status) ) {
|
| + return NULL;
|
| + }
|
| + Hashtable* hTable;
|
| + if ( (hTable = new Hashtable(TRUE, status)) == NULL ) {
|
| + status = U_MEMORY_ALLOCATION_ERROR;
|
| + return NULL;
|
| + }
|
| + hTable->setValueComparator(tmutfmtHashTableValueComparator);
|
| + return hTable;
|
| +}
|
| +
|
| +
|
| +const char*
|
| +TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField,
|
| + UErrorCode& status) {
|
| + if (U_FAILURE(status)) {
|
| + return NULL;
|
| + }
|
| + switch (unitField) {
|
| + case TimeUnit::UTIMEUNIT_YEAR:
|
| + return gTimeUnitYear;
|
| + case TimeUnit::UTIMEUNIT_MONTH:
|
| + return gTimeUnitMonth;
|
| + case TimeUnit::UTIMEUNIT_DAY:
|
| + return gTimeUnitDay;
|
| + case TimeUnit::UTIMEUNIT_WEEK:
|
| + return gTimeUnitWeek;
|
| + case TimeUnit::UTIMEUNIT_HOUR:
|
| + return gTimeUnitHour;
|
| + case TimeUnit::UTIMEUNIT_MINUTE:
|
| + return gTimeUnitMinute;
|
| + case TimeUnit::UTIMEUNIT_SECOND:
|
| + return gTimeUnitSecond;
|
| + default:
|
| + status = U_ILLEGAL_ARGUMENT_ERROR;
|
| + return NULL;
|
| + }
|
| +}
|
| +
|
| +U_NAMESPACE_END
|
| +
|
| +#endif
|
|
|
| Property changes on: icu46/source/i18n/tmutfmt.cpp
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|