Home | History | Annotate | Download | only in i18n
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4  *******************************************************************************
      5  * Copyright (C) 2009-2014, International Business Machines Corporation and
      6  * others. All Rights Reserved.
      7  *******************************************************************************
      8  */
      9 
     10 #include "unicode/currpinf.h"
     11 
     12 #if !UCONFIG_NO_FORMATTING
     13 
     14 //#define CURRENCY_PLURAL_INFO_DEBUG 1
     15 
     16 #ifdef CURRENCY_PLURAL_INFO_DEBUG
     17 #include <iostream>
     18 #endif
     19 
     20 #include "unicode/locid.h"
     21 #include "unicode/plurrule.h"
     22 #include "unicode/strenum.h"
     23 #include "unicode/ures.h"
     24 #include "unicode/numsys.h"
     25 #include "cstring.h"
     26 #include "hash.h"
     27 #include "uresimp.h"
     28 #include "ureslocs.h"
     29 
     30 U_NAMESPACE_BEGIN
     31 
     32 static const UChar gNumberPatternSeparator = 0x3B; // ;
     33 
     34 U_CDECL_BEGIN
     35 
     36 /**
     37  * @internal ICU 4.2
     38  */
     39 static UBool U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2);
     40 
     41 UBool
     42 U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2) {
     43     const UnicodeString* affix_1 = (UnicodeString*)val1.pointer;
     44     const UnicodeString* affix_2 = (UnicodeString*)val2.pointer;
     45     return  *affix_1 == *affix_2;
     46 }
     47 
     48 U_CDECL_END
     49 
     50 
     51 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo)
     52 
     53 static const UChar gDefaultCurrencyPluralPattern[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0};
     54 static const UChar gTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0};
     55 static const UChar gPluralCountOther[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0};
     56 static const UChar gPart0[] = {0x7B, 0x30, 0x7D, 0};
     57 static const UChar gPart1[] = {0x7B, 0x31, 0x7D, 0};
     58 
     59 static const char gNumberElementsTag[]="NumberElements";
     60 static const char gLatnTag[]="latn";
     61 static const char gPatternsTag[]="patterns";
     62 static const char gDecimalFormatTag[]="decimalFormat";
     63 static const char gCurrUnitPtnTag[]="CurrencyUnitPatterns";
     64 
     65 CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status)
     66 :   fPluralCountToCurrencyUnitPattern(nullptr),
     67     fPluralRules(nullptr),
     68     fLocale(nullptr),
     69     fInternalStatus(U_ZERO_ERROR) {
     70     initialize(Locale::getDefault(), status);
     71 }
     72 
     73 CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status)
     74 :   fPluralCountToCurrencyUnitPattern(nullptr),
     75     fPluralRules(nullptr),
     76     fLocale(nullptr),
     77     fInternalStatus(U_ZERO_ERROR) {
     78     initialize(locale, status);
     79 }
     80 
     81 CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info)
     82 :   UObject(info),
     83     fPluralCountToCurrencyUnitPattern(nullptr),
     84     fPluralRules(nullptr),
     85     fLocale(nullptr),
     86     fInternalStatus(U_ZERO_ERROR) {
     87     *this = info;
     88 }
     89 
     90 CurrencyPluralInfo&
     91 CurrencyPluralInfo::operator=(const CurrencyPluralInfo& info) {
     92     if (this == &info) {
     93         return *this;
     94     }
     95 
     96     fInternalStatus = info.fInternalStatus;
     97     if (U_FAILURE(fInternalStatus)) {
     98         // bail out early if the object we were copying from was already 'invalid'.
     99         return *this;
    100     }
    101 
    102     deleteHash(fPluralCountToCurrencyUnitPattern);
    103     fPluralCountToCurrencyUnitPattern = initHash(fInternalStatus);
    104     copyHash(info.fPluralCountToCurrencyUnitPattern,
    105              fPluralCountToCurrencyUnitPattern, fInternalStatus);
    106     if ( U_FAILURE(fInternalStatus) ) {
    107         return *this;
    108     }
    109 
    110     delete fPluralRules;
    111     fPluralRules = nullptr;
    112     delete fLocale;
    113     fLocale = nullptr;
    114 
    115     if (info.fPluralRules != nullptr) {
    116         fPluralRules = info.fPluralRules->clone();
    117         if (fPluralRules == nullptr) {
    118             fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
    119             return *this;
    120         }
    121     }
    122     if (info.fLocale != nullptr) {
    123         fLocale = info.fLocale->clone();
    124         if (fLocale == nullptr) {
    125             // Note: If clone had an error parameter, then we could check/set that instead.
    126             fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
    127             return *this;
    128         }
    129         // If the other locale wasn't bogus, but our clone'd locale is bogus, then OOM happened
    130         // during the call to clone().
    131         if (!info.fLocale->isBogus() && fLocale->isBogus()) {
    132             fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
    133             return *this;
    134         }
    135     }
    136     return *this;
    137 }
    138 
    139 CurrencyPluralInfo::~CurrencyPluralInfo() {
    140     deleteHash(fPluralCountToCurrencyUnitPattern);
    141     fPluralCountToCurrencyUnitPattern = nullptr;
    142     delete fPluralRules;
    143     delete fLocale;
    144     fPluralRules = nullptr;
    145     fLocale = nullptr;
    146 }
    147 
    148 UBool
    149 CurrencyPluralInfo::operator==(const CurrencyPluralInfo& info) const {
    150 #ifdef CURRENCY_PLURAL_INFO_DEBUG
    151     if (*fPluralRules == *info.fPluralRules) {
    152         std::cout << "same plural rules\n";
    153     }
    154     if (*fLocale == *info.fLocale) {
    155         std::cout << "same locale\n";
    156     }
    157     if (fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern)) {
    158         std::cout << "same pattern\n";
    159     }
    160 #endif
    161     return *fPluralRules == *info.fPluralRules &&
    162            *fLocale == *info.fLocale &&
    163            fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern);
    164 }
    165 
    166 
    167 CurrencyPluralInfo*
    168 CurrencyPluralInfo::clone() const {
    169     CurrencyPluralInfo* newObj = new CurrencyPluralInfo(*this);
    170     // Since clone doesn't have a 'status' parameter, the best we can do is return nullptr
    171     // if the new object was not full constructed properly (an error occurred).
    172     if (newObj != nullptr && U_FAILURE(newObj->fInternalStatus)) {
    173         delete newObj;
    174         newObj = nullptr;
    175     }
    176     return newObj;
    177 }
    178 
    179 const PluralRules*
    180 CurrencyPluralInfo::getPluralRules() const {
    181     return fPluralRules;
    182 }
    183 
    184 UnicodeString&
    185 CurrencyPluralInfo::getCurrencyPluralPattern(const UnicodeString&  pluralCount,
    186                                              UnicodeString& result) const {
    187     const UnicodeString* currencyPluralPattern =
    188         (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(pluralCount);
    189     if (currencyPluralPattern == nullptr) {
    190         // fall back to "other"
    191         if (pluralCount.compare(gPluralCountOther, 5)) {
    192             currencyPluralPattern =
    193                 (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(UnicodeString(TRUE, gPluralCountOther, 5));
    194         }
    195         if (currencyPluralPattern == nullptr) {
    196             // no currencyUnitPatterns defined,
    197             // fallback to predefined default.
    198             // This should never happen when ICU resource files are
    199             // available, since currencyUnitPattern of "other" is always
    200             // defined in root.
    201             result = UnicodeString(gDefaultCurrencyPluralPattern);
    202             return result;
    203         }
    204     }
    205     result = *currencyPluralPattern;
    206     return result;
    207 }
    208 
    209 const Locale&
    210 CurrencyPluralInfo::getLocale() const {
    211     return *fLocale;
    212 }
    213 
    214 void
    215 CurrencyPluralInfo::setPluralRules(const UnicodeString& ruleDescription,
    216                                    UErrorCode& status) {
    217     if (U_SUCCESS(status)) {
    218         delete fPluralRules;
    219         fPluralRules = PluralRules::createRules(ruleDescription, status);
    220     }
    221 }
    222 
    223 void
    224 CurrencyPluralInfo::setCurrencyPluralPattern(const UnicodeString& pluralCount,
    225                                              const UnicodeString& pattern,
    226                                              UErrorCode& status) {
    227     if (U_SUCCESS(status)) {
    228         UnicodeString* oldValue = static_cast<UnicodeString*>(
    229             fPluralCountToCurrencyUnitPattern->get(pluralCount));
    230         delete oldValue;
    231         LocalPointer<UnicodeString> p(new UnicodeString(pattern), status);
    232         if (U_SUCCESS(status)) {
    233             // the p object allocated above will be owned by fPluralCountToCurrencyUnitPattern
    234             // after the call to put(), even if the method returns failure.
    235             fPluralCountToCurrencyUnitPattern->put(pluralCount, p.orphan(), status);
    236         }
    237     }
    238 }
    239 
    240 void
    241 CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) {
    242     initialize(loc, status);
    243 }
    244 
    245 void
    246 CurrencyPluralInfo::initialize(const Locale& loc, UErrorCode& status) {
    247     if (U_FAILURE(status)) {
    248         return;
    249     }
    250     delete fLocale;
    251     fLocale = nullptr;
    252     delete fPluralRules;
    253     fPluralRules = nullptr;
    254 
    255     fLocale = loc.clone();
    256     if (fLocale == nullptr) {
    257         status = U_MEMORY_ALLOCATION_ERROR;
    258         return;
    259     }
    260     // If the locale passed in wasn't bogus, but our clone'd locale is bogus, then OOM happened
    261     // during the call to loc.clone().
    262     if (!loc.isBogus() && fLocale->isBogus()) {
    263         status = U_MEMORY_ALLOCATION_ERROR;
    264         return;
    265     }
    266     fPluralRules = PluralRules::forLocale(loc, status);
    267     setupCurrencyPluralPattern(loc, status);
    268 }
    269 
    270 void
    271 CurrencyPluralInfo::setupCurrencyPluralPattern(const Locale& loc, UErrorCode& status) {
    272     if (U_FAILURE(status)) {
    273         return;
    274     }
    275 
    276     deleteHash(fPluralCountToCurrencyUnitPattern);
    277     fPluralCountToCurrencyUnitPattern = initHash(status);
    278     if (U_FAILURE(status)) {
    279         return;
    280     }
    281 
    282     LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(loc, status), status);
    283     if (U_FAILURE(status)) {
    284         return;
    285     }
    286     UErrorCode ec = U_ZERO_ERROR;
    287     LocalUResourceBundlePointer rb(ures_open(nullptr, loc.getName(), &ec));
    288     LocalUResourceBundlePointer numElements(ures_getByKeyWithFallback(rb.getAlias(), gNumberElementsTag, nullptr, &ec));
    289     ures_getByKeyWithFallback(numElements.getAlias(), ns->getName(), rb.getAlias(), &ec);
    290     ures_getByKeyWithFallback(rb.getAlias(), gPatternsTag, rb.getAlias(), &ec);
    291     int32_t ptnLen;
    292     const UChar* numberStylePattern = ures_getStringByKeyWithFallback(rb.getAlias(), gDecimalFormatTag, &ptnLen, &ec);
    293     // Fall back to "latn" if num sys specific pattern isn't there.
    294     if ( ec == U_MISSING_RESOURCE_ERROR && (uprv_strcmp(ns->getName(), gLatnTag) != 0)) {
    295         ec = U_ZERO_ERROR;
    296         ures_getByKeyWithFallback(numElements.getAlias(), gLatnTag, rb.getAlias(), &ec);
    297         ures_getByKeyWithFallback(rb.getAlias(), gPatternsTag, rb.getAlias(), &ec);
    298         numberStylePattern = ures_getStringByKeyWithFallback(rb.getAlias(), gDecimalFormatTag, &ptnLen, &ec);
    299     }
    300     int32_t numberStylePatternLen = ptnLen;
    301     const UChar* negNumberStylePattern = nullptr;
    302     int32_t negNumberStylePatternLen = 0;
    303     // TODO: Java
    304     // parse to check whether there is ";" separator in the numberStylePattern
    305     UBool hasSeparator = false;
    306     if (U_SUCCESS(ec)) {
    307         for (int32_t styleCharIndex = 0; styleCharIndex < ptnLen; ++styleCharIndex) {
    308             if (numberStylePattern[styleCharIndex] == gNumberPatternSeparator) {
    309                 hasSeparator = true;
    310                 // split the number style pattern into positive and negative
    311                 negNumberStylePattern = numberStylePattern + styleCharIndex + 1;
    312                 negNumberStylePatternLen = ptnLen - styleCharIndex - 1;
    313                 numberStylePatternLen = styleCharIndex;
    314             }
    315         }
    316     }
    317 
    318     if (U_FAILURE(ec)) {
    319         // If OOM occurred during the above code, then we want to report that back to the caller.
    320         if (ec == U_MEMORY_ALLOCATION_ERROR) {
    321             status = ec;
    322         }
    323         return;
    324     }
    325 
    326     LocalUResourceBundlePointer currRb(ures_open(U_ICUDATA_CURR, loc.getName(), &ec));
    327     LocalUResourceBundlePointer currencyRes(ures_getByKeyWithFallback(currRb.getAlias(), gCurrUnitPtnTag, nullptr, &ec));
    328 
    329 #ifdef CURRENCY_PLURAL_INFO_DEBUG
    330     std::cout << "in set up\n";
    331 #endif
    332     LocalPointer<StringEnumeration> keywords(fPluralRules->getKeywords(ec), ec);
    333     if (U_SUCCESS(ec)) {
    334         const char* pluralCount;
    335         while (((pluralCount = keywords->next(nullptr, ec)) != nullptr) && U_SUCCESS(ec)) {
    336             int32_t ptnLength;
    337             UErrorCode err = U_ZERO_ERROR;
    338             const UChar* patternChars = ures_getStringByKeyWithFallback(currencyRes.getAlias(), pluralCount, &ptnLength, &err);
    339             if (err == U_MEMORY_ALLOCATION_ERROR || patternChars == nullptr) {
    340                 ec = err;
    341                 break;
    342             }
    343             if (U_SUCCESS(err) && ptnLength > 0) {
    344                 UnicodeString* pattern = new UnicodeString(patternChars, ptnLength);
    345                 if (pattern == nullptr) {
    346                     ec = U_MEMORY_ALLOCATION_ERROR;
    347                     break;
    348                 }
    349 #ifdef CURRENCY_PLURAL_INFO_DEBUG
    350                 char result_1[1000];
    351                 pattern->extract(0, pattern->length(), result_1, "UTF-8");
    352                 std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n";
    353 #endif
    354                 pattern->findAndReplace(UnicodeString(TRUE, gPart0, 3),
    355                     UnicodeString(numberStylePattern, numberStylePatternLen));
    356                 pattern->findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
    357 
    358                 if (hasSeparator) {
    359                     UnicodeString negPattern(patternChars, ptnLength);
    360                     negPattern.findAndReplace(UnicodeString(TRUE, gPart0, 3),
    361                         UnicodeString(negNumberStylePattern, negNumberStylePatternLen));
    362                     negPattern.findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
    363                     pattern->append(gNumberPatternSeparator);
    364                     pattern->append(negPattern);
    365                 }
    366 #ifdef CURRENCY_PLURAL_INFO_DEBUG
    367                 pattern->extract(0, pattern->length(), result_1, "UTF-8");
    368                 std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n";
    369 #endif
    370                 // the 'pattern' object allocated above will be owned by the fPluralCountToCurrencyUnitPattern after the call to
    371                 // put(), even if the method returns failure.
    372                 fPluralCountToCurrencyUnitPattern->put(UnicodeString(pluralCount, -1, US_INV), pattern, status);
    373             }
    374         }
    375     }
    376     // If OOM occurred during the above code, then we want to report that back to the caller.
    377     if (ec == U_MEMORY_ALLOCATION_ERROR) {
    378         status = ec;
    379     }
    380 }
    381 
    382 void
    383 CurrencyPluralInfo::deleteHash(Hashtable* hTable) {
    384     if ( hTable == nullptr ) {
    385         return;
    386     }
    387     int32_t pos = UHASH_FIRST;
    388     const UHashElement* element = nullptr;
    389     while ( (element = hTable->nextElement(pos)) != nullptr ) {
    390         const UHashTok valueTok = element->value;
    391         const UnicodeString* value = (UnicodeString*)valueTok.pointer;
    392         delete value;
    393     }
    394     delete hTable;
    395     hTable = nullptr;
    396 }
    397 
    398 Hashtable*
    399 CurrencyPluralInfo::initHash(UErrorCode& status) {
    400     if (U_FAILURE(status)) {
    401         return nullptr;
    402     }
    403     LocalPointer<Hashtable> hTable(new Hashtable(TRUE, status), status);
    404     if (U_FAILURE(status)) {
    405         return nullptr;
    406     }
    407     hTable->setValueComparator(ValueComparator);
    408     return hTable.orphan();
    409 }
    410 
    411 void
    412 CurrencyPluralInfo::copyHash(const Hashtable* source,
    413                            Hashtable* target,
    414                            UErrorCode& status) {
    415     if (U_FAILURE(status)) {
    416         return;
    417     }
    418     int32_t pos = UHASH_FIRST;
    419     const UHashElement* element = nullptr;
    420     if (source) {
    421         while ( (element = source->nextElement(pos)) != nullptr ) {
    422             const UHashTok keyTok = element->key;
    423             const UnicodeString* key = (UnicodeString*)keyTok.pointer;
    424             const UHashTok valueTok = element->value;
    425             const UnicodeString* value = (UnicodeString*)valueTok.pointer;
    426             LocalPointer<UnicodeString> copy(new UnicodeString(*value), status);
    427             if (U_FAILURE(status)) {
    428                 return;
    429             }
    430             // The HashTable owns the 'copy' object after the call to put().
    431             target->put(UnicodeString(*key), copy.orphan(), status);
    432             if (U_FAILURE(status)) {
    433                 return;
    434             }
    435         }
    436     }
    437 }
    438 
    439 U_NAMESPACE_END
    440 
    441 #endif
    442