1 /*
2  *******************************************************************************
3  * Copyright (C) 2009-2014, International Business Machines Corporation and
4  * others. All Rights Reserved.
5  *******************************************************************************
6  */
7 
8 #include "unicode/currpinf.h"
9 
10 #if !UCONFIG_NO_FORMATTING
11 
12 //#define CURRENCY_PLURAL_INFO_DEBUG 1
13 
14 #ifdef CURRENCY_PLURAL_INFO_DEBUG
15 #include <iostream>
16 #endif
17 
18 
19 #include "unicode/locid.h"
20 #include "unicode/plurrule.h"
21 #include "unicode/ures.h"
22 #include "unicode/numsys.h"
23 #include "cstring.h"
24 #include "hash.h"
25 #include "uresimp.h"
26 #include "ureslocs.h"
27 
28 U_NAMESPACE_BEGIN
29 
30 
31 static const UChar gNumberPatternSeparator = 0x3B; // ;
32 
33 U_CDECL_BEGIN
34 
35 /**
36  * @internal ICU 4.2
37  */
38 static UBool U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2);
39 
40 UBool
ValueComparator(UHashTok val1,UHashTok val2)41 U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2) {
42     const UnicodeString* affix_1 = (UnicodeString*)val1.pointer;
43     const UnicodeString* affix_2 = (UnicodeString*)val2.pointer;
44     return  *affix_1 == *affix_2;
45 }
46 
47 U_CDECL_END
48 
49 
50 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo)
51 
52 static const UChar gDefaultCurrencyPluralPattern[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0};
53 static const UChar gTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0};
54 static const UChar gPluralCountOther[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0};
55 static const UChar gPart0[] = {0x7B, 0x30, 0x7D, 0};
56 static const UChar gPart1[] = {0x7B, 0x31, 0x7D, 0};
57 
58 static const char gNumberElementsTag[]="NumberElements";
59 static const char gLatnTag[]="latn";
60 static const char gPatternsTag[]="patterns";
61 static const char gDecimalFormatTag[]="decimalFormat";
62 static const char gCurrUnitPtnTag[]="CurrencyUnitPatterns";
63 
CurrencyPluralInfo(UErrorCode & status)64 CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status)
65 :   fPluralCountToCurrencyUnitPattern(NULL),
66     fPluralRules(NULL),
67     fLocale(NULL) {
68     initialize(Locale::getDefault(), status);
69 }
70 
CurrencyPluralInfo(const Locale & locale,UErrorCode & status)71 CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status)
72 :   fPluralCountToCurrencyUnitPattern(NULL),
73     fPluralRules(NULL),
74     fLocale(NULL) {
75     initialize(locale, status);
76 }
77 
CurrencyPluralInfo(const CurrencyPluralInfo & info)78 CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info)
79 :   UObject(info),
80     fPluralCountToCurrencyUnitPattern(NULL),
81     fPluralRules(NULL),
82     fLocale(NULL) {
83     *this = info;
84 }
85 
86 
87 CurrencyPluralInfo&
operator =(const CurrencyPluralInfo & info)88 CurrencyPluralInfo::operator=(const CurrencyPluralInfo& info) {
89     if (this == &info) {
90         return *this;
91     }
92 
93     deleteHash(fPluralCountToCurrencyUnitPattern);
94     UErrorCode status = U_ZERO_ERROR;
95     fPluralCountToCurrencyUnitPattern = initHash(status);
96     copyHash(info.fPluralCountToCurrencyUnitPattern,
97              fPluralCountToCurrencyUnitPattern, status);
98     if ( U_FAILURE(status) ) {
99         return *this;
100     }
101 
102     delete fPluralRules;
103     delete fLocale;
104     if (info.fPluralRules) {
105         fPluralRules = info.fPluralRules->clone();
106     } else {
107         fPluralRules = NULL;
108     }
109     if (info.fLocale) {
110         fLocale = info.fLocale->clone();
111     } else {
112         fLocale = NULL;
113     }
114     return *this;
115 }
116 
117 
~CurrencyPluralInfo()118 CurrencyPluralInfo::~CurrencyPluralInfo() {
119     deleteHash(fPluralCountToCurrencyUnitPattern);
120     fPluralCountToCurrencyUnitPattern = NULL;
121     delete fPluralRules;
122     delete fLocale;
123     fPluralRules = NULL;
124     fLocale = NULL;
125 }
126 
127 UBool
operator ==(const CurrencyPluralInfo & info) const128 CurrencyPluralInfo::operator==(const CurrencyPluralInfo& info) const {
129 #ifdef CURRENCY_PLURAL_INFO_DEBUG
130     if (*fPluralRules == *info.fPluralRules) {
131         std::cout << "same plural rules\n";
132     }
133     if (*fLocale == *info.fLocale) {
134         std::cout << "same locale\n";
135     }
136     if (fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern)) {
137         std::cout << "same pattern\n";
138     }
139 #endif
140     return *fPluralRules == *info.fPluralRules &&
141            *fLocale == *info.fLocale &&
142            fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern);
143 }
144 
145 
146 CurrencyPluralInfo*
clone() const147 CurrencyPluralInfo::clone() const {
148     return new CurrencyPluralInfo(*this);
149 }
150 
151 const PluralRules*
getPluralRules() const152 CurrencyPluralInfo::getPluralRules() const {
153     return fPluralRules;
154 }
155 
156 UnicodeString&
getCurrencyPluralPattern(const UnicodeString & pluralCount,UnicodeString & result) const157 CurrencyPluralInfo::getCurrencyPluralPattern(const UnicodeString&  pluralCount,
158                                              UnicodeString& result) const {
159     const UnicodeString* currencyPluralPattern =
160         (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(pluralCount);
161     if (currencyPluralPattern == NULL) {
162         // fall back to "other"
163         if (pluralCount.compare(gPluralCountOther, 5)) {
164             currencyPluralPattern =
165                 (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(UnicodeString(TRUE, gPluralCountOther, 5));
166         }
167         if (currencyPluralPattern == NULL) {
168             // no currencyUnitPatterns defined,
169             // fallback to predefined defult.
170             // This should never happen when ICU resource files are
171             // available, since currencyUnitPattern of "other" is always
172             // defined in root.
173             result = UnicodeString(gDefaultCurrencyPluralPattern);
174             return result;
175         }
176     }
177     result = *currencyPluralPattern;
178     return result;
179 }
180 
181 const Locale&
getLocale() const182 CurrencyPluralInfo::getLocale() const {
183     return *fLocale;
184 }
185 
186 void
setPluralRules(const UnicodeString & ruleDescription,UErrorCode & status)187 CurrencyPluralInfo::setPluralRules(const UnicodeString& ruleDescription,
188                                    UErrorCode& status) {
189     if (U_SUCCESS(status)) {
190         if (fPluralRules) {
191             delete fPluralRules;
192         }
193         fPluralRules = PluralRules::createRules(ruleDescription, status);
194     }
195 }
196 
197 
198 void
setCurrencyPluralPattern(const UnicodeString & pluralCount,const UnicodeString & pattern,UErrorCode & status)199 CurrencyPluralInfo::setCurrencyPluralPattern(const UnicodeString& pluralCount,
200                                              const UnicodeString& pattern,
201                                              UErrorCode& status) {
202     if (U_SUCCESS(status)) {
203         fPluralCountToCurrencyUnitPattern->put(pluralCount, new UnicodeString(pattern), status);
204     }
205 }
206 
207 
208 void
setLocale(const Locale & loc,UErrorCode & status)209 CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) {
210     initialize(loc, status);
211 }
212 
213 
214 void
initialize(const Locale & loc,UErrorCode & status)215 CurrencyPluralInfo::initialize(const Locale& loc, UErrorCode& status) {
216     if (U_FAILURE(status)) {
217         return;
218     }
219     delete fLocale;
220     fLocale = loc.clone();
221     if (fPluralRules) {
222         delete fPluralRules;
223     }
224     fPluralRules = PluralRules::forLocale(loc, status);
225     setupCurrencyPluralPattern(loc, status);
226 }
227 
228 
229 void
setupCurrencyPluralPattern(const Locale & loc,UErrorCode & status)230 CurrencyPluralInfo::setupCurrencyPluralPattern(const Locale& loc, UErrorCode& status) {
231     if (U_FAILURE(status)) {
232         return;
233     }
234 
235     if (fPluralCountToCurrencyUnitPattern) {
236         deleteHash(fPluralCountToCurrencyUnitPattern);
237     }
238     fPluralCountToCurrencyUnitPattern = initHash(status);
239     if (U_FAILURE(status)) {
240         return;
241     }
242 
243     NumberingSystem *ns = NumberingSystem::createInstance(loc,status);
244     UErrorCode ec = U_ZERO_ERROR;
245     UResourceBundle *rb = ures_open(NULL, loc.getName(), &ec);
246     UResourceBundle *numElements = ures_getByKeyWithFallback(rb, gNumberElementsTag, NULL, &ec);
247     rb = ures_getByKeyWithFallback(numElements, ns->getName(), rb, &ec);
248     rb = ures_getByKeyWithFallback(rb, gPatternsTag, rb, &ec);
249     int32_t ptnLen;
250     const UChar* numberStylePattern = ures_getStringByKeyWithFallback(rb, gDecimalFormatTag, &ptnLen, &ec);
251     // Fall back to "latn" if num sys specific pattern isn't there.
252     if ( ec == U_MISSING_RESOURCE_ERROR && uprv_strcmp(ns->getName(),gLatnTag)) {
253         ec = U_ZERO_ERROR;
254         rb = ures_getByKeyWithFallback(numElements, gLatnTag, rb, &ec);
255         rb = ures_getByKeyWithFallback(rb, gPatternsTag, rb, &ec);
256         numberStylePattern = ures_getStringByKeyWithFallback(rb, gDecimalFormatTag, &ptnLen, &ec);
257     }
258     int32_t numberStylePatternLen = ptnLen;
259     const UChar* negNumberStylePattern = NULL;
260     int32_t negNumberStylePatternLen = 0;
261     // TODO: Java
262     // parse to check whether there is ";" separator in the numberStylePattern
263     UBool hasSeparator = false;
264     if (U_SUCCESS(ec)) {
265         for (int32_t styleCharIndex = 0; styleCharIndex < ptnLen; ++styleCharIndex) {
266             if (numberStylePattern[styleCharIndex] == gNumberPatternSeparator) {
267                 hasSeparator = true;
268                 // split the number style pattern into positive and negative
269                 negNumberStylePattern = numberStylePattern + styleCharIndex + 1;
270                 negNumberStylePatternLen = ptnLen - styleCharIndex - 1;
271                 numberStylePatternLen = styleCharIndex;
272             }
273         }
274     }
275 
276     ures_close(numElements);
277     ures_close(rb);
278     delete ns;
279 
280     if (U_FAILURE(ec)) {
281         return;
282     }
283 
284     UResourceBundle *currRb = ures_open(U_ICUDATA_CURR, loc.getName(), &ec);
285     UResourceBundle *currencyRes = ures_getByKeyWithFallback(currRb, gCurrUnitPtnTag, NULL, &ec);
286 
287 #ifdef CURRENCY_PLURAL_INFO_DEBUG
288     std::cout << "in set up\n";
289 #endif
290     StringEnumeration* keywords = fPluralRules->getKeywords(ec);
291     if (U_SUCCESS(ec)) {
292         const char* pluralCount;
293         while ((pluralCount = keywords->next(NULL, ec)) != NULL) {
294             if ( U_SUCCESS(ec) ) {
295                 int32_t ptnLen;
296                 UErrorCode err = U_ZERO_ERROR;
297                 const UChar* patternChars = ures_getStringByKeyWithFallback(
298                     currencyRes, pluralCount, &ptnLen, &err);
299                 if (U_SUCCESS(err) && ptnLen > 0) {
300                     UnicodeString* pattern = new UnicodeString(patternChars, ptnLen);
301 #ifdef CURRENCY_PLURAL_INFO_DEBUG
302                     char result_1[1000];
303                     pattern->extract(0, pattern->length(), result_1, "UTF-8");
304                     std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n";
305 #endif
306                     pattern->findAndReplace(UnicodeString(TRUE, gPart0, 3),
307                       UnicodeString(numberStylePattern, numberStylePatternLen));
308                     pattern->findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
309 
310                     if (hasSeparator) {
311                         UnicodeString negPattern(patternChars, ptnLen);
312                         negPattern.findAndReplace(UnicodeString(TRUE, gPart0, 3),
313                           UnicodeString(negNumberStylePattern, negNumberStylePatternLen));
314                         negPattern.findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
315                         pattern->append(gNumberPatternSeparator);
316                         pattern->append(negPattern);
317                     }
318 #ifdef CURRENCY_PLURAL_INFO_DEBUG
319                     pattern->extract(0, pattern->length(), result_1, "UTF-8");
320                     std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n";
321 #endif
322 
323                     fPluralCountToCurrencyUnitPattern->put(UnicodeString(pluralCount, -1, US_INV), pattern, status);
324                 }
325             }
326         }
327     }
328     delete keywords;
329     ures_close(currencyRes);
330     ures_close(currRb);
331 }
332 
333 
334 
335 void
deleteHash(Hashtable * hTable)336 CurrencyPluralInfo::deleteHash(Hashtable* hTable)
337 {
338     if ( hTable == NULL ) {
339         return;
340     }
341     int32_t pos = UHASH_FIRST;
342     const UHashElement* element = NULL;
343     while ( (element = hTable->nextElement(pos)) != NULL ) {
344         const UHashTok valueTok = element->value;
345         const UnicodeString* value = (UnicodeString*)valueTok.pointer;
346         delete value;
347     }
348     delete hTable;
349     hTable = NULL;
350 }
351 
352 
353 Hashtable*
initHash(UErrorCode & status)354 CurrencyPluralInfo::initHash(UErrorCode& status) {
355     if ( U_FAILURE(status) ) {
356         return NULL;
357     }
358     Hashtable* hTable;
359     if ( (hTable = new Hashtable(TRUE, status)) == NULL ) {
360         status = U_MEMORY_ALLOCATION_ERROR;
361         return NULL;
362     }
363     if ( U_FAILURE(status) ) {
364         delete hTable;
365         return NULL;
366     }
367     hTable->setValueComparator(ValueComparator);
368     return hTable;
369 }
370 
371 
372 void
copyHash(const Hashtable * source,Hashtable * target,UErrorCode & status)373 CurrencyPluralInfo::copyHash(const Hashtable* source,
374                            Hashtable* target,
375                            UErrorCode& status) {
376     if ( U_FAILURE(status) ) {
377         return;
378     }
379     int32_t pos = UHASH_FIRST;
380     const UHashElement* element = NULL;
381     if ( source ) {
382         while ( (element = source->nextElement(pos)) != NULL ) {
383             const UHashTok keyTok = element->key;
384             const UnicodeString* key = (UnicodeString*)keyTok.pointer;
385             const UHashTok valueTok = element->value;
386             const UnicodeString* value = (UnicodeString*)valueTok.pointer;
387             UnicodeString* copy = new UnicodeString(*value);
388             target->put(UnicodeString(*key), copy, status);
389             if ( U_FAILURE(status) ) {
390                 return;
391             }
392         }
393     }
394 }
395 
396 
397 U_NAMESPACE_END
398 
399 #endif
400