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 
     21 #include "unicode/locid.h"
     22 #include "unicode/plurrule.h"
     23 #include "unicode/strenum.h"
     24 #include "unicode/ures.h"
     25 #include "unicode/numsys.h"
     26 #include "cstring.h"
     27 #include "hash.h"
     28 #include "uresimp.h"
     29 #include "ureslocs.h"
     30 
     31 U_NAMESPACE_BEGIN
     32 
     33 
     34 static const UChar gNumberPatternSeparator = 0x3B; // ;
     35 
     36 U_CDECL_BEGIN
     37 
     38 /**
     39  * @internal ICU 4.2
     40  */
     41 static UBool U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2);
     42 
     43 UBool
     44 U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2) {
     45     const UnicodeString* affix_1 = (UnicodeString*)val1.pointer;
     46     const UnicodeString* affix_2 = (UnicodeString*)val2.pointer;
     47     return  *affix_1 == *affix_2;
     48 }
     49 
     50 U_CDECL_END
     51 
     52 
     53 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo)
     54 
     55 static const UChar gDefaultCurrencyPluralPattern[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0};
     56 static const UChar gTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0};
     57 static const UChar gPluralCountOther[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0};
     58 static const UChar gPart0[] = {0x7B, 0x30, 0x7D, 0};
     59 static const UChar gPart1[] = {0x7B, 0x31, 0x7D, 0};
     60 
     61 static const char gNumberElementsTag[]="NumberElements";
     62 static const char gLatnTag[]="latn";
     63 static const char gPatternsTag[]="patterns";
     64 static const char gDecimalFormatTag[]="decimalFormat";
     65 static const char gCurrUnitPtnTag[]="CurrencyUnitPatterns";
     66 
     67 CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status)
     68 :   fPluralCountToCurrencyUnitPattern(NULL),
     69     fPluralRules(NULL),
     70     fLocale(NULL) {
     71     initialize(Locale::getDefault(), status);
     72 }
     73 
     74 CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status)
     75 :   fPluralCountToCurrencyUnitPattern(NULL),
     76     fPluralRules(NULL),
     77     fLocale(NULL) {
     78     initialize(locale, status);
     79 }
     80 
     81 CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info)
     82 :   UObject(info),
     83     fPluralCountToCurrencyUnitPattern(NULL),
     84     fPluralRules(NULL),
     85     fLocale(NULL) {
     86     *this = info;
     87 }
     88 
     89 
     90 CurrencyPluralInfo&
     91 CurrencyPluralInfo::operator=(const CurrencyPluralInfo& info) {
     92     if (this == &info) {
     93         return *this;
     94     }
     95 
     96     deleteHash(fPluralCountToCurrencyUnitPattern);
     97     UErrorCode status = U_ZERO_ERROR;
     98     fPluralCountToCurrencyUnitPattern = initHash(status);
     99     copyHash(info.fPluralCountToCurrencyUnitPattern,
    100              fPluralCountToCurrencyUnitPattern, status);
    101     if ( U_FAILURE(status) ) {
    102         return *this;
    103     }
    104 
    105     delete fPluralRules;
    106     delete fLocale;
    107     if (info.fPluralRules) {
    108         fPluralRules = info.fPluralRules->clone();
    109     } else {
    110         fPluralRules = NULL;
    111     }
    112     if (info.fLocale) {
    113         fLocale = info.fLocale->clone();
    114     } else {
    115         fLocale = NULL;
    116     }
    117     return *this;
    118 }
    119 
    120 
    121 CurrencyPluralInfo::~CurrencyPluralInfo() {
    122     deleteHash(fPluralCountToCurrencyUnitPattern);
    123     fPluralCountToCurrencyUnitPattern = NULL;
    124     delete fPluralRules;
    125     delete fLocale;
    126     fPluralRules = NULL;
    127     fLocale = NULL;
    128 }
    129 
    130 UBool
    131 CurrencyPluralInfo::operator==(const CurrencyPluralInfo& info) const {
    132 #ifdef CURRENCY_PLURAL_INFO_DEBUG
    133     if (*fPluralRules == *info.fPluralRules) {
    134         std::cout << "same plural rules\n";
    135     }
    136     if (*fLocale == *info.fLocale) {
    137         std::cout << "same locale\n";
    138     }
    139     if (fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern)) {
    140         std::cout << "same pattern\n";
    141     }
    142 #endif
    143     return *fPluralRules == *info.fPluralRules &&
    144            *fLocale == *info.fLocale &&
    145            fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern);
    146 }
    147 
    148 
    149 CurrencyPluralInfo*
    150 CurrencyPluralInfo::clone() const {
    151     return new CurrencyPluralInfo(*this);
    152 }
    153 
    154 const PluralRules*
    155 CurrencyPluralInfo::getPluralRules() const {
    156     return fPluralRules;
    157 }
    158 
    159 UnicodeString&
    160 CurrencyPluralInfo::getCurrencyPluralPattern(const UnicodeString&  pluralCount,
    161                                              UnicodeString& result) const {
    162     const UnicodeString* currencyPluralPattern =
    163         (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(pluralCount);
    164     if (currencyPluralPattern == NULL) {
    165         // fall back to "other"
    166         if (pluralCount.compare(gPluralCountOther, 5)) {
    167             currencyPluralPattern =
    168                 (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(UnicodeString(TRUE, gPluralCountOther, 5));
    169         }
    170         if (currencyPluralPattern == NULL) {
    171             // no currencyUnitPatterns defined,
    172             // fallback to predefined defult.
    173             // This should never happen when ICU resource files are
    174             // available, since currencyUnitPattern of "other" is always
    175             // defined in root.
    176             result = UnicodeString(gDefaultCurrencyPluralPattern);
    177             return result;
    178         }
    179     }
    180     result = *currencyPluralPattern;
    181     return result;
    182 }
    183 
    184 const Locale&
    185 CurrencyPluralInfo::getLocale() const {
    186     return *fLocale;
    187 }
    188 
    189 void
    190 CurrencyPluralInfo::setPluralRules(const UnicodeString& ruleDescription,
    191                                    UErrorCode& status) {
    192     if (U_SUCCESS(status)) {
    193         if (fPluralRules) {
    194             delete fPluralRules;
    195         }
    196         fPluralRules = PluralRules::createRules(ruleDescription, status);
    197     }
    198 }
    199 
    200 
    201 void
    202 CurrencyPluralInfo::setCurrencyPluralPattern(const UnicodeString& pluralCount,
    203                                              const UnicodeString& pattern,
    204                                              UErrorCode& status) {
    205     if (U_SUCCESS(status)) {
    206         fPluralCountToCurrencyUnitPattern->put(pluralCount, new UnicodeString(pattern), status);
    207     }
    208 }
    209 
    210 
    211 void
    212 CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) {
    213     initialize(loc, status);
    214 }
    215 
    216 
    217 void
    218 CurrencyPluralInfo::initialize(const Locale& loc, UErrorCode& status) {
    219     if (U_FAILURE(status)) {
    220         return;
    221     }
    222     delete fLocale;
    223     fLocale = loc.clone();
    224     if (fPluralRules) {
    225         delete fPluralRules;
    226     }
    227     fPluralRules = PluralRules::forLocale(loc, status);
    228     setupCurrencyPluralPattern(loc, status);
    229 }
    230 
    231 
    232 void
    233 CurrencyPluralInfo::setupCurrencyPluralPattern(const Locale& loc, UErrorCode& status) {
    234     if (U_FAILURE(status)) {
    235         return;
    236     }
    237 
    238     if (fPluralCountToCurrencyUnitPattern) {
    239         deleteHash(fPluralCountToCurrencyUnitPattern);
    240     }
    241     fPluralCountToCurrencyUnitPattern = initHash(status);
    242     if (U_FAILURE(status)) {
    243         return;
    244     }
    245 
    246     NumberingSystem *ns = NumberingSystem::createInstance(loc,status);
    247     UErrorCode ec = U_ZERO_ERROR;
    248     UResourceBundle *rb = ures_open(NULL, loc.getName(), &ec);
    249     UResourceBundle *numElements = ures_getByKeyWithFallback(rb, gNumberElementsTag, NULL, &ec);
    250     rb = ures_getByKeyWithFallback(numElements, ns->getName(), rb, &ec);
    251     rb = ures_getByKeyWithFallback(rb, gPatternsTag, rb, &ec);
    252     int32_t ptnLen;
    253     const UChar* numberStylePattern = ures_getStringByKeyWithFallback(rb, gDecimalFormatTag, &ptnLen, &ec);
    254     // Fall back to "latn" if num sys specific pattern isn't there.
    255     if ( ec == U_MISSING_RESOURCE_ERROR && uprv_strcmp(ns->getName(),gLatnTag)) {
    256         ec = U_ZERO_ERROR;
    257         rb = ures_getByKeyWithFallback(numElements, gLatnTag, rb, &ec);
    258         rb = ures_getByKeyWithFallback(rb, gPatternsTag, rb, &ec);
    259         numberStylePattern = ures_getStringByKeyWithFallback(rb, gDecimalFormatTag, &ptnLen, &ec);
    260     }
    261     int32_t numberStylePatternLen = ptnLen;
    262     const UChar* negNumberStylePattern = NULL;
    263     int32_t negNumberStylePatternLen = 0;
    264     // TODO: Java
    265     // parse to check whether there is ";" separator in the numberStylePattern
    266     UBool hasSeparator = false;
    267     if (U_SUCCESS(ec)) {
    268         for (int32_t styleCharIndex = 0; styleCharIndex < ptnLen; ++styleCharIndex) {
    269             if (numberStylePattern[styleCharIndex] == gNumberPatternSeparator) {
    270                 hasSeparator = true;
    271                 // split the number style pattern into positive and negative
    272                 negNumberStylePattern = numberStylePattern + styleCharIndex + 1;
    273                 negNumberStylePatternLen = ptnLen - styleCharIndex - 1;
    274                 numberStylePatternLen = styleCharIndex;
    275             }
    276         }
    277     }
    278 
    279     ures_close(numElements);
    280     ures_close(rb);
    281     delete ns;
    282 
    283     if (U_FAILURE(ec)) {
    284         return;
    285     }
    286 
    287     UResourceBundle *currRb = ures_open(U_ICUDATA_CURR, loc.getName(), &ec);
    288     UResourceBundle *currencyRes = ures_getByKeyWithFallback(currRb, gCurrUnitPtnTag, NULL, &ec);
    289 
    290 #ifdef CURRENCY_PLURAL_INFO_DEBUG
    291     std::cout << "in set up\n";
    292 #endif
    293     StringEnumeration* keywords = fPluralRules->getKeywords(ec);
    294     if (U_SUCCESS(ec)) {
    295         const char* pluralCount;
    296         while ((pluralCount = keywords->next(NULL, ec)) != NULL) {
    297             if ( U_SUCCESS(ec) ) {
    298                 int32_t ptnLen;
    299                 UErrorCode err = U_ZERO_ERROR;
    300                 const UChar* patternChars = ures_getStringByKeyWithFallback(
    301                     currencyRes, pluralCount, &ptnLen, &err);
    302                 if (U_SUCCESS(err) && ptnLen > 0) {
    303                     UnicodeString* pattern = new UnicodeString(patternChars, ptnLen);
    304 #ifdef CURRENCY_PLURAL_INFO_DEBUG
    305                     char result_1[1000];
    306                     pattern->extract(0, pattern->length(), result_1, "UTF-8");
    307                     std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n";
    308 #endif
    309                     pattern->findAndReplace(UnicodeString(TRUE, gPart0, 3),
    310                       UnicodeString(numberStylePattern, numberStylePatternLen));
    311                     pattern->findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
    312 
    313                     if (hasSeparator) {
    314                         UnicodeString negPattern(patternChars, ptnLen);
    315                         negPattern.findAndReplace(UnicodeString(TRUE, gPart0, 3),
    316                           UnicodeString(negNumberStylePattern, negNumberStylePatternLen));
    317                         negPattern.findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
    318                         pattern->append(gNumberPatternSeparator);
    319                         pattern->append(negPattern);
    320                     }
    321 #ifdef CURRENCY_PLURAL_INFO_DEBUG
    322                     pattern->extract(0, pattern->length(), result_1, "UTF-8");
    323                     std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n";
    324 #endif
    325 
    326                     fPluralCountToCurrencyUnitPattern->put(UnicodeString(pluralCount, -1, US_INV), pattern, status);
    327                 }
    328             }
    329         }
    330     }
    331     delete keywords;
    332     ures_close(currencyRes);
    333     ures_close(currRb);
    334 }
    335 
    336 
    337 
    338 void
    339 CurrencyPluralInfo::deleteHash(Hashtable* hTable)
    340 {
    341     if ( hTable == NULL ) {
    342         return;
    343     }
    344     int32_t pos = UHASH_FIRST;
    345     const UHashElement* element = NULL;
    346     while ( (element = hTable->nextElement(pos)) != NULL ) {
    347         const UHashTok valueTok = element->value;
    348         const UnicodeString* value = (UnicodeString*)valueTok.pointer;
    349         delete value;
    350     }
    351     delete hTable;
    352     hTable = NULL;
    353 }
    354 
    355 
    356 Hashtable*
    357 CurrencyPluralInfo::initHash(UErrorCode& status) {
    358     if ( U_FAILURE(status) ) {
    359         return NULL;
    360     }
    361     Hashtable* hTable;
    362     if ( (hTable = new Hashtable(TRUE, status)) == NULL ) {
    363         status = U_MEMORY_ALLOCATION_ERROR;
    364         return NULL;
    365     }
    366     if ( U_FAILURE(status) ) {
    367         delete hTable;
    368         return NULL;
    369     }
    370     hTable->setValueComparator(ValueComparator);
    371     return hTable;
    372 }
    373 
    374 
    375 void
    376 CurrencyPluralInfo::copyHash(const Hashtable* source,
    377                            Hashtable* target,
    378                            UErrorCode& status) {
    379     if ( U_FAILURE(status) ) {
    380         return;
    381     }
    382     int32_t pos = UHASH_FIRST;
    383     const UHashElement* element = NULL;
    384     if ( source ) {
    385         while ( (element = source->nextElement(pos)) != NULL ) {
    386             const UHashTok keyTok = element->key;
    387             const UnicodeString* key = (UnicodeString*)keyTok.pointer;
    388             const UHashTok valueTok = element->value;
    389             const UnicodeString* value = (UnicodeString*)valueTok.pointer;
    390             UnicodeString* copy = new UnicodeString(*value);
    391             target->put(UnicodeString(*key), copy, status);
    392             if ( U_FAILURE(status) ) {
    393                 return;
    394             }
    395         }
    396     }
    397 }
    398 
    399 
    400 U_NAMESPACE_END
    401 
    402 #endif
    403