442 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			442 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
| // © 2016 and later: Unicode, Inc. and others.
 | |
| // License & terms of use: http://www.unicode.org/copyright.html
 | |
| /*
 | |
|  *******************************************************************************
 | |
|  * Copyright (C) 2009-2014, International Business Machines Corporation and
 | |
|  * others. All Rights Reserved.
 | |
|  *******************************************************************************
 | |
|  */
 | |
| 
 | |
| #include "unicode/currpinf.h"
 | |
| 
 | |
| #if !UCONFIG_NO_FORMATTING
 | |
| 
 | |
| //#define CURRENCY_PLURAL_INFO_DEBUG 1
 | |
| 
 | |
| #ifdef CURRENCY_PLURAL_INFO_DEBUG
 | |
| #include <iostream>
 | |
| #endif
 | |
| 
 | |
| #include "unicode/locid.h"
 | |
| #include "unicode/plurrule.h"
 | |
| #include "unicode/strenum.h"
 | |
| #include "unicode/ures.h"
 | |
| #include "unicode/numsys.h"
 | |
| #include "cstring.h"
 | |
| #include "hash.h"
 | |
| #include "uresimp.h"
 | |
| #include "ureslocs.h"
 | |
| 
 | |
| U_NAMESPACE_BEGIN
 | |
| 
 | |
| static const UChar gNumberPatternSeparator = 0x3B; // ;
 | |
| 
 | |
| U_CDECL_BEGIN
 | |
| 
 | |
| /**
 | |
|  * @internal ICU 4.2
 | |
|  */
 | |
| static UBool U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2);
 | |
| 
 | |
| UBool
 | |
| U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2) {
 | |
|     const UnicodeString* affix_1 = (UnicodeString*)val1.pointer;
 | |
|     const UnicodeString* affix_2 = (UnicodeString*)val2.pointer;
 | |
|     return  *affix_1 == *affix_2;
 | |
| }
 | |
| 
 | |
| U_CDECL_END
 | |
| 
 | |
| 
 | |
| UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo)
 | |
| 
 | |
| static const UChar gDefaultCurrencyPluralPattern[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0};
 | |
| static const UChar gTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0};
 | |
| static const UChar gPluralCountOther[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0};
 | |
| static const UChar gPart0[] = {0x7B, 0x30, 0x7D, 0};
 | |
| static const UChar gPart1[] = {0x7B, 0x31, 0x7D, 0};
 | |
| 
 | |
| static const char gNumberElementsTag[]="NumberElements";
 | |
| static const char gLatnTag[]="latn";
 | |
| static const char gPatternsTag[]="patterns";
 | |
| static const char gDecimalFormatTag[]="decimalFormat";
 | |
| static const char gCurrUnitPtnTag[]="CurrencyUnitPatterns";
 | |
| 
 | |
| CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status)
 | |
| :   fPluralCountToCurrencyUnitPattern(nullptr),
 | |
|     fPluralRules(nullptr),
 | |
|     fLocale(nullptr),
 | |
|     fInternalStatus(U_ZERO_ERROR) {
 | |
|     initialize(Locale::getDefault(), status);
 | |
| }
 | |
| 
 | |
| CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status)
 | |
| :   fPluralCountToCurrencyUnitPattern(nullptr),
 | |
|     fPluralRules(nullptr),
 | |
|     fLocale(nullptr),
 | |
|     fInternalStatus(U_ZERO_ERROR) {
 | |
|     initialize(locale, status);
 | |
| }
 | |
| 
 | |
| CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info) 
 | |
| :   UObject(info),
 | |
|     fPluralCountToCurrencyUnitPattern(nullptr),
 | |
|     fPluralRules(nullptr),
 | |
|     fLocale(nullptr),
 | |
|     fInternalStatus(U_ZERO_ERROR) {
 | |
|     *this = info;
 | |
| }
 | |
| 
 | |
| CurrencyPluralInfo&
 | |
