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