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