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
31 U_NAMESPACE_BEGIN
32
33
34 static const UChar gNumberPatternSeparator = 0x3B; // ;
35
36 U_CDECL_BEGIN
37
38 /**
39 * @internal ICU 4.2
40 */
41 static UBool U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2);
42
43 UBool
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;
48 }
49
50 U_CDECL_END
51
52
53 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo)
54
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};
60
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";
66
CurrencyPluralInfo(UErrorCode & status)67 CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status)
68 : fPluralCountToCurrencyUnitPattern(NULL),
69 fPluralRules(NULL),
70 fLocale(NULL) {
71 initialize(Locale::getDefault(), status);
72 }
73
CurrencyPluralInfo(const Locale & locale,UErrorCode & status)74 CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status)
75 : fPluralCountToCurrencyUnitPattern(NULL),
76 fPluralRules(NULL),
77 fLocale(NULL) {
78 initialize(locale, status);
79 }
80
CurrencyPluralInfo(const CurrencyPluralInfo & info)81 CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info)
82 : UObject(info),
83 fPluralCountToCurrencyUnitPattern(NULL),
84 fPluralRules(NULL),
85 fLocale(NULL) {
86 *this = info;
87 }
88
89
90 CurrencyPluralInfo&
operator =(const CurrencyPluralInfo & info)91 CurrencyPluralInfo::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
~CurrencyPluralInfo()121 CurrencyPluralInfo::~CurrencyPluralInfo() {
122 deleteHash(fPluralCountToCurrencyUnitPattern);
123 fPluralCountToCurrencyUnitPattern = NULL;
124 delete fPluralRules;
125 delete fLocale;
126 fPluralRules = NULL;
127 fLocale = NULL;
128 }
129
130 UBool
operator ==(const CurrencyPluralInfo & info) const131 CurrencyPluralInfo::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
149 CurrencyPluralInfo*
clone() const150 CurrencyPluralInfo::clone() const {
151 return new CurrencyPluralInfo(*this);
152 }
153
154 const PluralRules*
getPluralRules() const155 CurrencyPluralInfo::getPluralRules() const {
156 return fPluralRules;
157 }
158
159 UnicodeString&
getCurrencyPluralPattern(const UnicodeString & pluralCount,UnicodeString & result) const160 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));
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
184 const Locale&
getLocale() const185 CurrencyPluralInfo::getLocale() const {
186 return *fLocale;
187 }
188
189 void
setPluralRules(const UnicodeString & ruleDescription,UErrorCode & status)190 CurrencyPluralInfo::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
201 void
setCurrencyPluralPattern(const UnicodeString & pluralCount,const UnicodeString & pattern,UErrorCode & status)202 CurrencyPluralInfo::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
211 void
setLocale(const Locale & loc,UErrorCode & status)212 CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) {
213 initialize(loc, status);
214 }
215
216
217 void
initialize(const Locale & loc,UErrorCode & status)218 CurrencyPluralInfo::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
232 void
setupCurrencyPluralPattern(const Locale & loc,UErrorCode & status)233 CurrencyPluralInfo::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
338 void
deleteHash(Hashtable * hTable)339 CurrencyPluralInfo::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
356 Hashtable*
initHash(UErrorCode & status)357 CurrencyPluralInfo::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
375 void
copyHash(const Hashtable * source,Hashtable * target,UErrorCode & status)376 CurrencyPluralInfo::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
400 U_NAMESPACE_END
401
402 #endif
403