| CurrencyPluralInfo::operator=(const CurrencyPluralInfo& info) {
 | |
|     if (this == &info) {
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     fInternalStatus = info.fInternalStatus;
 | |
|     if (U_FAILURE(fInternalStatus)) {
 | |
|         // bail out early if the object we were copying from was already 'invalid'.
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     deleteHash(fPluralCountToCurrencyUnitPattern);
 | |
|     fPluralCountToCurrencyUnitPattern = initHash(fInternalStatus);
 | |
|     copyHash(info.fPluralCountToCurrencyUnitPattern, 
 | |
|              fPluralCountToCurrencyUnitPattern, fInternalStatus);
 | |
|     if ( U_FAILURE(fInternalStatus) ) {
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     delete fPluralRules;
 | |
|     fPluralRules = nullptr;
 | |
|     delete fLocale;
 | |
|     fLocale = nullptr;
 | |
| 
 | |
|     if (info.fPluralRules != nullptr) {
 | |
|         fPluralRules = info.fPluralRules->clone();
 | |
|         if (fPluralRules == nullptr) {
 | |
|             fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
 | |
|             return *this;
 | |
|         }
 | |
|     }
 | |
|     if (info.fLocale != nullptr) {
 | |
|         fLocale = info.fLocale->clone();
 | |
|         if (fLocale == nullptr) {
 | |
|             // Note: If clone had an error parameter, then we could check/set that instead.
 | |
|             fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
 | |
|             return *this;
 | |
|         }
 | |
|         // If the other locale wasn't bogus, but our clone'd locale is bogus, then OOM happened
 | |
|         // during the call to clone().
 | |
|         if (!info.fLocale->isBogus() && fLocale->isBogus()) {
 | |
|             fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
 | |
|             return *this;
 | |
|         }
 | |
|     }
 | |
|     return *this;
 | |
| }
 | |
| 
 | |
| CurrencyPluralInfo::~CurrencyPluralInfo() {
 | |
|     deleteHash(fPluralCountToCurrencyUnitPattern);
 | |
|     fPluralCountToCurrencyUnitPattern = nullptr;
 | |
|     delete fPluralRules;
 | |
|     delete fLocale;
 | |
|     fPluralRules = nullptr;
 | |
|     fLocale = nullptr;
 | |
| }
 | |
| 
 | |
| bool
 | |
| CurrencyPluralInfo::operator==(const CurrencyPluralInfo& info) const {
 | |
| #ifdef CURRENCY_PLURAL_INFO_DEBUG
 | |
|     if (*fPluralRules == *info.fPluralRules) {
 | |
|         std::cout << "same plural rules\n";
 | |
|     }
 | |
|     if (*fLocale == *info.fLocale) {
 | |
|         std::cout << "same locale\n";
 | |
|     }
 | |
|     if (fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern)) {
 | |
|         std::cout << "same pattern\n";
 | |
|     }
 | |
| #endif
 | |
|     return *fPluralRules == *info.fPluralRules &&
 | |
|            *fLocale == *info.fLocale &&
 | |
|            fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern);
 | |
| }
 | |
| 
 | |
| 
 | |
| CurrencyPluralInfo*
 | |
| CurrencyPluralInfo::clone() const {
 | |
|     CurrencyPluralInfo* newObj = new CurrencyPluralInfo(*this);
 | |
|     // Since clone doesn't have a 'status' parameter, the best we can do is return nullptr
 | |
|     // if the new object was not full constructed properly (an error occurred).
 | |
|     if (newObj != nullptr && U_FAILURE(newObj->fInternalStatus)) {
 | |
|         delete newObj;
 | |
|         newObj = nullptr;
 | |
|     }
 | |
|     return newObj;
 | |
| }
 | |
| 
 | |
| const PluralRules* 
 | |
| CurrencyPluralInfo::getPluralRules() const {
 | |
|     return fPluralRules;
 | |
| }
 | |
| 
 | |
| UnicodeString&
 | |
| CurrencyPluralInfo::getCurrencyPluralPattern(const UnicodeString&  pluralCount,
 | |
|                                              UnicodeString& result) const {
 | |
|     const UnicodeString* currencyPluralPattern = 
 | |
|         (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(pluralCount);
 | |
|     if (currencyPluralPattern == nullptr) {
 | |
|         // fall back to "other"
 | |
|         if (pluralCount.compare(gPluralCountOther, 5)) {
 | |
|             currencyPluralPattern = 
 | |
|                 (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(UnicodeString(TRUE, gPluralCountOther, 5));
 | |
|         }
 | |
|         if (currencyPluralPattern == nullptr) {
 | |
|             // no currencyUnitPatterns defined, 
 | |
|             // fallback to predefined default.
 | |
|             // This should never happen when ICU resource files are
 | |
|             // available, since currencyUnitPattern of "other" is always
 | |
|             // defined in root.
 | |
|             result = UnicodeString(gDefaultCurrencyPluralPattern);
 | |
|             return result;
 | |
|         }
 | |
|     }
 | |
|     result = *currencyPluralPattern;
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| const Locale&
 | |
| CurrencyPluralInfo::getLocale() const {
 | |
|     return *fLocale;
 | |
| }
 | |
| 
 | |
| void
 | |
| CurrencyPluralInfo::setPluralRules(const UnicodeString& ruleDescription,
 | |
|                                    UErrorCode& status) {
 | |
|     if (U_SUCCESS(status)) {
 | |
|         delete fPluralRules;
 | |
|         fPluralRules = PluralRules::createRules(ruleDescription, status);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| CurrencyPluralInfo::setCurrencyPluralPattern(const UnicodeString& pluralCount,
 | |
|                                              const UnicodeString& pattern,
 | |
|                                              UErrorCode& status) {
 | |
|     if (U_SUCCESS(status)) {
 | |
|         UnicodeString* oldValue = static_cast<UnicodeString*>(
 | |
|             fPluralCountToCurrencyUnitPattern->get(pluralCount));
 | |
|         delete oldValue;
 | |
|         LocalPointer<UnicodeString> p(new UnicodeString(pattern), status);
 | |
|         if (U_SUCCESS(status)) {
 | |
|             // the p object allocated above will be owned by fPluralCountToCurrencyUnitPattern
 | |
|             // after the call to put(), even if the method returns failure.
 | |
|             fPluralCountToCurrencyUnitPattern->put(pluralCount, p.orphan(), status);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) {
 | |
|     initialize(loc, status);
 | |
| }
 | |
| 
 | |
| void 
 | |
| CurrencyPluralInfo::initialize(const Locale& loc, UErrorCode& status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return;
 | |
|     }
 | |
|     delete fLocale;
 | |
|     fLocale = nullptr;    
 | |
|     delete fPluralRules;
 | |
|     fPluralRules = nullptr;
 | |
| 
 | |
|     fLocale = loc.clone();
 | |
|     if (fLocale == nullptr) {
 | |
|         status = U_MEMORY_ALLOCATION_ERROR;
 | |
|         return;
 | |
|     }
 | |
|     // If the locale passed in wasn't bogus, but our clone'd locale is bogus, then OOM happened
 | |
|     // during the call to loc.clone().
 | |
|     if (!loc.isBogus() && fLocale->isBogus()) {
 | |
|         status = U_MEMORY_ALLOCATION_ERROR;
 | |
|         return;
 | |
|     }
 | |
|     fPluralRules = PluralRules::forLocale(loc, status);
 | |
|     setupCurrencyPluralPattern(loc, status);
 | |
| }
 | |
|    
 | |
| void
 | |
| CurrencyPluralInfo::setupCurrencyPluralPattern(const Locale& loc, UErrorCode& status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     deleteHash(fPluralCountToCurrencyUnitPattern);
 | |
|     fPluralCountToCurrencyUnitPattern = initHash(status);
 | |
|     if (U_FAILURE(status)) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(loc, status), status);
 | |
|     if (U_FAILURE(status)) {
 | |
|         return;
 | |
|     }
 | |
|     UErrorCode ec = U_ZERO_ERROR;
 | |
|     LocalUResourceBundlePointer rb(ures_open(nullptr, loc.getName(), &ec));
 | |
|     LocalUResourceBundlePointer numElements(ures_getByKeyWithFallback(rb.getAlias(), gNumberElementsTag, nullptr, &ec));
 | |
|     ures_getByKeyWithFallback(numElements.getAlias(), ns->getName(), rb.getAlias(), &ec);
 | |
|     ures_getByKeyWithFallback(rb.getAlias(), gPatternsTag, rb.getAlias(), &ec);
 | |
|     int32_t ptnLen;
 | |
|     const UChar* numberStylePattern = ures_getStringByKeyWithFallback(rb.getAlias(), gDecimalFormatTag, &ptnLen, &ec);
 | |
|     // Fall back to "latn" if num sys specific pattern isn't there.
 | |
|     if ( ec == U_MISSING_RESOURCE_ERROR && (uprv_strcmp(ns->getName(), gLatnTag) != 0)) {
 | |
|         ec = U_ZERO_ERROR;
 | |
|         ures_getByKeyWithFallback(numElements.getAlias(), gLatnTag, rb.getAlias(), &ec);
 | |
|         ures_getByKeyWithFallback(rb.getAlias(), gPatternsTag, rb.getAlias(), &ec);
 | |
|         numberStylePattern = ures_getStringByKeyWithFallback(rb.getAlias(), gDecimalFormatTag, &ptnLen, &ec);
 | |
|     }
 | |
|     int32_t numberStylePatternLen = ptnLen;
 | |
|     const UChar* negNumberStylePattern = nullptr;
 | |
|     int32_t negNumberStylePatternLen = 0;
 | |
|     // TODO: Java
 | |
|     // parse to check whether there is ";" separator in the numberStylePattern
 | |
|     UBool hasSeparator = false;
 | |
|     if (U_SUCCESS(ec)) {
 | |
|         for (int32_t styleCharIndex = 0; styleCharIndex < ptnLen; ++styleCharIndex) {
 | |
|             if (numberStylePattern[styleCharIndex] == gNumberPatternSeparator) {
 | |
|                 hasSeparator = true;
 | |
|                 // split the number style pattern into positive and negative
 | |
|                 negNumberStylePattern = numberStylePattern + styleCharIndex + 1;
 | |
|                 negNumberStylePatternLen = ptnLen - styleCharIndex - 1;
 | |
|                 numberStylePatternLen = styleCharIndex;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (U_FAILURE(ec)) {
 | |
|         // If OOM occurred during the above code, then we want to report that back to the caller.
 | |
|         if (ec == U_MEMORY_ALLOCATION_ERROR) {
 | |
|             status = ec;
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     LocalUResourceBundlePointer currRb(ures_open(U_ICUDATA_CURR, loc.getName(), &ec));
 | |
|     LocalUResourceBundlePointer currencyRes(ures_getByKeyWithFallback(currRb.getAlias(), gCurrUnitPtnTag, nullptr, &ec));
 | |
|     
 | |
| #ifdef CURRENCY_PLURAL_INFO_DEBUG
 | |
|     std::cout << "in set up\n";
 | |
| #endif
 | |
|     LocalPointer<StringEnumeration> keywords(fPluralRules->getKeywords(ec), ec);
 | |
|     if (U_SUCCESS(ec)) {
 | |
|         const char* pluralCount;
 | |
|         while (((pluralCount = keywords->next(nullptr, ec)) != nullptr) && U_SUCCESS(ec)) {
 | |
|             int32_t ptnLength;
 | |
|             UErrorCode err = U_ZERO_ERROR;
 | |
|             const UChar* patternChars = ures_getStringByKeyWithFallback(currencyRes.getAlias(), pluralCount, &ptnLength, &err);
 | |
|             if (err == U_MEMORY_ALLOCATION_ERROR || patternChars == nullptr) {
 | |
|                 ec = err;
 | |
|                 break;
 | |
|             }
 | |
|             if (U_SUCCESS(err) && ptnLength > 0) {
 | |
|                 UnicodeString* pattern = new UnicodeString(patternChars, ptnLength);
 | |
|                 if (pattern == nullptr) {
 | |
|                     ec = U_MEMORY_ALLOCATION_ERROR;
 | |
|                     break;
 | |
|                 }
 | |
| #ifdef CURRENCY_PLURAL_INFO_DEBUG
 | |
|                 char result_1[1000];
 | |
|                 pattern->extract(0, pattern->length(), result_1, "UTF-8");
 | |
|                 std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n";
 | |
| #endif
 | |
|                 pattern->findAndReplace(UnicodeString(TRUE, gPart0, 3), 
 | |
|                     UnicodeString(numberStylePattern, numberStylePatternLen));
 | |
|                 pattern->findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
 | |
| 
 | |
|                 if (hasSeparator) {
 | |
|                     UnicodeString negPattern(patternChars, ptnLength);
 | |
|                     negPattern.findAndReplace(UnicodeString(TRUE, gPart0, 3), 
 | |
|                         UnicodeString(negNumberStylePattern, negNumberStylePatternLen));
 | |
|                     negPattern.findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
 | |
|                     pattern->append(gNumberPatternSeparator);
 | |
|                     pattern->append(negPattern);
 | |
|                 }
 | |
| #ifdef CURRENCY_PLURAL_INFO_DEBUG
 | |
|                 pattern->extract(0, pattern->length(), result_1, "UTF-8");
 | |
|                 std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n";
 | |
| #endif
 | |
|                 // the 'pattern' object allocated above will be owned by the fPluralCountToCurrencyUnitPattern after the call to
 | |
|                 // put(), even if the method returns failure.
 | |
|                 fPluralCountToCurrencyUnitPattern->put(UnicodeString(pluralCount, -1, US_INV), pattern, status);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     // If OOM occurred during the above code, then we want to report that back to the caller.
 | |
|     if (ec == U_MEMORY_ALLOCATION_ERROR) {
 | |
|         status = ec;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| CurrencyPluralInfo::deleteHash(Hashtable* hTable) {
 | |
|     if ( hTable == nullptr ) {
 | |
|         return;
 | |
|     }
 | |
|     int32_t pos = UHASH_FIRST;
 | |
|     const UHashElement* element = nullptr;
 | |
|     while ( (element = hTable->nextElement(pos)) != nullptr ) {
 | |
|         const UHashTok valueTok = element->value;
 | |
|         const UnicodeString* value = (UnicodeString*)valueTok.pointer;
 | |
|         delete value;
 | |
|     }
 | |
|     delete hTable;
 | |
|     hTable = nullptr;
 | |
| }
 | |
| 
 | |
| Hashtable*
 | |
| CurrencyPluralInfo::initHash(UErrorCode& status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return nullptr;
 | |
|     }
 | |
|     LocalPointer<Hashtable> hTable(new Hashtable(TRUE, status), status);
 | |
|     if (U_FAILURE(status)) {
 | |
|         return nullptr;
 | |
|     }
 | |
|     hTable->setValueComparator(ValueComparator);
 | |
|     return hTable.orphan();
 | |
| }
 | |
| 
 | |
| void
 | |
| CurrencyPluralInfo::copyHash(const Hashtable* source,
 | |
|                            Hashtable* target,
 | |
|                            UErrorCode& status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return;
 | |
|     }
 | |
|     int32_t pos = UHASH_FIRST;
 | |
|     const UHashElement* element = nullptr;
 | |
|     if (source) {
 | |
|         while ( (element = source->nextElement(pos)) != nullptr ) {
 | |
|             const UHashTok keyTok = element->key;
 | |
|             const UnicodeString* key = (UnicodeString*)keyTok.pointer;
 | |
|             const UHashTok valueTok = element->value;
 | |
|             const UnicodeString* value = (UnicodeString*)valueTok.pointer;
 | |
|             LocalPointer<UnicodeString> copy(new UnicodeString(*value), status);
 | |
|             if (U_FAILURE(status)) {
 | |
|                 return;
 | |
|             }
 | |
|             // The HashTable owns the 'copy' object after the call to put().
 | |
|             target->put(UnicodeString(*key), copy.orphan(), status);
 | |
|             if (U_FAILURE(status)) {
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| U_NAMESPACE_END
 | |
| 
 | |
| #endif
 |