|
1 /* |
|
2 ******************************************************************************* |
|
3 * Copyright (C) 1997-2013, International Business Machines Corporation and |
|
4 * others. All Rights Reserved. |
|
5 ******************************************************************************* |
|
6 * |
|
7 * File DCFMTSYM.CPP |
|
8 * |
|
9 * Modification History: |
|
10 * |
|
11 * Date Name Description |
|
12 * 02/19/97 aliu Converted from java. |
|
13 * 03/18/97 clhuang Implemented with C++ APIs. |
|
14 * 03/27/97 helena Updated to pass the simple test after code review. |
|
15 * 08/26/97 aliu Added currency/intl currency symbol support. |
|
16 * 07/20/98 stephen Slightly modified initialization of monetarySeparator |
|
17 ******************************************************************************** |
|
18 */ |
|
19 |
|
20 #include "unicode/utypes.h" |
|
21 |
|
22 #if !UCONFIG_NO_FORMATTING |
|
23 |
|
24 #include "unicode/dcfmtsym.h" |
|
25 #include "unicode/ures.h" |
|
26 #include "unicode/decimfmt.h" |
|
27 #include "unicode/ucurr.h" |
|
28 #include "unicode/choicfmt.h" |
|
29 #include "unicode/unistr.h" |
|
30 #include "unicode/numsys.h" |
|
31 #include "unicode/unum.h" |
|
32 #include "unicode/utf16.h" |
|
33 #include "ucurrimp.h" |
|
34 #include "cstring.h" |
|
35 #include "locbased.h" |
|
36 #include "uresimp.h" |
|
37 #include "ureslocs.h" |
|
38 |
|
39 // ***************************************************************************** |
|
40 // class DecimalFormatSymbols |
|
41 // ***************************************************************************** |
|
42 |
|
43 U_NAMESPACE_BEGIN |
|
44 |
|
45 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormatSymbols) |
|
46 |
|
47 static const char gNumberElements[] = "NumberElements"; |
|
48 static const char gCurrencySpacingTag[] = "currencySpacing"; |
|
49 static const char gBeforeCurrencyTag[] = "beforeCurrency"; |
|
50 static const char gAfterCurrencyTag[] = "afterCurrency"; |
|
51 static const char gCurrencyMatchTag[] = "currencyMatch"; |
|
52 static const char gCurrencySudMatchTag[] = "surroundingMatch"; |
|
53 static const char gCurrencyInsertBtnTag[] = "insertBetween"; |
|
54 |
|
55 |
|
56 static const UChar INTL_CURRENCY_SYMBOL_STR[] = {0xa4, 0xa4, 0}; |
|
57 |
|
58 // ------------------------------------- |
|
59 // Initializes this with the decimal format symbols in the default locale. |
|
60 |
|
61 DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status) |
|
62 : UObject(), |
|
63 locale() |
|
64 { |
|
65 initialize(locale, status, TRUE); |
|
66 } |
|
67 |
|
68 // ------------------------------------- |
|
69 // Initializes this with the decimal format symbols in the desired locale. |
|
70 |
|
71 DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status) |
|
72 : UObject(), |
|
73 locale(loc) |
|
74 { |
|
75 initialize(locale, status); |
|
76 } |
|
77 |
|
78 DecimalFormatSymbols::DecimalFormatSymbols() |
|
79 : UObject(), |
|
80 locale(Locale::getRoot()), |
|
81 currPattern(NULL) { |
|
82 *validLocale = *actualLocale = 0; |
|
83 initialize(); |
|
84 } |
|
85 |
|
86 DecimalFormatSymbols* |
|
87 DecimalFormatSymbols::createWithLastResortData(UErrorCode& status) { |
|
88 if (U_FAILURE(status)) { return NULL; } |
|
89 DecimalFormatSymbols* sym = new DecimalFormatSymbols(); |
|
90 if (sym == NULL) { |
|
91 status = U_MEMORY_ALLOCATION_ERROR; |
|
92 } |
|
93 return sym; |
|
94 } |
|
95 |
|
96 // ------------------------------------- |
|
97 |
|
98 DecimalFormatSymbols::~DecimalFormatSymbols() |
|
99 { |
|
100 } |
|
101 |
|
102 // ------------------------------------- |
|
103 // copy constructor |
|
104 |
|
105 DecimalFormatSymbols::DecimalFormatSymbols(const DecimalFormatSymbols &source) |
|
106 : UObject(source) |
|
107 { |
|
108 *this = source; |
|
109 } |
|
110 |
|
111 // ------------------------------------- |
|
112 // assignment operator |
|
113 |
|
114 DecimalFormatSymbols& |
|
115 DecimalFormatSymbols::operator=(const DecimalFormatSymbols& rhs) |
|
116 { |
|
117 if (this != &rhs) { |
|
118 for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) { |
|
119 // fastCopyFrom is safe, see docs on fSymbols |
|
120 fSymbols[(ENumberFormatSymbol)i].fastCopyFrom(rhs.fSymbols[(ENumberFormatSymbol)i]); |
|
121 } |
|
122 for(int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; ++i) { |
|
123 currencySpcBeforeSym[i].fastCopyFrom(rhs.currencySpcBeforeSym[i]); |
|
124 currencySpcAfterSym[i].fastCopyFrom(rhs.currencySpcAfterSym[i]); |
|
125 } |
|
126 locale = rhs.locale; |
|
127 uprv_strcpy(validLocale, rhs.validLocale); |
|
128 uprv_strcpy(actualLocale, rhs.actualLocale); |
|
129 } |
|
130 return *this; |
|
131 } |
|
132 |
|
133 // ------------------------------------- |
|
134 |
|
135 UBool |
|
136 DecimalFormatSymbols::operator==(const DecimalFormatSymbols& that) const |
|
137 { |
|
138 if (this == &that) { |
|
139 return TRUE; |
|
140 } |
|
141 for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) { |
|
142 if(fSymbols[(ENumberFormatSymbol)i] != that.fSymbols[(ENumberFormatSymbol)i]) { |
|
143 return FALSE; |
|
144 } |
|
145 } |
|
146 for(int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; ++i) { |
|
147 if(currencySpcBeforeSym[i] != that.currencySpcBeforeSym[i]) { |
|
148 return FALSE; |
|
149 } |
|
150 if(currencySpcAfterSym[i] != that.currencySpcAfterSym[i]) { |
|
151 return FALSE; |
|
152 } |
|
153 } |
|
154 return locale == that.locale && |
|
155 uprv_strcmp(validLocale, that.validLocale) == 0 && |
|
156 uprv_strcmp(actualLocale, that.actualLocale) == 0; |
|
157 } |
|
158 |
|
159 // ------------------------------------- |
|
160 |
|
161 void |
|
162 DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool useLastResortData) |
|
163 { |
|
164 static const char *gNumberElementKeys[kFormatSymbolCount] = { |
|
165 "decimal", |
|
166 "group", |
|
167 "list", |
|
168 "percentSign", |
|
169 NULL, /* Native zero digit is deprecated from CLDR - get it from the numbering system */ |
|
170 NULL, /* Pattern digit character is deprecated from CLDR - use # by default always */ |
|
171 "minusSign", |
|
172 "plusSign", |
|
173 NULL, /* currency symbol - We don't really try to load this directly from CLDR until we know the currency */ |
|
174 NULL, /* intl currency symbol - We don't really try to load this directly from CLDR until we know the currency */ |
|
175 "currencyDecimal", |
|
176 "exponential", |
|
177 "perMille", |
|
178 NULL, /* Escape padding character - not in CLDR */ |
|
179 "infinity", |
|
180 "nan", |
|
181 NULL, /* Significant digit symbol - not in CLDR */ |
|
182 "currencyGroup", |
|
183 NULL, /* one digit - get it from the numbering system */ |
|
184 NULL, /* two digit - get it from the numbering system */ |
|
185 NULL, /* three digit - get it from the numbering system */ |
|
186 NULL, /* four digit - get it from the numbering system */ |
|
187 NULL, /* five digit - get it from the numbering system */ |
|
188 NULL, /* six digit - get it from the numbering system */ |
|
189 NULL, /* seven digit - get it from the numbering system */ |
|
190 NULL, /* eight digit - get it from the numbering system */ |
|
191 NULL, /* nine digit - get it from the numbering system */ |
|
192 }; |
|
193 |
|
194 static const char *gLatn = "latn"; |
|
195 static const char *gSymbols = "symbols"; |
|
196 const char *nsName; |
|
197 const UChar *sym = NULL; |
|
198 int32_t len = 0; |
|
199 |
|
200 *validLocale = *actualLocale = 0; |
|
201 currPattern = NULL; |
|
202 if (U_FAILURE(status)) |
|
203 return; |
|
204 |
|
205 const char* locStr = loc.getName(); |
|
206 LocalUResourceBundlePointer resource(ures_open(NULL, locStr, &status)); |
|
207 LocalUResourceBundlePointer numberElementsRes( |
|
208 ures_getByKeyWithFallback(resource.getAlias(), gNumberElements, NULL, &status)); |
|
209 |
|
210 if (U_FAILURE(status)) { |
|
211 if ( useLastResortData ) { |
|
212 status = U_USING_DEFAULT_WARNING; |
|
213 initialize(); |
|
214 } |
|
215 return; |
|
216 } |
|
217 |
|
218 // First initialize all the symbols to the fallbacks for anything we can't find |
|
219 initialize(); |
|
220 |
|
221 // |
|
222 // Next get the numbering system for this locale and set zero digit |
|
223 // and the digit string based on the numbering system for the locale |
|
224 // |
|
225 |
|
226 LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(loc, status)); |
|
227 if (U_SUCCESS(status) && ns->getRadix() == 10 && !ns->isAlgorithmic()) { |
|
228 nsName = ns->getName(); |
|
229 UnicodeString digitString(ns->getDescription()); |
|
230 int32_t digitIndex = 0; |
|
231 UChar32 digit = digitString.char32At(0); |
|
232 fSymbols[kZeroDigitSymbol].setTo(digit); |
|
233 for (int32_t i = kOneDigitSymbol; i <= kNineDigitSymbol; ++i) { |
|
234 digitIndex += U16_LENGTH(digit); |
|
235 digit = digitString.char32At(digitIndex); |
|
236 fSymbols[i].setTo(digit); |
|
237 } |
|
238 } else { |
|
239 nsName = gLatn; |
|
240 } |
|
241 |
|
242 UBool isLatn = !uprv_strcmp(nsName,gLatn); |
|
243 |
|
244 UErrorCode nlStatus = U_ZERO_ERROR; |
|
245 LocalUResourceBundlePointer nonLatnSymbols; |
|
246 if ( !isLatn ) { |
|
247 nonLatnSymbols.adoptInstead( |
|
248 ures_getByKeyWithFallback(numberElementsRes.getAlias(), nsName, NULL, &nlStatus)); |
|
249 ures_getByKeyWithFallback(nonLatnSymbols.getAlias(), gSymbols, nonLatnSymbols.getAlias(), &nlStatus); |
|
250 } |
|
251 |
|
252 LocalUResourceBundlePointer latnSymbols( |
|
253 ures_getByKeyWithFallback(numberElementsRes.getAlias(), gLatn, NULL, &status)); |
|
254 ures_getByKeyWithFallback(latnSymbols.getAlias(), gSymbols, latnSymbols.getAlias(), &status); |
|
255 |
|
256 UBool kMonetaryDecimalSet = FALSE; |
|
257 UBool kMonetaryGroupingSet = FALSE; |
|
258 for(int32_t i = 0; i<kFormatSymbolCount; i++) { |
|
259 if ( gNumberElementKeys[i] != NULL ) { |
|
260 UErrorCode localStatus = U_ZERO_ERROR; |
|
261 if ( !isLatn ) { |
|
262 sym = ures_getStringByKeyWithFallback(nonLatnSymbols.getAlias(), |
|
263 gNumberElementKeys[i], &len, &localStatus); |
|
264 // If we can't find the symbol in the numbering system specific resources, |
|
265 // use the "latn" numbering system as the fallback. |
|
266 if ( U_FAILURE(localStatus) ) { |
|
267 localStatus = U_ZERO_ERROR; |
|
268 sym = ures_getStringByKeyWithFallback(latnSymbols.getAlias(), |
|
269 gNumberElementKeys[i], &len, &localStatus); |
|
270 } |
|
271 } else { |
|
272 sym = ures_getStringByKeyWithFallback(latnSymbols.getAlias(), |
|
273 gNumberElementKeys[i], &len, &localStatus); |
|
274 } |
|
275 |
|
276 if ( U_SUCCESS(localStatus) ) { |
|
277 setSymbol((ENumberFormatSymbol)i, UnicodeString(TRUE, sym, len)); |
|
278 if ( i == kMonetarySeparatorSymbol ) { |
|
279 kMonetaryDecimalSet = TRUE; |
|
280 } else if ( i == kMonetaryGroupingSeparatorSymbol ) { |
|
281 kMonetaryGroupingSet = TRUE; |
|
282 } |
|
283 } |
|
284 } |
|
285 } |
|
286 |
|
287 // If monetary decimal or grouping were not explicitly set, then set them to be the |
|
288 // same as their non-monetary counterparts. |
|
289 |
|
290 if ( !kMonetaryDecimalSet ) { |
|
291 setSymbol(kMonetarySeparatorSymbol,fSymbols[kDecimalSeparatorSymbol]); |
|
292 } |
|
293 if ( !kMonetaryGroupingSet ) { |
|
294 setSymbol(kMonetaryGroupingSeparatorSymbol,fSymbols[kGroupingSeparatorSymbol]); |
|
295 } |
|
296 |
|
297 // Obtain currency data from the currency API. This is strictly |
|
298 // for backward compatibility; we don't use DecimalFormatSymbols |
|
299 // for currency data anymore. |
|
300 UErrorCode internalStatus = U_ZERO_ERROR; // don't propagate failures out |
|
301 UChar curriso[4]; |
|
302 UnicodeString tempStr; |
|
303 ucurr_forLocale(locStr, curriso, 4, &internalStatus); |
|
304 |
|
305 uprv_getStaticCurrencyName(curriso, locStr, tempStr, internalStatus); |
|
306 if (U_SUCCESS(internalStatus)) { |
|
307 fSymbols[kIntlCurrencySymbol].setTo(curriso, -1); |
|
308 fSymbols[kCurrencySymbol] = tempStr; |
|
309 } |
|
310 /* else use the default values. */ |
|
311 |
|
312 U_LOCALE_BASED(locBased, *this); |
|
313 locBased.setLocaleIDs(ures_getLocaleByType(numberElementsRes.getAlias(), |
|
314 ULOC_VALID_LOCALE, &status), |
|
315 ures_getLocaleByType(numberElementsRes.getAlias(), |
|
316 ULOC_ACTUAL_LOCALE, &status)); |
|
317 |
|
318 //load the currency data |
|
319 UChar ucc[4]={0}; //Currency Codes are always 3 chars long |
|
320 int32_t uccLen = 4; |
|
321 const char* locName = loc.getName(); |
|
322 UErrorCode localStatus = U_ZERO_ERROR; |
|
323 uccLen = ucurr_forLocale(locName, ucc, uccLen, &localStatus); |
|
324 |
|
325 if(U_SUCCESS(localStatus) && uccLen > 0) { |
|
326 char cc[4]={0}; |
|
327 u_UCharsToChars(ucc, cc, uccLen); |
|
328 /* An explicit currency was requested */ |
|
329 LocalUResourceBundlePointer currencyResource(ures_open(U_ICUDATA_CURR, locStr, &localStatus)); |
|
330 LocalUResourceBundlePointer currency( |
|
331 ures_getByKeyWithFallback(currencyResource.getAlias(), "Currencies", NULL, &localStatus)); |
|
332 ures_getByKeyWithFallback(currency.getAlias(), cc, currency.getAlias(), &localStatus); |
|
333 if(U_SUCCESS(localStatus) && ures_getSize(currency.getAlias())>2) { // the length is 3 if more data is present |
|
334 ures_getByIndex(currency.getAlias(), 2, currency.getAlias(), &localStatus); |
|
335 int32_t currPatternLen = 0; |
|
336 currPattern = |
|
337 ures_getStringByIndex(currency.getAlias(), (int32_t)0, &currPatternLen, &localStatus); |
|
338 UnicodeString decimalSep = |
|
339 ures_getUnicodeStringByIndex(currency.getAlias(), (int32_t)1, &localStatus); |
|
340 UnicodeString groupingSep = |
|
341 ures_getUnicodeStringByIndex(currency.getAlias(), (int32_t)2, &localStatus); |
|
342 if(U_SUCCESS(localStatus)){ |
|
343 fSymbols[kMonetaryGroupingSeparatorSymbol] = groupingSep; |
|
344 fSymbols[kMonetarySeparatorSymbol] = decimalSep; |
|
345 //pattern.setTo(TRUE, currPattern, currPatternLen); |
|
346 status = localStatus; |
|
347 } |
|
348 } |
|
349 /* else An explicit currency was requested and is unknown or locale data is malformed. */ |
|
350 /* ucurr_* API will get the correct value later on. */ |
|
351 } |
|
352 // else ignore the error if no currency |
|
353 |
|
354 // Currency Spacing. |
|
355 localStatus = U_ZERO_ERROR; |
|
356 LocalUResourceBundlePointer currencyResource(ures_open(U_ICUDATA_CURR, locStr, &localStatus)); |
|
357 LocalUResourceBundlePointer currencySpcRes( |
|
358 ures_getByKeyWithFallback(currencyResource.getAlias(), |
|
359 gCurrencySpacingTag, NULL, &localStatus)); |
|
360 |
|
361 if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) { |
|
362 const char* keywords[UNUM_CURRENCY_SPACING_COUNT] = { |
|
363 gCurrencyMatchTag, gCurrencySudMatchTag, gCurrencyInsertBtnTag |
|
364 }; |
|
365 localStatus = U_ZERO_ERROR; |
|
366 LocalUResourceBundlePointer dataRes( |
|
367 ures_getByKeyWithFallback(currencySpcRes.getAlias(), |
|
368 gBeforeCurrencyTag, NULL, &localStatus)); |
|
369 if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) { |
|
370 localStatus = U_ZERO_ERROR; |
|
371 for (int32_t i = 0; i < UNUM_CURRENCY_SPACING_COUNT; i++) { |
|
372 currencySpcBeforeSym[i] = |
|
373 ures_getUnicodeStringByKey(dataRes.getAlias(), keywords[i], &localStatus); |
|
374 } |
|
375 } |
|
376 dataRes.adoptInstead( |
|
377 ures_getByKeyWithFallback(currencySpcRes.getAlias(), |
|
378 gAfterCurrencyTag, NULL, &localStatus)); |
|
379 if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) { |
|
380 localStatus = U_ZERO_ERROR; |
|
381 for (int32_t i = 0; i < UNUM_CURRENCY_SPACING_COUNT; i++) { |
|
382 currencySpcAfterSym[i] = |
|
383 ures_getUnicodeStringByKey(dataRes.getAlias(), keywords[i], &localStatus); |
|
384 } |
|
385 } |
|
386 } |
|
387 } |
|
388 |
|
389 void |
|
390 DecimalFormatSymbols::initialize() { |
|
391 /* |
|
392 * These strings used to be in static arrays, but the HP/UX aCC compiler |
|
393 * cannot initialize a static array with class constructors. |
|
394 * markus 2000may25 |
|
395 */ |
|
396 fSymbols[kDecimalSeparatorSymbol] = (UChar)0x2e; // '.' decimal separator |
|
397 fSymbols[kGroupingSeparatorSymbol].remove(); // group (thousands) separator |
|
398 fSymbols[kPatternSeparatorSymbol] = (UChar)0x3b; // ';' pattern separator |
|
399 fSymbols[kPercentSymbol] = (UChar)0x25; // '%' percent sign |
|
400 fSymbols[kZeroDigitSymbol] = (UChar)0x30; // '0' native 0 digit |
|
401 fSymbols[kOneDigitSymbol] = (UChar)0x31; // '1' native 1 digit |
|
402 fSymbols[kTwoDigitSymbol] = (UChar)0x32; // '2' native 2 digit |
|
403 fSymbols[kThreeDigitSymbol] = (UChar)0x33; // '3' native 3 digit |
|
404 fSymbols[kFourDigitSymbol] = (UChar)0x34; // '4' native 4 digit |
|
405 fSymbols[kFiveDigitSymbol] = (UChar)0x35; // '5' native 5 digit |
|
406 fSymbols[kSixDigitSymbol] = (UChar)0x36; // '6' native 6 digit |
|
407 fSymbols[kSevenDigitSymbol] = (UChar)0x37; // '7' native 7 digit |
|
408 fSymbols[kEightDigitSymbol] = (UChar)0x38; // '8' native 8 digit |
|
409 fSymbols[kNineDigitSymbol] = (UChar)0x39; // '9' native 9 digit |
|
410 fSymbols[kDigitSymbol] = (UChar)0x23; // '#' pattern digit |
|
411 fSymbols[kPlusSignSymbol] = (UChar)0x002b; // '+' plus sign |
|
412 fSymbols[kMinusSignSymbol] = (UChar)0x2d; // '-' minus sign |
|
413 fSymbols[kCurrencySymbol] = (UChar)0xa4; // 'OX' currency symbol |
|
414 fSymbols[kIntlCurrencySymbol].setTo(TRUE, INTL_CURRENCY_SYMBOL_STR, 2); |
|
415 fSymbols[kMonetarySeparatorSymbol] = (UChar)0x2e; // '.' monetary decimal separator |
|
416 fSymbols[kExponentialSymbol] = (UChar)0x45; // 'E' exponential |
|
417 fSymbols[kPerMillSymbol] = (UChar)0x2030; // '%o' per mill |
|
418 fSymbols[kPadEscapeSymbol] = (UChar)0x2a; // '*' pad escape symbol |
|
419 fSymbols[kInfinitySymbol] = (UChar)0x221e; // 'oo' infinite |
|
420 fSymbols[kNaNSymbol] = (UChar)0xfffd; // SUB NaN |
|
421 fSymbols[kSignificantDigitSymbol] = (UChar)0x0040; // '@' significant digit |
|
422 fSymbols[kMonetaryGroupingSeparatorSymbol].remove(); // |
|
423 } |
|
424 |
|
425 Locale |
|
426 DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const { |
|
427 U_LOCALE_BASED(locBased, *this); |
|
428 return locBased.getLocale(type, status); |
|
429 } |
|
430 |
|
431 const UnicodeString& |
|
432 DecimalFormatSymbols::getPatternForCurrencySpacing(UCurrencySpacing type, |
|
433 UBool beforeCurrency, |
|
434 UErrorCode& status) const { |
|
435 if (U_FAILURE(status)) { |
|
436 return fNoSymbol; // always empty. |
|
437 } |
|
438 if (beforeCurrency) { |
|
439 return currencySpcBeforeSym[(int32_t)type]; |
|
440 } else { |
|
441 return currencySpcAfterSym[(int32_t)type]; |
|
442 } |
|
443 } |
|
444 |
|
445 void |
|
446 DecimalFormatSymbols::setPatternForCurrencySpacing(UCurrencySpacing type, |
|
447 UBool beforeCurrency, |
|
448 const UnicodeString& pattern) { |
|
449 if (beforeCurrency) { |
|
450 currencySpcBeforeSym[(int32_t)type] = pattern; |
|
451 } else { |
|
452 currencySpcAfterSym[(int32_t)type] = pattern; |
|
453 } |
|
454 } |
|
455 U_NAMESPACE_END |
|
456 |
|
457 #endif /* #if !UCONFIG_NO_FORMATTING */ |
|
458 |
|
459 //eof |