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