1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2009-2014, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
10 #include "unicode/currpinf.h"
12 #if !UCONFIG_NO_FORMATTING
14 //#define CURRENCY_PLURAL_INFO_DEBUG 1
16 #ifdef CURRENCY_PLURAL_INFO_DEBUG
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"
34 static const UChar gNumberPatternSeparator
= 0x3B; // ;
41 static UBool U_CALLCONV
ValueComparator(UHashTok val1
, UHashTok val2
);
44 U_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
;
53 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo
)
55 static const UChar gDefaultCurrencyPluralPattern
[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0};
56 static const UChar gTripleCurrencySign
[] = {0xA4, 0xA4, 0xA4, 0};
57 static const UChar gPluralCountOther
[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0};
58 static const UChar gPart0
[] = {0x7B, 0x30, 0x7D, 0};
59 static const UChar gPart1
[] = {0x7B, 0x31, 0x7D, 0};
61 static const char gNumberElementsTag
[]="NumberElements";
62 static const char gLatnTag
[]="latn";
63 static const char gPatternsTag
[]="patterns";
64 static const char gDecimalFormatTag
[]="decimalFormat";
65 static const char gCurrUnitPtnTag
[]="CurrencyUnitPatterns";
67 CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode
& status
)
68 : fPluralCountToCurrencyUnitPattern(NULL
),
71 initialize(Locale::getDefault(), status
);
74 CurrencyPluralInfo::CurrencyPluralInfo(const Locale
& locale
, UErrorCode
& status
)
75 : fPluralCountToCurrencyUnitPattern(NULL
),
78 initialize(locale
, status
);
81 CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo
& info
)
83 fPluralCountToCurrencyUnitPattern(NULL
),
91 CurrencyPluralInfo::operator=(const CurrencyPluralInfo
& info
) {
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
) ) {
107 if (info
.fPluralRules
) {
108 fPluralRules
= info
.fPluralRules
->clone();
113 fLocale
= info
.fLocale
->clone();
121 CurrencyPluralInfo::~CurrencyPluralInfo() {
122 deleteHash(fPluralCountToCurrencyUnitPattern
);
123 fPluralCountToCurrencyUnitPattern
= NULL
;
131 CurrencyPluralInfo::operator==(const CurrencyPluralInfo
& info
) const {
132 #ifdef CURRENCY_PLURAL_INFO_DEBUG
133 if (*fPluralRules
== *info
.fPluralRules
) {
134 std::cout
<< "same plural rules\n";
136 if (*fLocale
== *info
.fLocale
) {
137 std::cout
<< "same locale\n";
139 if (fPluralCountToCurrencyUnitPattern
->equals(*info
.fPluralCountToCurrencyUnitPattern
)) {
140 std::cout
<< "same pattern\n";
143 return *fPluralRules
== *info
.fPluralRules
&&
144 *fLocale
== *info
.fLocale
&&
145 fPluralCountToCurrencyUnitPattern
->equals(*info
.fPluralCountToCurrencyUnitPattern
);
150 CurrencyPluralInfo::clone() const {
151 return new CurrencyPluralInfo(*this);
155 CurrencyPluralInfo::getPluralRules() const {
160 CurrencyPluralInfo::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));
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
176 result
= UnicodeString(gDefaultCurrencyPluralPattern
);
180 result
= *currencyPluralPattern
;
185 CurrencyPluralInfo::getLocale() const {
190 CurrencyPluralInfo::setPluralRules(const UnicodeString
& ruleDescription
,
191 UErrorCode
& status
) {
192 if (U_SUCCESS(status
)) {
196 fPluralRules
= PluralRules::createRules(ruleDescription
, status
);
202 CurrencyPluralInfo::setCurrencyPluralPattern(const UnicodeString
& pluralCount
,
203 const UnicodeString
& pattern
,
204 UErrorCode
& status
) {
205 if (U_SUCCESS(status
)) {
206 UnicodeString
* oldValue
= static_cast<UnicodeString
*>(
207 fPluralCountToCurrencyUnitPattern
->get(pluralCount
));
209 fPluralCountToCurrencyUnitPattern
->put(pluralCount
, new UnicodeString(pattern
), status
);
215 CurrencyPluralInfo::setLocale(const Locale
& loc
, UErrorCode
& status
) {
216 initialize(loc
, status
);
221 CurrencyPluralInfo::initialize(const Locale
& loc
, UErrorCode
& status
) {
222 if (U_FAILURE(status
)) {
226 fLocale
= loc
.clone();
230 fPluralRules
= PluralRules::forLocale(loc
, status
);
231 setupCurrencyPluralPattern(loc
, status
);
236 CurrencyPluralInfo::setupCurrencyPluralPattern(const Locale
& loc
, UErrorCode
& status
) {
237 if (U_FAILURE(status
)) {
241 if (fPluralCountToCurrencyUnitPattern
) {
242 deleteHash(fPluralCountToCurrencyUnitPattern
);
244 fPluralCountToCurrencyUnitPattern
= initHash(status
);
245 if (U_FAILURE(status
)) {
249 NumberingSystem
*ns
= NumberingSystem::createInstance(loc
,status
);
250 UErrorCode ec
= U_ZERO_ERROR
;
251 UResourceBundle
*rb
= ures_open(NULL
, loc
.getName(), &ec
);
252 UResourceBundle
*numElements
= ures_getByKeyWithFallback(rb
, gNumberElementsTag
, NULL
, &ec
);
253 rb
= ures_getByKeyWithFallback(numElements
, ns
->getName(), rb
, &ec
);
254 rb
= ures_getByKeyWithFallback(rb
, gPatternsTag
, rb
, &ec
);
256 const UChar
* numberStylePattern
= ures_getStringByKeyWithFallback(rb
, gDecimalFormatTag
, &ptnLen
, &ec
);
257 // Fall back to "latn" if num sys specific pattern isn't there.
258 if ( ec
== U_MISSING_RESOURCE_ERROR
&& uprv_strcmp(ns
->getName(),gLatnTag
)) {
260 rb
= ures_getByKeyWithFallback(numElements
, gLatnTag
, rb
, &ec
);
261 rb
= ures_getByKeyWithFallback(rb
, gPatternsTag
, rb
, &ec
);
262 numberStylePattern
= ures_getStringByKeyWithFallback(rb
, gDecimalFormatTag
, &ptnLen
, &ec
);
264 int32_t numberStylePatternLen
= ptnLen
;
265 const UChar
* negNumberStylePattern
= NULL
;
266 int32_t negNumberStylePatternLen
= 0;
268 // parse to check whether there is ";" separator in the numberStylePattern
269 UBool hasSeparator
= false;
271 for (int32_t styleCharIndex
= 0; styleCharIndex
< ptnLen
; ++styleCharIndex
) {
272 if (numberStylePattern
[styleCharIndex
] == gNumberPatternSeparator
) {
274 // split the number style pattern into positive and negative
275 negNumberStylePattern
= numberStylePattern
+ styleCharIndex
+ 1;
276 negNumberStylePatternLen
= ptnLen
- styleCharIndex
- 1;
277 numberStylePatternLen
= styleCharIndex
;
282 ures_close(numElements
);
290 UResourceBundle
*currRb
= ures_open(U_ICUDATA_CURR
, loc
.getName(), &ec
);
291 UResourceBundle
*currencyRes
= ures_getByKeyWithFallback(currRb
, gCurrUnitPtnTag
, NULL
, &ec
);
293 #ifdef CURRENCY_PLURAL_INFO_DEBUG
294 std::cout
<< "in set up\n";
296 StringEnumeration
* keywords
= fPluralRules
->getKeywords(ec
);
298 const char* pluralCount
;
299 while ((pluralCount
= keywords
->next(NULL
, ec
)) != NULL
) {
300 if ( U_SUCCESS(ec
) ) {
302 UErrorCode err
= U_ZERO_ERROR
;
303 const UChar
* patternChars
= ures_getStringByKeyWithFallback(
304 currencyRes
, pluralCount
, &ptnLen
, &err
);
305 if (U_SUCCESS(err
) && ptnLen
> 0) {
306 UnicodeString
* pattern
= new UnicodeString(patternChars
, ptnLen
);
307 #ifdef CURRENCY_PLURAL_INFO_DEBUG
309 pattern
->extract(0, pattern
->length(), result_1
, "UTF-8");
310 std::cout
<< "pluralCount: " << pluralCount
<< "; pattern: " << result_1
<< "\n";
312 pattern
->findAndReplace(UnicodeString(TRUE
, gPart0
, 3),
313 UnicodeString(numberStylePattern
, numberStylePatternLen
));
314 pattern
->findAndReplace(UnicodeString(TRUE
, gPart1
, 3), UnicodeString(TRUE
, gTripleCurrencySign
, 3));
317 UnicodeString
negPattern(patternChars
, ptnLen
);
318 negPattern
.findAndReplace(UnicodeString(TRUE
, gPart0
, 3),
319 UnicodeString(negNumberStylePattern
, negNumberStylePatternLen
));
320 negPattern
.findAndReplace(UnicodeString(TRUE
, gPart1
, 3), UnicodeString(TRUE
, gTripleCurrencySign
, 3));
321 pattern
->append(gNumberPatternSeparator
);
322 pattern
->append(negPattern
);
324 #ifdef CURRENCY_PLURAL_INFO_DEBUG
325 pattern
->extract(0, pattern
->length(), result_1
, "UTF-8");
326 std::cout
<< "pluralCount: " << pluralCount
<< "; pattern: " << result_1
<< "\n";
329 fPluralCountToCurrencyUnitPattern
->put(UnicodeString(pluralCount
, -1, US_INV
), pattern
, status
);
335 ures_close(currencyRes
);
342 CurrencyPluralInfo::deleteHash(Hashtable
* hTable
)
344 if ( hTable
== NULL
) {
347 int32_t pos
= UHASH_FIRST
;
348 const UHashElement
* element
= NULL
;
349 while ( (element
= hTable
->nextElement(pos
)) != NULL
) {
350 const UHashTok valueTok
= element
->value
;
351 const UnicodeString
* value
= (UnicodeString
*)valueTok
.pointer
;
360 CurrencyPluralInfo::initHash(UErrorCode
& status
) {
361 if ( U_FAILURE(status
) ) {
365 if ( (hTable
= new Hashtable(TRUE
, status
)) == NULL
) {
366 status
= U_MEMORY_ALLOCATION_ERROR
;
369 if ( U_FAILURE(status
) ) {
373 hTable
->setValueComparator(ValueComparator
);
379 CurrencyPluralInfo::copyHash(const Hashtable
* source
,
381 UErrorCode
& status
) {
382 if ( U_FAILURE(status
) ) {
385 int32_t pos
= UHASH_FIRST
;
386 const UHashElement
* element
= NULL
;
388 while ( (element
= source
->nextElement(pos
)) != NULL
) {
389 const UHashTok keyTok
= element
->key
;
390 const UnicodeString
* key
= (UnicodeString
*)keyTok
.pointer
;
391 const UHashTok valueTok
= element
->value
;
392 const UnicodeString
* value
= (UnicodeString
*)valueTok
.pointer
;
393 UnicodeString
* copy
= new UnicodeString(*value
);
394 target
->put(UnicodeString(*key
), copy
, status
);
395 if ( U_FAILURE(status
) ) {