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