• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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