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
30 U_NAMESPACE_BEGIN
31
32
33 static const UChar gNumberPatternSeparator = 0x3B; // ;
34
35 U_CDECL_BEGIN
36
37 /**
38 * @internal ICU 4.2
39 */
40 static UBool U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2);
41
42 UBool
ValueComparator(UHashTok val1,UHashTok val2)43 U_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
49 U_CDECL_END
50
51
52 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo)
53
54 static const UChar gDefaultCurrencyPluralPattern[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0};
55 static const UChar gTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0};
56 static const UChar gPluralCountOther[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0};
57 static const UChar gPart0[] = {0x7B, 0x30, 0x7D, 0};
58 static const UChar gPart1[] = {0x7B, 0x31, 0x7D, 0};
59
60 static const char gNumberElementsTag[]="NumberElements";
61 static const char gLatnTag[]="latn";
62 static const char gPatternsTag[]="patterns";
63 static const char gDecimalFormatTag[]="decimalFormat";
64 static const char gCurrUnitPtnTag[]="CurrencyUnitPatterns";
65
CurrencyPluralInfo(UErrorCode & status)66 CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status)
67 : fPluralCountToCurrencyUnitPattern(NULL),
68 fPluralRules(NULL),
69 fLocale(NULL) {
70 initialize(Locale::getDefault(), status);
71 }
72
CurrencyPluralInfo(const Locale & locale,UErrorCode & status)73 CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status)
74 : fPluralCountToCurrencyUnitPattern(NULL),
75 fPluralRules(NULL),
76 fLocale(NULL) {
77 initialize(locale, status);
78 }
79
CurrencyPluralInfo(const CurrencyPluralInfo & info)80 CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info)
81 : UObject(info),
82 fPluralCountToCurrencyUnitPattern(NULL),
83 fPluralRules(NULL),
84 fLocale(NULL) {
85 *this = info;
86 }
87
88
89 CurrencyPluralInfo&
operator =(const CurrencyPluralInfo & info)90 CurrencyPluralInfo::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
~CurrencyPluralInfo()120 CurrencyPluralInfo::~CurrencyPluralInfo() {
121 deleteHash(fPluralCountToCurrencyUnitPattern);
122 fPluralCountToCurrencyUnitPattern = NULL;
123 delete fPluralRules;
124 delete fLocale;
125 fPluralRules = NULL;
126 fLocale = NULL;
127 }
128
129 UBool
operator ==(const CurrencyPluralInfo & info) const130 CurrencyPluralInfo::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
148 CurrencyPluralInfo*
clone() const149 CurrencyPluralInfo::clone() const {
150 return new CurrencyPluralInfo(*this);
151 }
152
153 const PluralRules*
getPluralRules() const154 CurrencyPluralInfo::getPluralRules() const {
155 return fPluralRules;
156 }
157
158 UnicodeString&
getCurrencyPluralPattern(const UnicodeString & pluralCount,UnicodeString & result) const159 CurrencyPluralInfo::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
183 const Locale&
getLocale() const184 CurrencyPluralInfo::getLocale() const {
185 return *fLocale;
186 }
187
188 void
setPluralRules(const UnicodeString & ruleDescription,UErrorCode & status)189 CurrencyPluralInfo::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
200 void
setCurrencyPluralPattern(const UnicodeString & pluralCount,const UnicodeString & pattern,UErrorCode & status)201 CurrencyPluralInfo::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
210 void
setLocale(const Locale & loc,UErrorCode & status)211 CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) {
212 initialize(loc, status);
213 }
214
215
216 void
initialize(const Locale & loc,UErrorCode & status)217 CurrencyPluralInfo::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
231 void
setupCurrencyPluralPattern(const Locale & loc,UErrorCode & status)232 CurrencyPluralInfo::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
337 void
deleteHash(Hashtable * hTable)338 CurrencyPluralInfo::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
355 Hashtable*
initHash(UErrorCode & status)356 CurrencyPluralInfo::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
374 void
copyHash(const Hashtable * source,Hashtable * target,UErrorCode & status)375 CurrencyPluralInfo::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
399 U_NAMESPACE_END
400
401 #endif
402