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