michael@0: /* michael@0: ******************************************************************************* michael@0: * Copyright (C) 2009-2011, International Business Machines Corporation and michael@0: * others. All Rights Reserved. michael@0: ******************************************************************************* michael@0: */ michael@0: michael@0: #include "unicode/currpinf.h" michael@0: michael@0: #if !UCONFIG_NO_FORMATTING michael@0: michael@0: //#define CURRENCY_PLURAL_INFO_DEBUG 1 michael@0: michael@0: #ifdef CURRENCY_PLURAL_INFO_DEBUG michael@0: #include michael@0: #endif michael@0: michael@0: michael@0: #include "unicode/locid.h" michael@0: #include "unicode/plurrule.h" michael@0: #include "unicode/ures.h" michael@0: #include "unicode/numsys.h" michael@0: #include "cstring.h" michael@0: #include "hash.h" michael@0: #include "uresimp.h" michael@0: #include "ureslocs.h" michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: michael@0: static const UChar gNumberPatternSeparator = 0x3B; // ; michael@0: michael@0: U_CDECL_BEGIN michael@0: michael@0: /** michael@0: * @internal ICU 4.2 michael@0: */ michael@0: static UBool U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2); michael@0: michael@0: UBool michael@0: U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2) { michael@0: const UnicodeString* affix_1 = (UnicodeString*)val1.pointer; michael@0: const UnicodeString* affix_2 = (UnicodeString*)val2.pointer; michael@0: return *affix_1 == *affix_2; michael@0: } michael@0: michael@0: U_CDECL_END michael@0: michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo) michael@0: michael@0: static const UChar gDefaultCurrencyPluralPattern[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0}; michael@0: static const UChar gTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0}; michael@0: static const UChar gPluralCountOther[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0}; michael@0: static const UChar gPart0[] = {0x7B, 0x30, 0x7D, 0}; michael@0: static const UChar gPart1[] = {0x7B, 0x31, 0x7D, 0}; michael@0: michael@0: static const char gNumberElementsTag[]="NumberElements"; michael@0: static const char gLatnTag[]="latn"; michael@0: static const char gPatternsTag[]="patterns"; michael@0: static const char gDecimalFormatTag[]="decimalFormat"; michael@0: static const char gCurrUnitPtnTag[]="CurrencyUnitPatterns"; michael@0: michael@0: CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status) michael@0: : fPluralCountToCurrencyUnitPattern(NULL), michael@0: fPluralRules(NULL), michael@0: fLocale(NULL) { michael@0: initialize(Locale::getDefault(), status); michael@0: } michael@0: michael@0: CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status) michael@0: : fPluralCountToCurrencyUnitPattern(NULL), michael@0: fPluralRules(NULL), michael@0: fLocale(NULL) { michael@0: initialize(locale, status); michael@0: } michael@0: michael@0: CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info) michael@0: : UObject(info), michael@0: fPluralCountToCurrencyUnitPattern(NULL), michael@0: fPluralRules(NULL), michael@0: fLocale(NULL) { michael@0: *this = info; michael@0: } michael@0: michael@0: michael@0: CurrencyPluralInfo& michael@0: CurrencyPluralInfo::operator=(const CurrencyPluralInfo& info) { michael@0: if (this == &info) { michael@0: return *this; michael@0: } michael@0: michael@0: deleteHash(fPluralCountToCurrencyUnitPattern); michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: fPluralCountToCurrencyUnitPattern = initHash(status); michael@0: copyHash(info.fPluralCountToCurrencyUnitPattern, michael@0: fPluralCountToCurrencyUnitPattern, status); michael@0: if ( U_FAILURE(status) ) { michael@0: return *this; michael@0: } michael@0: michael@0: delete fPluralRules; michael@0: delete fLocale; michael@0: if (info.fPluralRules) { michael@0: fPluralRules = info.fPluralRules->clone(); michael@0: } else { michael@0: fPluralRules = NULL; michael@0: } michael@0: if (info.fLocale) { michael@0: fLocale = info.fLocale->clone(); michael@0: } else { michael@0: fLocale = NULL; michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: michael@0: CurrencyPluralInfo::~CurrencyPluralInfo() { michael@0: deleteHash(fPluralCountToCurrencyUnitPattern); michael@0: fPluralCountToCurrencyUnitPattern = NULL; michael@0: delete fPluralRules; michael@0: delete fLocale; michael@0: fPluralRules = NULL; michael@0: fLocale = NULL; michael@0: } michael@0: michael@0: UBool michael@0: CurrencyPluralInfo::operator==(const CurrencyPluralInfo& info) const { michael@0: #ifdef CURRENCY_PLURAL_INFO_DEBUG michael@0: if (*fPluralRules == *info.fPluralRules) { michael@0: std::cout << "same plural rules\n"; michael@0: } michael@0: if (*fLocale == *info.fLocale) { michael@0: std::cout << "same locale\n"; michael@0: } michael@0: if (fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern)) { michael@0: std::cout << "same pattern\n"; michael@0: } michael@0: #endif michael@0: return *fPluralRules == *info.fPluralRules && michael@0: *fLocale == *info.fLocale && michael@0: fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern); michael@0: } michael@0: michael@0: michael@0: CurrencyPluralInfo* michael@0: CurrencyPluralInfo::clone() const { michael@0: return new CurrencyPluralInfo(*this); michael@0: } michael@0: michael@0: const PluralRules* michael@0: CurrencyPluralInfo::getPluralRules() const { michael@0: return fPluralRules; michael@0: } michael@0: michael@0: UnicodeString& michael@0: CurrencyPluralInfo::getCurrencyPluralPattern(const UnicodeString& pluralCount, michael@0: UnicodeString& result) const { michael@0: const UnicodeString* currencyPluralPattern = michael@0: (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(pluralCount); michael@0: if (currencyPluralPattern == NULL) { michael@0: // fall back to "other" michael@0: if (pluralCount.compare(gPluralCountOther, 5)) { michael@0: currencyPluralPattern = michael@0: (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(UnicodeString(TRUE, gPluralCountOther, 5)); michael@0: } michael@0: if (currencyPluralPattern == NULL) { michael@0: // no currencyUnitPatterns defined, michael@0: // fallback to predefined defult. michael@0: // This should never happen when ICU resource files are michael@0: // available, since currencyUnitPattern of "other" is always michael@0: // defined in root. michael@0: result = UnicodeString(gDefaultCurrencyPluralPattern); michael@0: return result; michael@0: } michael@0: } michael@0: result = *currencyPluralPattern; michael@0: return result; michael@0: } michael@0: michael@0: const Locale& michael@0: CurrencyPluralInfo::getLocale() const { michael@0: return *fLocale; michael@0: } michael@0: michael@0: void michael@0: CurrencyPluralInfo::setPluralRules(const UnicodeString& ruleDescription, michael@0: UErrorCode& status) { michael@0: if (U_SUCCESS(status)) { michael@0: if (fPluralRules) { michael@0: delete fPluralRules; michael@0: } michael@0: fPluralRules = PluralRules::createRules(ruleDescription, status); michael@0: } michael@0: } michael@0: michael@0: michael@0: void michael@0: CurrencyPluralInfo::setCurrencyPluralPattern(const UnicodeString& pluralCount, michael@0: const UnicodeString& pattern, michael@0: UErrorCode& status) { michael@0: if (U_SUCCESS(status)) { michael@0: fPluralCountToCurrencyUnitPattern->put(pluralCount, new UnicodeString(pattern), status); michael@0: } michael@0: } michael@0: michael@0: michael@0: void michael@0: CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) { michael@0: initialize(loc, status); michael@0: } michael@0: michael@0: michael@0: void michael@0: CurrencyPluralInfo::initialize(const Locale& loc, UErrorCode& status) { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: delete fLocale; michael@0: fLocale = loc.clone(); michael@0: if (fPluralRules) { michael@0: delete fPluralRules; michael@0: } michael@0: fPluralRules = PluralRules::forLocale(loc, status); michael@0: setupCurrencyPluralPattern(loc, status); michael@0: } michael@0: michael@0: michael@0: void michael@0: CurrencyPluralInfo::setupCurrencyPluralPattern(const Locale& loc, UErrorCode& status) { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: michael@0: if (fPluralCountToCurrencyUnitPattern) { michael@0: deleteHash(fPluralCountToCurrencyUnitPattern); michael@0: } michael@0: fPluralCountToCurrencyUnitPattern = initHash(status); michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: michael@0: NumberingSystem *ns = NumberingSystem::createInstance(loc,status); michael@0: UErrorCode ec = U_ZERO_ERROR; michael@0: UResourceBundle *rb = ures_open(NULL, loc.getName(), &ec); michael@0: UResourceBundle *numElements = ures_getByKeyWithFallback(rb, gNumberElementsTag, NULL, &ec); michael@0: rb = ures_getByKeyWithFallback(numElements, ns->getName(), rb, &ec); michael@0: rb = ures_getByKeyWithFallback(rb, gPatternsTag, rb, &ec); michael@0: int32_t ptnLen; michael@0: const UChar* numberStylePattern = ures_getStringByKeyWithFallback(rb, gDecimalFormatTag, &ptnLen, &ec); michael@0: // Fall back to "latn" if num sys specific pattern isn't there. michael@0: if ( ec == U_MISSING_RESOURCE_ERROR && uprv_strcmp(ns->getName(),gLatnTag)) { michael@0: ec = U_ZERO_ERROR; michael@0: rb = ures_getByKeyWithFallback(numElements, gLatnTag, rb, &ec); michael@0: rb = ures_getByKeyWithFallback(rb, gPatternsTag, rb, &ec); michael@0: numberStylePattern = ures_getStringByKeyWithFallback(rb, gDecimalFormatTag, &ptnLen, &ec); michael@0: } michael@0: int32_t numberStylePatternLen = ptnLen; michael@0: const UChar* negNumberStylePattern = NULL; michael@0: int32_t negNumberStylePatternLen = 0; michael@0: // TODO: Java michael@0: // parse to check whether there is ";" separator in the numberStylePattern michael@0: UBool hasSeparator = false; michael@0: if (U_SUCCESS(ec)) { michael@0: for (int32_t styleCharIndex = 0; styleCharIndex < ptnLen; ++styleCharIndex) { michael@0: if (numberStylePattern[styleCharIndex] == gNumberPatternSeparator) { michael@0: hasSeparator = true; michael@0: // split the number style pattern into positive and negative michael@0: negNumberStylePattern = numberStylePattern + styleCharIndex + 1; michael@0: negNumberStylePatternLen = ptnLen - styleCharIndex - 1; michael@0: numberStylePatternLen = styleCharIndex; michael@0: } michael@0: } michael@0: } michael@0: michael@0: ures_close(numElements); michael@0: ures_close(rb); michael@0: delete ns; michael@0: michael@0: if (U_FAILURE(ec)) { michael@0: return; michael@0: } michael@0: michael@0: UResourceBundle *currRb = ures_open(U_ICUDATA_CURR, loc.getName(), &ec); michael@0: UResourceBundle *currencyRes = ures_getByKeyWithFallback(currRb, gCurrUnitPtnTag, NULL, &ec); michael@0: michael@0: #ifdef CURRENCY_PLURAL_INFO_DEBUG michael@0: std::cout << "in set up\n"; michael@0: #endif michael@0: StringEnumeration* keywords = fPluralRules->getKeywords(ec); michael@0: if (U_SUCCESS(ec)) { michael@0: const char* pluralCount; michael@0: while ((pluralCount = keywords->next(NULL, ec)) != NULL) { michael@0: if ( U_SUCCESS(ec) ) { michael@0: int32_t ptnLen; michael@0: UErrorCode err = U_ZERO_ERROR; michael@0: const UChar* patternChars = ures_getStringByKeyWithFallback( michael@0: currencyRes, pluralCount, &ptnLen, &err); michael@0: if (U_SUCCESS(err) && ptnLen > 0) { michael@0: UnicodeString* pattern = new UnicodeString(patternChars, ptnLen); michael@0: #ifdef CURRENCY_PLURAL_INFO_DEBUG michael@0: char result_1[1000]; michael@0: pattern->extract(0, pattern->length(), result_1, "UTF-8"); michael@0: std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n"; michael@0: #endif michael@0: pattern->findAndReplace(UnicodeString(TRUE, gPart0, 3), michael@0: UnicodeString(numberStylePattern, numberStylePatternLen)); michael@0: pattern->findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3)); michael@0: michael@0: if (hasSeparator) { michael@0: UnicodeString negPattern(patternChars, ptnLen); michael@0: negPattern.findAndReplace(UnicodeString(TRUE, gPart0, 3), michael@0: UnicodeString(negNumberStylePattern, negNumberStylePatternLen)); michael@0: negPattern.findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3)); michael@0: pattern->append(gNumberPatternSeparator); michael@0: pattern->append(negPattern); michael@0: } michael@0: #ifdef CURRENCY_PLURAL_INFO_DEBUG michael@0: pattern->extract(0, pattern->length(), result_1, "UTF-8"); michael@0: std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n"; michael@0: #endif michael@0: michael@0: fPluralCountToCurrencyUnitPattern->put(UnicodeString(pluralCount, -1, US_INV), pattern, status); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: delete keywords; michael@0: ures_close(currencyRes); michael@0: ures_close(currRb); michael@0: } michael@0: michael@0: michael@0: michael@0: void michael@0: CurrencyPluralInfo::deleteHash(Hashtable* hTable) michael@0: { michael@0: if ( hTable == NULL ) { michael@0: return; michael@0: } michael@0: int32_t pos = -1; michael@0: const UHashElement* element = NULL; michael@0: while ( (element = hTable->nextElement(pos)) != NULL ) { michael@0: const UHashTok valueTok = element->value; michael@0: const UnicodeString* value = (UnicodeString*)valueTok.pointer; michael@0: delete value; michael@0: } michael@0: delete hTable; michael@0: hTable = NULL; michael@0: } michael@0: michael@0: michael@0: Hashtable* michael@0: CurrencyPluralInfo::initHash(UErrorCode& status) { michael@0: if ( U_FAILURE(status) ) { michael@0: return NULL; michael@0: } michael@0: Hashtable* hTable; michael@0: if ( (hTable = new Hashtable(TRUE, status)) == NULL ) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: if ( U_FAILURE(status) ) { michael@0: delete hTable; michael@0: return NULL; michael@0: } michael@0: hTable->setValueComparator(ValueComparator); michael@0: return hTable; michael@0: } michael@0: michael@0: michael@0: void michael@0: CurrencyPluralInfo::copyHash(const Hashtable* source, michael@0: Hashtable* target, michael@0: UErrorCode& status) { michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: int32_t pos = -1; michael@0: const UHashElement* element = NULL; michael@0: if ( source ) { michael@0: while ( (element = source->nextElement(pos)) != NULL ) { michael@0: const UHashTok keyTok = element->key; michael@0: const UnicodeString* key = (UnicodeString*)keyTok.pointer; michael@0: const UHashTok valueTok = element->value; michael@0: const UnicodeString* value = (UnicodeString*)valueTok.pointer; michael@0: UnicodeString* copy = new UnicodeString(*value); michael@0: target->put(UnicodeString(*key), copy, status); michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: #endif