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 "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 = -1;
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 = -1;
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