tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

currpinf.cpp (15928B)


      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 char16_t 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 char16_t gDefaultCurrencyPluralPattern[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0};
     54 static const char16_t gTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0};
     55 static const char16_t gPluralCountOther[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0};
     56 static const char16_t gPart0[] = {0x7B, 0x30, 0x7D, 0};
     57 static const char16_t 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 bool
    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        static_cast<UnicodeString*>(fPluralCountToCurrencyUnitPattern->get(pluralCount));
    189    if (currencyPluralPattern == nullptr) {
    190        // fall back to "other"
    191        if (pluralCount.compare(gPluralCountOther, 5)) {
    192            currencyPluralPattern = 
    193                static_cast<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 char16_t* 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 char16_t* 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 char16_t* 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 = static_cast<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 = static_cast<UnicodeString*>(keyTok.pointer);
    424            const UHashTok valueTok = element->value;
    425            const UnicodeString* value = static_cast<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