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 "cstring.h"
23#include "hash.h"
24#include "uresimp.h"
25#include "ureslocs.h"
26
27U_NAMESPACE_BEGIN
28
29
30static const UChar gNumberPatternSeparator = 0x3B; // ;
31
32U_CDECL_BEGIN
33
34/**
35 * @internal ICU 4.2
36 */
37static UBool U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2);
38
39UBool
40U_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
46U_CDECL_END
47
48
49UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo)
50
51static const UChar gDefaultCurrencyPluralPattern[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0};
52static const UChar gTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0};
53static const UChar gPluralCountOther[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0};
54static const UChar gPart0[] = {0x7B, 0x30, 0x7D, 0};
55static const UChar gPart1[] = {0x7B, 0x31, 0x7D, 0};
56
57static const char gNumberElementsTag[]="NumberElements";
58static const char gLatnTag[]="latn";
59static const char gPatternsTag[]="patterns";
60static const char gDecimalFormatTag[]="decimalFormat";
61static const char gCurrUnitPtnTag[]="CurrencyUnitPatterns";
62
63CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status)
64:   fPluralCountToCurrencyUnitPattern(NULL),
65    fPluralRules(NULL),
66    fLocale(NULL) {
67    initialize(Locale::getDefault(), status);
68}
69
70CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status)
71:   fPluralCountToCurrencyUnitPattern(NULL),
72    fPluralRules(NULL),
73    fLocale(NULL) {
74    initialize(locale, status);
75}
76
77CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info)
78:   UObject(info),
79    fPluralCountToCurrencyUnitPattern(NULL),
80    fPluralRules(NULL),
81    fLocale(NULL) {
82    *this = info;
83}
84
85
86CurrencyPluralInfo&
87CurrencyPluralInfo::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
117CurrencyPluralInfo::~CurrencyPluralInfo() {
118    deleteHash(fPluralCountToCurrencyUnitPattern);
119    fPluralCountToCurrencyUnitPattern = NULL;
120    delete fPluralRules;
121    delete fLocale;
122    fPluralRules = NULL;
123    fLocale = NULL;
124}
125
126UBool
127CurrencyPluralInfo::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
145CurrencyPluralInfo*
146CurrencyPluralInfo::clone() const {
147    return new CurrencyPluralInfo(*this);
148}
149
150const PluralRules*
151CurrencyPluralInfo::getPluralRules() const {
152    return fPluralRules;
153}
154
155UnicodeString&
156CurrencyPluralInfo::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, 5)) {
163            currencyPluralPattern =
164                (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(UnicodeString(TRUE, gPluralCountOther, 5));
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
180const Locale&
181CurrencyPluralInfo::getLocale() const {
182    return *fLocale;
183}
184
185void
186CurrencyPluralInfo::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
197void
198CurrencyPluralInfo::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
207void
208CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) {
209    initialize(loc, status);
210}
211
212
213void
214CurrencyPluralInfo::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
228void
229CurrencyPluralInfo::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_getByKeyWithFallback(rb, gNumberElementsTag, rb, &ec);
245    rb = ures_getByKeyWithFallback(rb, gLatnTag, rb, &ec);
246    rb = ures_getByKeyWithFallback(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(UnicodeString(TRUE, gPart0, 3),
295                      UnicodeString(numberStylePattern, numberStylePatternLen));
296                    pattern->findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
297
298                    if (hasSeparator) {
299                        UnicodeString negPattern(patternChars, ptnLen);
300                        negPattern.findAndReplace(UnicodeString(TRUE, gPart0, 3),
301                          UnicodeString(negNumberStylePattern, negNumberStylePatternLen));
302                        negPattern.findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
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, -1, US_INV), pattern, status);
312                }
313            }
314        }
315    }
316    delete keywords;
317    ures_close(currencyRes);
318    ures_close(currRb);
319}
320
321
322
323void
324CurrencyPluralInfo::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
342Hashtable*
343CurrencyPluralInfo::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    if ( U_FAILURE(status) ) {
353        delete hTable;
354        return NULL;
355    }
356    hTable->setValueComparator(ValueComparator);
357    return hTable;
358}
359
360
361void
362CurrencyPluralInfo::copyHash(const Hashtable* source,
363                           Hashtable* target,
364                           UErrorCode& status) {
365    if ( U_FAILURE(status) ) {
366        return;
367    }
368    int32_t pos = -1;
369    const UHashElement* element = NULL;
370    if ( source ) {
371        while ( (element = source->nextElement(pos)) != NULL ) {
372            const UHashTok keyTok = element->key;
373            const UnicodeString* key = (UnicodeString*)keyTok.pointer;
374            const UHashTok valueTok = element->value;
375            const UnicodeString* value = (UnicodeString*)valueTok.pointer;
376            UnicodeString* copy = new UnicodeString(*value);
377            target->put(UnicodeString(*key), copy, status);
378            if ( U_FAILURE(status) ) {
379                return;
380            }
381        }
382    }
383}
384
385
386U_NAMESPACE_END
387
388#endif
389