Line data Source code
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
44 0 : U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2) {
45 0 : const UnicodeString* affix_1 = (UnicodeString*)val1.pointer;
46 0 : const UnicodeString* affix_2 = (UnicodeString*)val2.pointer;
47 0 : return *affix_1 == *affix_2;
48 : }
49 :
50 : U_CDECL_END
51 :
52 :
53 0 : 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 :
67 0 : CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status)
68 : : fPluralCountToCurrencyUnitPattern(NULL),
69 : fPluralRules(NULL),
70 0 : fLocale(NULL) {
71 0 : initialize(Locale::getDefault(), status);
72 0 : }
73 :
74 0 : CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status)
75 : : fPluralCountToCurrencyUnitPattern(NULL),
76 : fPluralRules(NULL),
77 0 : fLocale(NULL) {
78 0 : initialize(locale, status);
79 0 : }
80 :
81 0 : CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info)
82 : : UObject(info),
83 : fPluralCountToCurrencyUnitPattern(NULL),
84 : fPluralRules(NULL),
85 0 : fLocale(NULL) {
86 0 : *this = info;
87 0 : }
88 :
89 :
90 : CurrencyPluralInfo&
91 0 : CurrencyPluralInfo::operator=(const CurrencyPluralInfo& info) {
92 0 : if (this == &info) {
93 0 : return *this;
94 : }
95 :
96 0 : deleteHash(fPluralCountToCurrencyUnitPattern);
97 0 : UErrorCode status = U_ZERO_ERROR;
98 0 : fPluralCountToCurrencyUnitPattern = initHash(status);
99 0 : copyHash(info.fPluralCountToCurrencyUnitPattern,
100 0 : fPluralCountToCurrencyUnitPattern, status);
101 0 : if ( U_FAILURE(status) ) {
102 0 : return *this;
103 : }
104 :
105 0 : delete fPluralRules;
106 0 : delete fLocale;
107 0 : if (info.fPluralRules) {
108 0 : fPluralRules = info.fPluralRules->clone();
109 : } else {
110 0 : fPluralRules = NULL;
111 : }
112 0 : if (info.fLocale) {
113 0 : fLocale = info.fLocale->clone();
114 : } else {
115 0 : fLocale = NULL;
116 : }
117 0 : return *this;
118 : }
119 :
120 :
121 0 : CurrencyPluralInfo::~CurrencyPluralInfo() {
122 0 : deleteHash(fPluralCountToCurrencyUnitPattern);
123 0 : fPluralCountToCurrencyUnitPattern = NULL;
124 0 : delete fPluralRules;
125 0 : delete fLocale;
126 0 : fPluralRules = NULL;
127 0 : fLocale = NULL;
128 0 : }
129 :
130 : UBool
131 0 : 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 0 : return *fPluralRules == *info.fPluralRules &&
144 0 : *fLocale == *info.fLocale &&
145 0 : fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern);
146 : }
147 :
148 :
149 : CurrencyPluralInfo*
150 0 : CurrencyPluralInfo::clone() const {
151 0 : return new CurrencyPluralInfo(*this);
152 : }
153 :
154 : const PluralRules*
155 0 : CurrencyPluralInfo::getPluralRules() const {
156 0 : return fPluralRules;
157 : }
158 :
159 : UnicodeString&
160 0 : CurrencyPluralInfo::getCurrencyPluralPattern(const UnicodeString& pluralCount,
161 : UnicodeString& result) const {
162 : const UnicodeString* currencyPluralPattern =
163 0 : (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(pluralCount);
164 0 : if (currencyPluralPattern == NULL) {
165 : // fall back to "other"
166 0 : if (pluralCount.compare(gPluralCountOther, 5)) {
167 : currencyPluralPattern =
168 0 : (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(UnicodeString(TRUE, gPluralCountOther, 5));
169 : }
170 0 : 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 0 : result = UnicodeString(gDefaultCurrencyPluralPattern);
177 0 : return result;
178 : }
179 : }
180 0 : result = *currencyPluralPattern;
181 0 : return result;
182 : }
183 :
184 : const Locale&
185 0 : CurrencyPluralInfo::getLocale() const {
186 0 : return *fLocale;
187 : }
188 :
189 : void
190 0 : CurrencyPluralInfo::setPluralRules(const UnicodeString& ruleDescription,
191 : UErrorCode& status) {
192 0 : if (U_SUCCESS(status)) {
193 0 : if (fPluralRules) {
194 0 : delete fPluralRules;
195 : }
196 0 : fPluralRules = PluralRules::createRules(ruleDescription, status);
197 : }
198 0 : }
199 :
200 :
201 : void
202 0 : CurrencyPluralInfo::setCurrencyPluralPattern(const UnicodeString& pluralCount,
203 : const UnicodeString& pattern,
204 : UErrorCode& status) {
205 0 : if (U_SUCCESS(status)) {
206 0 : fPluralCountToCurrencyUnitPattern->put(pluralCount, new UnicodeString(pattern), status);
207 : }
208 0 : }
209 :
210 :
211 : void
212 0 : CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) {
213 0 : initialize(loc, status);
214 0 : }
215 :
216 :
217 : void
218 0 : CurrencyPluralInfo::initialize(const Locale& loc, UErrorCode& status) {
219 0 : if (U_FAILURE(status)) {
220 0 : return;
221 : }
222 0 : delete fLocale;
223 0 : fLocale = loc.clone();
224 0 : if (fPluralRules) {
225 0 : delete fPluralRules;
226 : }
227 0 : fPluralRules = PluralRules::forLocale(loc, status);
228 0 : setupCurrencyPluralPattern(loc, status);
229 : }
230 :
231 :
232 : void
233 0 : CurrencyPluralInfo::setupCurrencyPluralPattern(const Locale& loc, UErrorCode& status) {
234 0 : if (U_FAILURE(status)) {
235 0 : return;
236 : }
237 :
238 0 : if (fPluralCountToCurrencyUnitPattern) {
239 0 : deleteHash(fPluralCountToCurrencyUnitPattern);
240 : }
241 0 : fPluralCountToCurrencyUnitPattern = initHash(status);
242 0 : if (U_FAILURE(status)) {
243 0 : return;
244 : }
245 :
246 0 : NumberingSystem *ns = NumberingSystem::createInstance(loc,status);
247 0 : UErrorCode ec = U_ZERO_ERROR;
248 0 : UResourceBundle *rb = ures_open(NULL, loc.getName(), &ec);
249 0 : UResourceBundle *numElements = ures_getByKeyWithFallback(rb, gNumberElementsTag, NULL, &ec);
250 0 : rb = ures_getByKeyWithFallback(numElements, ns->getName(), rb, &ec);
251 0 : rb = ures_getByKeyWithFallback(rb, gPatternsTag, rb, &ec);
252 : int32_t ptnLen;
253 0 : const UChar* numberStylePattern = ures_getStringByKeyWithFallback(rb, gDecimalFormatTag, &ptnLen, &ec);
254 : // Fall back to "latn" if num sys specific pattern isn't there.
255 0 : if ( ec == U_MISSING_RESOURCE_ERROR && uprv_strcmp(ns->getName(),gLatnTag)) {
256 0 : ec = U_ZERO_ERROR;
257 0 : rb = ures_getByKeyWithFallback(numElements, gLatnTag, rb, &ec);
258 0 : rb = ures_getByKeyWithFallback(rb, gPatternsTag, rb, &ec);
259 0 : numberStylePattern = ures_getStringByKeyWithFallback(rb, gDecimalFormatTag, &ptnLen, &ec);
260 : }
261 0 : int32_t numberStylePatternLen = ptnLen;
262 0 : const UChar* negNumberStylePattern = NULL;
263 0 : int32_t negNumberStylePatternLen = 0;
264 : // TODO: Java
265 : // parse to check whether there is ";" separator in the numberStylePattern
266 0 : UBool hasSeparator = false;
267 0 : if (U_SUCCESS(ec)) {
268 0 : for (int32_t styleCharIndex = 0; styleCharIndex < ptnLen; ++styleCharIndex) {
269 0 : if (numberStylePattern[styleCharIndex] == gNumberPatternSeparator) {
270 0 : hasSeparator = true;
271 : // split the number style pattern into positive and negative
272 0 : negNumberStylePattern = numberStylePattern + styleCharIndex + 1;
273 0 : negNumberStylePatternLen = ptnLen - styleCharIndex - 1;
274 0 : numberStylePatternLen = styleCharIndex;
275 : }
276 : }
277 : }
278 :
279 0 : ures_close(numElements);
280 0 : ures_close(rb);
281 0 : delete ns;
282 :
283 0 : if (U_FAILURE(ec)) {
284 0 : return;
285 : }
286 :
287 0 : UResourceBundle *currRb = ures_open(U_ICUDATA_CURR, loc.getName(), &ec);
288 0 : 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 0 : StringEnumeration* keywords = fPluralRules->getKeywords(ec);
294 0 : if (U_SUCCESS(ec)) {
295 : const char* pluralCount;
296 0 : while ((pluralCount = keywords->next(NULL, ec)) != NULL) {
297 0 : if ( U_SUCCESS(ec) ) {
298 : int32_t ptnLen;
299 0 : UErrorCode err = U_ZERO_ERROR;
300 : const UChar* patternChars = ures_getStringByKeyWithFallback(
301 0 : currencyRes, pluralCount, &ptnLen, &err);
302 0 : if (U_SUCCESS(err) && ptnLen > 0) {
303 0 : 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 0 : pattern->findAndReplace(UnicodeString(TRUE, gPart0, 3),
310 0 : UnicodeString(numberStylePattern, numberStylePatternLen));
311 0 : pattern->findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
312 :
313 0 : if (hasSeparator) {
314 0 : UnicodeString negPattern(patternChars, ptnLen);
315 0 : negPattern.findAndReplace(UnicodeString(TRUE, gPart0, 3),
316 0 : UnicodeString(negNumberStylePattern, negNumberStylePatternLen));
317 0 : negPattern.findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
318 0 : pattern->append(gNumberPatternSeparator);
319 0 : 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 0 : fPluralCountToCurrencyUnitPattern->put(UnicodeString(pluralCount, -1, US_INV), pattern, status);
327 : }
328 : }
329 : }
330 : }
331 0 : delete keywords;
332 0 : ures_close(currencyRes);
333 0 : ures_close(currRb);
334 : }
335 :
336 :
337 :
338 : void
339 0 : CurrencyPluralInfo::deleteHash(Hashtable* hTable)
340 : {
341 0 : if ( hTable == NULL ) {
342 0 : return;
343 : }
344 0 : int32_t pos = UHASH_FIRST;
345 0 : const UHashElement* element = NULL;
346 0 : while ( (element = hTable->nextElement(pos)) != NULL ) {
347 0 : const UHashTok valueTok = element->value;
348 0 : const UnicodeString* value = (UnicodeString*)valueTok.pointer;
349 0 : delete value;
350 : }
351 0 : delete hTable;
352 0 : hTable = NULL;
353 : }
354 :
355 :
356 : Hashtable*
357 0 : CurrencyPluralInfo::initHash(UErrorCode& status) {
358 0 : if ( U_FAILURE(status) ) {
359 0 : return NULL;
360 : }
361 : Hashtable* hTable;
362 0 : if ( (hTable = new Hashtable(TRUE, status)) == NULL ) {
363 0 : status = U_MEMORY_ALLOCATION_ERROR;
364 0 : return NULL;
365 : }
366 0 : if ( U_FAILURE(status) ) {
367 0 : delete hTable;
368 0 : return NULL;
369 : }
370 0 : hTable->setValueComparator(ValueComparator);
371 0 : return hTable;
372 : }
373 :
374 :
375 : void
376 0 : CurrencyPluralInfo::copyHash(const Hashtable* source,
377 : Hashtable* target,
378 : UErrorCode& status) {
379 0 : if ( U_FAILURE(status) ) {
380 0 : return;
381 : }
382 0 : int32_t pos = UHASH_FIRST;
383 0 : const UHashElement* element = NULL;
384 0 : if ( source ) {
385 0 : while ( (element = source->nextElement(pos)) != NULL ) {
386 0 : const UHashTok keyTok = element->key;
387 0 : const UnicodeString* key = (UnicodeString*)keyTok.pointer;
388 0 : const UHashTok valueTok = element->value;
389 0 : const UnicodeString* value = (UnicodeString*)valueTok.pointer;
390 0 : UnicodeString* copy = new UnicodeString(*value);
391 0 : target->put(UnicodeString(*key), copy, status);
392 0 : if ( U_FAILURE(status) ) {
393 0 : return;
394 : }
395 : }
396 : }
397 : }
398 :
399 :
400 : U_NAMESPACE_END
401 :
402 : #endif
|