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
28U_NAMESPACE_BEGIN
29
30
31static const UChar gNumberPatternSeparator = 0x3B; // ;
32
33U_CDECL_BEGIN
34
35/**
36 * @internal ICU 4.2
37 */
38static UBool U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2);
39
40UBool
41U_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
47U_CDECL_END
48
49
50UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo)
51
52static const UChar gDefaultCurrencyPluralPattern[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0};
53static const UChar gTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0};
54static const UChar gPluralCountOther[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0};
55static const UChar gPart0[] = {0x7B, 0x30, 0x7D, 0};
56static const UChar gPart1[] = {0x7B, 0x31, 0x7D, 0};
57
58static const char gNumberElementsTag[]="NumberElements";
59static const char gLatnTag[]="latn";
60static const char gPatternsTag[]="patterns";
61static const char gDecimalFormatTag[]="decimalFormat";
62static const char gCurrUnitPtnTag[]="CurrencyUnitPatterns";
63
64CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status)
65:   fPluralCountToCurrencyUnitPattern(NULL),
66    fPluralRules(NULL),
67    fLocale(NULL) {
68    initialize(Locale::getDefault(), status);
69}
70
71CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status)
72:   fPluralCountToCurrencyUnitPattern(NULL),
73    fPluralRules(NULL),
74    fLocale(NULL) {
75    initialize(locale, status);
76}
77
78CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info)
79:   UObject(info),
80    fPluralCountToCurrencyUnitPattern(NULL),
81    fPluralRules(NULL),
82    fLocale(NULL) {
83    *this = info;
84}
85
86
87CurrencyPluralInfo&
88CurrencyPluralInfo::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
118CurrencyPluralInfo::~CurrencyPluralInfo() {
119    deleteHash(fPluralCountToCurrencyUnitPattern);
120    fPluralCountToCurrencyUnitPattern = NULL;
121    delete fPluralRules;
122    delete fLocale;
123    fPluralRules = NULL;
124    fLocale = NULL;
125}
126
127UBool
128CurrencyPluralInfo::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
146CurrencyPluralInfo*
147CurrencyPluralInfo::clone() const {
148    return new CurrencyPluralInfo(*this);
149}
150
151const PluralRules*
152CurrencyPluralInfo::getPluralRules() const {
153    return fPluralRules;
154}
155
156UnicodeString&
157CurrencyPluralInfo::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
181const Locale&
182CurrencyPluralInfo::getLocale() const {
183    return *fLocale;
184}
185
186void
187CurrencyPluralInfo::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
198void
199CurrencyPluralInfo::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
208void
209CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) {
210    initialize(loc, status);
211}
212
213
214void
215CurrencyPluralInfo::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
229void
230CurrencyPluralInfo::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
335void
336CurrencyPluralInfo::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
353Hashtable*
354CurrencyPluralInfo::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
372void
373CurrencyPluralInfo::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
397U_NAMESPACE_END
398
399#endif
400