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