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
20 #include "unicode/locid.h"
21 #include "unicode/plurrule.h"
22 #include "unicode/strenum.h"
23 #include "unicode/ures.h"
24 #include "unicode/numsys.h"
32 static const UChar gNumberPatternSeparator
= 0x3B; // ;
39 static UBool U_CALLCONV
ValueComparator(UHashTok val1
, UHashTok val2
);
42 U_CALLCONV
ValueComparator(UHashTok val1
, UHashTok val2
) {
43 const UnicodeString
* affix_1
= (UnicodeString
*)val1
.pointer
;
44 const UnicodeString
* affix_2
= (UnicodeString
*)val2
.pointer
;
45 return *affix_1
== *affix_2
;
51 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo
)
53 static const UChar gDefaultCurrencyPluralPattern
[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0};
54 static const UChar gTripleCurrencySign
[] = {0xA4, 0xA4, 0xA4, 0};
55 static const UChar gPluralCountOther
[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0};
56 static const UChar gPart0
[] = {0x7B, 0x30, 0x7D, 0};
57 static const UChar gPart1
[] = {0x7B, 0x31, 0x7D, 0};
59 static const char gNumberElementsTag
[]="NumberElements";
60 static const char gLatnTag
[]="latn";
61 static const char gPatternsTag
[]="patterns";
62 static const char gDecimalFormatTag
[]="decimalFormat";
63 static const char gCurrUnitPtnTag
[]="CurrencyUnitPatterns";
65 CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode
& status
)
66 : fPluralCountToCurrencyUnitPattern(nullptr),
67 fPluralRules(nullptr),
69 fInternalStatus(U_ZERO_ERROR
) {
70 initialize(Locale::getDefault(), status
);
73 CurrencyPluralInfo::CurrencyPluralInfo(const Locale
& locale
, UErrorCode
& status
)
74 : fPluralCountToCurrencyUnitPattern(nullptr),
75 fPluralRules(nullptr),
77 fInternalStatus(U_ZERO_ERROR
) {
78 initialize(locale
, status
);
81 CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo
& info
)
83 fPluralCountToCurrencyUnitPattern(nullptr),
84 fPluralRules(nullptr),
86 fInternalStatus(U_ZERO_ERROR
) {
91 CurrencyPluralInfo::operator=(const CurrencyPluralInfo
& info
) {
96 fInternalStatus
= info
.fInternalStatus
;
97 if (U_FAILURE(fInternalStatus
)) {
98 // bail out early if the object we were copying from was already 'invalid'.
102 deleteHash(fPluralCountToCurrencyUnitPattern
);
103 fPluralCountToCurrencyUnitPattern
= initHash(fInternalStatus
);
104 copyHash(info
.fPluralCountToCurrencyUnitPattern
,
105 fPluralCountToCurrencyUnitPattern
, fInternalStatus
);
106 if ( U_FAILURE(fInternalStatus
) ) {
111 fPluralRules
= nullptr;
115 if (info
.fPluralRules
!= nullptr) {
116 fPluralRules
= info
.fPluralRules
->clone();
117 if (fPluralRules
== nullptr) {
118 fInternalStatus
= U_MEMORY_ALLOCATION_ERROR
;
122 if (info
.fLocale
!= nullptr) {
123 fLocale
= info
.fLocale
->clone();
124 if (fLocale
== nullptr) {
125 // Note: If clone had an error parameter, then we could check/set that instead.
126 fInternalStatus
= U_MEMORY_ALLOCATION_ERROR
;
129 // If the other locale wasn't bogus, but our clone'd locale is bogus, then OOM happened
130 // during the call to clone().
131 if (!info
.fLocale
->isBogus() && fLocale
->isBogus()) {
132 fInternalStatus
= U_MEMORY_ALLOCATION_ERROR
;
139 CurrencyPluralInfo::~CurrencyPluralInfo() {
140 deleteHash(fPluralCountToCurrencyUnitPattern
);
141 fPluralCountToCurrencyUnitPattern
= nullptr;
144 fPluralRules
= nullptr;
149 CurrencyPluralInfo::operator==(const CurrencyPluralInfo
& info
) const {
150 #ifdef CURRENCY_PLURAL_INFO_DEBUG
151 if (*fPluralRules
== *info
.fPluralRules
) {
152 std::cout
<< "same plural rules\n";
154 if (*fLocale
== *info
.fLocale
) {
155 std::cout
<< "same locale\n";
157 if (fPluralCountToCurrencyUnitPattern
->equals(*info
.fPluralCountToCurrencyUnitPattern
)) {
158 std::cout
<< "same pattern\n";
161 return *fPluralRules
== *info
.fPluralRules
&&
162 *fLocale
== *info
.fLocale
&&
163 fPluralCountToCurrencyUnitPattern
->equals(*info
.fPluralCountToCurrencyUnitPattern
);
168 CurrencyPluralInfo::clone() const {
169 CurrencyPluralInfo
* newObj
= new CurrencyPluralInfo(*this);
170 // Since clone doesn't have a 'status' parameter, the best we can do is return nullptr
171 // if the new object was not full constructed properly (an error occurred).
172 if (newObj
!= nullptr && U_FAILURE(newObj
->fInternalStatus
)) {
180 CurrencyPluralInfo::getPluralRules() const {
185 CurrencyPluralInfo::getCurrencyPluralPattern(const UnicodeString
& pluralCount
,
186 UnicodeString
& result
) const {
187 const UnicodeString
* currencyPluralPattern
=
188 (UnicodeString
*)fPluralCountToCurrencyUnitPattern
->get(pluralCount
);
189 if (currencyPluralPattern
== nullptr) {
190 // fall back to "other"
191 if (pluralCount
.compare(gPluralCountOther
, 5)) {
192 currencyPluralPattern
=
193 (UnicodeString
*)fPluralCountToCurrencyUnitPattern
->get(UnicodeString(TRUE
, gPluralCountOther
, 5));
195 if (currencyPluralPattern
== nullptr) {
196 // no currencyUnitPatterns defined,
197 // fallback to predefined default.
198 // This should never happen when ICU resource files are
199 // available, since currencyUnitPattern of "other" is always
201 result
= UnicodeString(gDefaultCurrencyPluralPattern
);
205 result
= *currencyPluralPattern
;
210 CurrencyPluralInfo::getLocale() const {
215 CurrencyPluralInfo::setPluralRules(const UnicodeString
& ruleDescription
,
216 UErrorCode
& status
) {
217 if (U_SUCCESS(status
)) {
219 fPluralRules
= PluralRules::createRules(ruleDescription
, status
);
224 CurrencyPluralInfo::setCurrencyPluralPattern(const UnicodeString
& pluralCount
,
225 const UnicodeString
& pattern
,
226 UErrorCode
& status
) {
227 if (U_SUCCESS(status
)) {
228 UnicodeString
* oldValue
= static_cast<UnicodeString
*>(
229 fPluralCountToCurrencyUnitPattern
->get(pluralCount
));
231 LocalPointer
<UnicodeString
> p(new UnicodeString(pattern
), status
);
232 if (U_SUCCESS(status
)) {
233 // the p object allocated above will be owned by fPluralCountToCurrencyUnitPattern
234 // after the call to put(), even if the method returns failure.
235 fPluralCountToCurrencyUnitPattern
->put(pluralCount
, p
.orphan(), status
);
241 CurrencyPluralInfo::setLocale(const Locale
& loc
, UErrorCode
& status
) {
242 initialize(loc
, status
);
246 CurrencyPluralInfo::initialize(const Locale
& loc
, UErrorCode
& status
) {
247 if (U_FAILURE(status
)) {
253 fPluralRules
= nullptr;
255 fLocale
= loc
.clone();
256 if (fLocale
== nullptr) {
257 status
= U_MEMORY_ALLOCATION_ERROR
;
260 // If the locale passed in wasn't bogus, but our clone'd locale is bogus, then OOM happened
261 // during the call to loc.clone().
262 if (!loc
.isBogus() && fLocale
->isBogus()) {
263 status
= U_MEMORY_ALLOCATION_ERROR
;
266 fPluralRules
= PluralRules::forLocale(loc
, status
);
267 setupCurrencyPluralPattern(loc
, status
);
271 CurrencyPluralInfo::setupCurrencyPluralPattern(const Locale
& loc
, UErrorCode
& status
) {
272 if (U_FAILURE(status
)) {
276 deleteHash(fPluralCountToCurrencyUnitPattern
);
277 fPluralCountToCurrencyUnitPattern
= initHash(status
);
278 if (U_FAILURE(status
)) {
282 LocalPointer
<NumberingSystem
> ns(NumberingSystem::createInstance(loc
, status
), status
);
283 if (U_FAILURE(status
)) {
286 UErrorCode ec
= U_ZERO_ERROR
;
287 LocalUResourceBundlePointer
rb(ures_open(nullptr, loc
.getName(), &ec
));
288 LocalUResourceBundlePointer
numElements(ures_getByKeyWithFallback(rb
.getAlias(), gNumberElementsTag
, nullptr, &ec
));
289 ures_getByKeyWithFallback(numElements
.getAlias(), ns
->getName(), rb
.getAlias(), &ec
);
290 ures_getByKeyWithFallback(rb
.getAlias(), gPatternsTag
, rb
.getAlias(), &ec
);
292 const UChar
* numberStylePattern
= ures_getStringByKeyWithFallback(rb
.getAlias(), gDecimalFormatTag
, &ptnLen
, &ec
);
293 // Fall back to "latn" if num sys specific pattern isn't there.
294 if ( ec
== U_MISSING_RESOURCE_ERROR
&& (uprv_strcmp(ns
->getName(), gLatnTag
) != 0)) {
296 ures_getByKeyWithFallback(numElements
.getAlias(), gLatnTag
, rb
.getAlias(), &ec
);
297 ures_getByKeyWithFallback(rb
.getAlias(), gPatternsTag
, rb
.getAlias(), &ec
);
298 numberStylePattern
= ures_getStringByKeyWithFallback(rb
.getAlias(), gDecimalFormatTag
, &ptnLen
, &ec
);
300 int32_t numberStylePatternLen
= ptnLen
;
301 const UChar
* negNumberStylePattern
= nullptr;
302 int32_t negNumberStylePatternLen
= 0;
304 // parse to check whether there is ";" separator in the numberStylePattern
305 UBool hasSeparator
= false;
307 for (int32_t styleCharIndex
= 0; styleCharIndex
< ptnLen
; ++styleCharIndex
) {
308 if (numberStylePattern
[styleCharIndex
] == gNumberPatternSeparator
) {
310 // split the number style pattern into positive and negative
311 negNumberStylePattern
= numberStylePattern
+ styleCharIndex
+ 1;
312 negNumberStylePatternLen
= ptnLen
- styleCharIndex
- 1;
313 numberStylePatternLen
= styleCharIndex
;
319 // If OOM occurred during the above code, then we want to report that back to the caller.
320 if (ec
== U_MEMORY_ALLOCATION_ERROR
) {
326 LocalUResourceBundlePointer
currRb(ures_open(U_ICUDATA_CURR
, loc
.getName(), &ec
));
327 LocalUResourceBundlePointer
currencyRes(ures_getByKeyWithFallback(currRb
.getAlias(), gCurrUnitPtnTag
, nullptr, &ec
));
329 #ifdef CURRENCY_PLURAL_INFO_DEBUG
330 std::cout
<< "in set up\n";
332 LocalPointer
<StringEnumeration
> keywords(fPluralRules
->getKeywords(ec
), ec
);
334 const char* pluralCount
;
335 while (((pluralCount
= keywords
->next(nullptr, ec
)) != nullptr) && U_SUCCESS(ec
)) {
337 UErrorCode err
= U_ZERO_ERROR
;
338 const UChar
* patternChars
= ures_getStringByKeyWithFallback(currencyRes
.getAlias(), pluralCount
, &ptnLength
, &err
);
339 if (err
== U_MEMORY_ALLOCATION_ERROR
|| patternChars
== nullptr) {
343 if (U_SUCCESS(err
) && ptnLength
> 0) {
344 UnicodeString
* pattern
= new UnicodeString(patternChars
, ptnLength
);
345 if (pattern
== nullptr) {
346 ec
= U_MEMORY_ALLOCATION_ERROR
;
349 #ifdef CURRENCY_PLURAL_INFO_DEBUG
351 pattern
->extract(0, pattern
->length(), result_1
, "UTF-8");
352 std::cout
<< "pluralCount: " << pluralCount
<< "; pattern: " << result_1
<< "\n";
354 pattern
->findAndReplace(UnicodeString(TRUE
, gPart0
, 3),
355 UnicodeString(numberStylePattern
, numberStylePatternLen
));
356 pattern
->findAndReplace(UnicodeString(TRUE
, gPart1
, 3), UnicodeString(TRUE
, gTripleCurrencySign
, 3));
359 UnicodeString
negPattern(patternChars
, ptnLength
);
360 negPattern
.findAndReplace(UnicodeString(TRUE
, gPart0
, 3),
361 UnicodeString(negNumberStylePattern
, negNumberStylePatternLen
));
362 negPattern
.findAndReplace(UnicodeString(TRUE
, gPart1
, 3), UnicodeString(TRUE
, gTripleCurrencySign
, 3));
363 pattern
->append(gNumberPatternSeparator
);
364 pattern
->append(negPattern
);
366 #ifdef CURRENCY_PLURAL_INFO_DEBUG
367 pattern
->extract(0, pattern
->length(), result_1
, "UTF-8");
368 std::cout
<< "pluralCount: " << pluralCount
<< "; pattern: " << result_1
<< "\n";
370 // the 'pattern' object allocated above will be owned by the fPluralCountToCurrencyUnitPattern after the call to
371 // put(), even if the method returns failure.
372 fPluralCountToCurrencyUnitPattern
->put(UnicodeString(pluralCount
, -1, US_INV
), pattern
, status
);
376 // If OOM occurred during the above code, then we want to report that back to the caller.
377 if (ec
== U_MEMORY_ALLOCATION_ERROR
) {
383 CurrencyPluralInfo::deleteHash(Hashtable
* hTable
) {
384 if ( hTable
== nullptr ) {
387 int32_t pos
= UHASH_FIRST
;
388 const UHashElement
* element
= nullptr;
389 while ( (element
= hTable
->nextElement(pos
)) != nullptr ) {
390 const UHashTok valueTok
= element
->value
;
391 const UnicodeString
* value
= (UnicodeString
*)valueTok
.pointer
;
399 CurrencyPluralInfo::initHash(UErrorCode
& status
) {
400 if (U_FAILURE(status
)) {
403 LocalPointer
<Hashtable
> hTable(new Hashtable(TRUE
, status
), status
);
404 if (U_FAILURE(status
)) {
407 hTable
->setValueComparator(ValueComparator
);
408 return hTable
.orphan();
412 CurrencyPluralInfo::copyHash(const Hashtable
* source
,
414 UErrorCode
& status
) {
415 if (U_FAILURE(status
)) {
418 int32_t pos
= UHASH_FIRST
;
419 const UHashElement
* element
= nullptr;
421 while ( (element
= source
->nextElement(pos
)) != nullptr ) {
422 const UHashTok keyTok
= element
->key
;
423 const UnicodeString
* key
= (UnicodeString
*)keyTok
.pointer
;
424 const UHashTok valueTok
= element
->value
;
425 const UnicodeString
* value
= (UnicodeString
*)valueTok
.pointer
;
426 LocalPointer
<UnicodeString
> copy(new UnicodeString(*value
), status
);
427 if (U_FAILURE(status
)) {
430 // The HashTable owns the 'copy' object after the call to put().
431 target
->put(UnicodeString(*key
), copy
.orphan(), status
);
432 if (U_FAILURE(status
)) {