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