1 /*
2 *******************************************************************************
3 * Copyright (C) 1997-2010, 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 "ucurrimp.h"
33 #include "cstring.h"
34 #include "locbased.h"
35 #include "uresimp.h"
36 #include "ureslocs.h"
37
38 // *****************************************************************************
39 // class DecimalFormatSymbols
40 // *****************************************************************************
41
42 U_NAMESPACE_BEGIN
43
44 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormatSymbols)
45
46 static const char gNumberElements[] = "NumberElements";
47 static const char gCurrencySpacingTag[] = "currencySpacing";
48 static const char gBeforeCurrencyTag[] = "beforeCurrency";
49 static const char gAfterCurrencyTag[] = "afterCurrency";
50 static const char gCurrencyMatchTag[] = "currencyMatch";
51 static const char gCurrencySudMatchTag[] = "surroundingMatch";
52 static const char gCurrencyInsertBtnTag[] = "insertBetween";
53
54
55 static const UChar INTL_CURRENCY_SYMBOL_STR[] = {0xa4, 0xa4, 0};
56
57 // -------------------------------------
58 // Initializes this with the decimal format symbols in the default locale.
59
DecimalFormatSymbols(UErrorCode & status)60 DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status)
61 : UObject(),
62 locale()
63 {
64 initialize(locale, status, TRUE);
65 }
66
67 // -------------------------------------
68 // Initializes this with the decimal format symbols in the desired locale.
69
DecimalFormatSymbols(const Locale & loc,UErrorCode & status)70 DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status)
71 : UObject(),
72 locale(loc)
73 {
74 initialize(locale, status);
75 }
76
77 // BEGIN android-added: we need a default constructor for performance.
78 // -------------------------------------
79
DecimalFormatSymbols()80 DecimalFormatSymbols::DecimalFormatSymbols()
81 {
82 initialize();
83 }
84 // END android-added
85
86 // -------------------------------------
87
~DecimalFormatSymbols()88 DecimalFormatSymbols::~DecimalFormatSymbols()
89 {
90 }
91
92 // -------------------------------------
93 // copy constructor
94
DecimalFormatSymbols(const DecimalFormatSymbols & source)95 DecimalFormatSymbols::DecimalFormatSymbols(const DecimalFormatSymbols &source)
96 : UObject(source)
97 {
98 *this = source;
99 }
100
101 // -------------------------------------
102 // assignment operator
103
104 DecimalFormatSymbols&
operator =(const DecimalFormatSymbols & rhs)105 DecimalFormatSymbols::operator=(const DecimalFormatSymbols& rhs)
106 {
107 if (this != &rhs) {
108 for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) {
109 // fastCopyFrom is safe, see docs on fSymbols
110 fSymbols[(ENumberFormatSymbol)i].fastCopyFrom(rhs.fSymbols[(ENumberFormatSymbol)i]);
111 }
112 for(int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; ++i) {
113 currencySpcBeforeSym[i].fastCopyFrom(rhs.currencySpcBeforeSym[i]);
114 currencySpcAfterSym[i].fastCopyFrom(rhs.currencySpcAfterSym[i]);
115 }
116 locale = rhs.locale;
117 uprv_strcpy(validLocale, rhs.validLocale);
118 uprv_strcpy(actualLocale, rhs.actualLocale);
119 }
120 return *this;
121 }
122
123 // -------------------------------------
124
125 UBool
operator ==(const DecimalFormatSymbols & that) const126 DecimalFormatSymbols::operator==(const DecimalFormatSymbols& that) const
127 {
128 if (this == &that) {
129 return TRUE;
130 }
131 for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) {
132 if(fSymbols[(ENumberFormatSymbol)i] != that.fSymbols[(ENumberFormatSymbol)i]) {
133 return FALSE;
134 }
135 }
136 for(int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; ++i) {
137 if(currencySpcBeforeSym[i] != that.currencySpcBeforeSym[i]) {
138 return FALSE;
139 }
140 if(currencySpcAfterSym[i] != that.currencySpcAfterSym[i]) {
141 return FALSE;
142 }
143 }
144 return locale == that.locale &&
145 uprv_strcmp(validLocale, that.validLocale) == 0 &&
146 uprv_strcmp(actualLocale, that.actualLocale) == 0;
147 }
148
149 // -------------------------------------
150
151 void
initialize(const Locale & loc,UErrorCode & status,UBool useLastResortData)152 DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool useLastResortData)
153 {
154 static const char *gNumberElementKeys[kFormatSymbolCount] = {
155 "decimal",
156 "group",
157 "list",
158 "percentSign",
159 NULL, /* Native zero digit is deprecated from CLDR - get it from the numbering system */
160 NULL, /* Pattern digit character is deprecated from CLDR - use # by default always */
161 "minusSign",
162 "plusSign",
163 NULL, /* currency symbol - We don't really try to load this directly from CLDR until we know the currency */
164 NULL, /* intl currency symbol - We don't really try to load this directly from CLDR until we know the currency */
165 "currencyDecimal",
166 "exponential",
167 "perMille",
168 NULL, /* Escape padding character - not in CLDR */
169 "infinity",
170 "nan",
171 NULL, /* Significant digit symbol - not in CLDR */
172 "currencyGroup",
173 NULL, /* one digit - get it from the numbering system */
174 NULL, /* two digit - get it from the numbering system */
175 NULL, /* three digit - get it from the numbering system */
176 NULL, /* four digit - get it from the numbering system */
177 NULL, /* five digit - get it from the numbering system */
178 NULL, /* six digit - get it from the numbering system */
179 NULL, /* seven digit - get it from the numbering system */
180 NULL, /* eight digit - get it from the numbering system */
181 NULL, /* nine digit - get it from the numbering system */
182 };
183
184 static const char *gLatn = "latn";
185 static const char *gSymbols = "symbols";
186 const char *nsName;
187 const UChar *sym = NULL;
188 int32_t len = 0;
189
190 *validLocale = *actualLocale = 0;
191 currPattern = NULL;
192 if (U_FAILURE(status))
193 return;
194
195 const char* locStr = loc.getName();
196 UResourceBundle *resource = ures_open((char *)0, locStr, &status);
197 UResourceBundle *numberElementsRes = ures_getByKeyWithFallback(resource, gNumberElements, NULL, &status);
198
199 if (U_FAILURE(status)) {
200 if ( useLastResortData ) {
201 status = U_USING_FALLBACK_WARNING;
202 initialize();
203 }
204 return;
205 } else {
206
207 // First initialize all the symbols to the fallbacks for anything we can't find
208 initialize();
209
210 //
211 // Next get the numbering system for this locale and set zero digit
212 // and the digit string based on the numbering system for the locale
213 //
214
215 NumberingSystem* ns = NumberingSystem::createInstance(loc,status);
216 if (U_SUCCESS(status) && ns->getRadix() == 10 && !ns->isAlgorithmic()) {
217 nsName = ns->getName();
218 UnicodeString *DigitString = new UnicodeString(ns->getDescription());
219 setSymbol(kZeroDigitSymbol,DigitString->charAt(0),FALSE);
220 setSymbol(kOneDigitSymbol,DigitString->charAt(1),FALSE);
221 setSymbol(kTwoDigitSymbol,DigitString->charAt(2),FALSE);
222 setSymbol(kThreeDigitSymbol,DigitString->charAt(3),FALSE);
223 setSymbol(kFourDigitSymbol,DigitString->charAt(4),FALSE);
224 setSymbol(kFiveDigitSymbol,DigitString->charAt(5),FALSE);
225 setSymbol(kSixDigitSymbol,DigitString->charAt(6),FALSE);
226 setSymbol(kSevenDigitSymbol,DigitString->charAt(7),FALSE);
227 setSymbol(kEightDigitSymbol,DigitString->charAt(8),FALSE);
228 setSymbol(kNineDigitSymbol,DigitString->charAt(9),FALSE);
229 delete DigitString;
230 } else {
231 nsName = gLatn;
232 }
233
234 UBool isLatn = !uprv_strcmp(nsName,gLatn);
235
236 UErrorCode nlStatus = U_ZERO_ERROR;
237 UResourceBundle *nonLatnSymbols = NULL;
238 if ( !isLatn ) {
239 nonLatnSymbols = ures_getByKeyWithFallback(numberElementsRes, nsName, NULL, &nlStatus);
240 nonLatnSymbols = ures_getByKeyWithFallback(nonLatnSymbols, gSymbols, nonLatnSymbols, &nlStatus);
241 }
242
243 UResourceBundle *latnSymbols = ures_getByKeyWithFallback(numberElementsRes, gLatn, NULL, &status);
244 latnSymbols = ures_getByKeyWithFallback(latnSymbols, gSymbols, latnSymbols, &status);
245
246 UBool kMonetaryDecimalSet = FALSE;
247 UBool kMonetaryGroupingSet = FALSE;
248 for(int32_t i = 0; i<kFormatSymbolCount; i++) {
249 if ( gNumberElementKeys[i] != NULL ) {
250 UErrorCode localStatus = U_ZERO_ERROR;
251 if ( !isLatn ) {
252 sym = ures_getStringByKeyWithFallback(nonLatnSymbols,gNumberElementKeys[i],&len,&localStatus);
253 // If we can't find the symbol in the numbering system specific resources,
254 // use the "latn" numbering system as the fallback.
255 if ( U_FAILURE(localStatus) ) {
256 localStatus = U_ZERO_ERROR;
257 sym = ures_getStringByKeyWithFallback(latnSymbols,gNumberElementKeys[i],&len,&localStatus);
258 }
259 } else {
260 sym = ures_getStringByKeyWithFallback(latnSymbols,gNumberElementKeys[i],&len,&localStatus);
261 }
262
263 if ( U_SUCCESS(localStatus) ) {
264 setSymbol((ENumberFormatSymbol)i,sym);
265 if ( i == kMonetarySeparatorSymbol ) {
266 kMonetaryDecimalSet = TRUE;
267 } else if ( i == kMonetaryGroupingSeparatorSymbol ) {
268 kMonetaryGroupingSet = TRUE;
269 }
270 }
271 }
272 }
273
274 ures_close(latnSymbols);
275 if ( !isLatn ) {
276 ures_close(nonLatnSymbols);
277 }
278
279 // If monetary decimal or grouping were not explicitly set, then set them to be the
280 // same as their non-monetary counterparts.
281
282 if ( !kMonetaryDecimalSet ) {
283 setSymbol(kMonetarySeparatorSymbol,fSymbols[kDecimalSeparatorSymbol]);
284 }
285 if ( !kMonetaryGroupingSet ) {
286 setSymbol(kMonetaryGroupingSeparatorSymbol,fSymbols[kGroupingSeparatorSymbol]);
287 }
288
289 if (ns) {
290 delete ns;
291 }
292
293 // Obtain currency data from the currency API. This is strictly
294 // for backward compatibility; we don't use DecimalFormatSymbols
295 // for currency data anymore.
296 UErrorCode internalStatus = U_ZERO_ERROR; // don't propagate failures out
297 UChar curriso[4];
298 UnicodeString tempStr;
299 ucurr_forLocale(locStr, curriso, 4, &internalStatus);
300
301 // Reuse numberElements[0] as a temporary buffer
302 uprv_getStaticCurrencyName(curriso, locStr, tempStr, internalStatus);
303 if (U_SUCCESS(internalStatus)) {
304 fSymbols[kIntlCurrencySymbol] = curriso;
305 fSymbols[kCurrencySymbol] = tempStr;
306 }
307 /* else use the default values. */
308
309 U_LOCALE_BASED(locBased, *this);
310 locBased.setLocaleIDs(ures_getLocaleByType(numberElementsRes,
311 ULOC_VALID_LOCALE, &status),
312 ures_getLocaleByType(numberElementsRes,
313 ULOC_ACTUAL_LOCALE, &status));
314
315 //load the currency data
316 UChar ucc[4]={0}; //Currency Codes are always 3 chars long
317 int32_t uccLen = 4;
318 const char* locName = loc.getName();
319 UErrorCode localStatus = U_ZERO_ERROR;
320 uccLen = ucurr_forLocale(locName, ucc, uccLen, &localStatus);
321
322 if(U_SUCCESS(localStatus) && uccLen > 0) {
323 char cc[4]={0};
324 u_UCharsToChars(ucc, cc, uccLen);
325 /* An explicit currency was requested */
326 UResourceBundle *currencyResource = ures_open(U_ICUDATA_CURR, locStr, &localStatus);
327 UResourceBundle *currency = ures_getByKeyWithFallback(currencyResource, "Currencies", NULL, &localStatus);
328 currency = ures_getByKeyWithFallback(currency, cc, currency, &localStatus);
329 if(U_SUCCESS(localStatus) && ures_getSize(currency)>2) { // the length is 3 if more data is present
330 currency = ures_getByIndex(currency, 2, currency, &localStatus);
331 int32_t currPatternLen = 0;
332 currPattern = ures_getStringByIndex(currency, (int32_t)0, &currPatternLen, &localStatus);
333 UnicodeString decimalSep = ures_getStringByIndex(currency, (int32_t)1, NULL, &localStatus);
334 UnicodeString groupingSep = ures_getStringByIndex(currency, (int32_t)2, NULL, &localStatus);
335 if(U_SUCCESS(localStatus)){
336 fSymbols[kMonetaryGroupingSeparatorSymbol] = groupingSep;
337 fSymbols[kMonetarySeparatorSymbol] = decimalSep;
338 //pattern.setTo(TRUE, currPattern, currPatternLen);
339 status = localStatus;
340 }
341 }
342 ures_close(currency);
343 ures_close(currencyResource);
344 /* else An explicit currency was requested and is unknown or locale data is malformed. */
345 /* ucurr_* API will get the correct value later on. */
346 }
347 // else ignore the error if no currency
348
349 // Currency Spacing.
350 localStatus = U_ZERO_ERROR;
351 UResourceBundle *currencyResource = ures_open(U_ICUDATA_CURR, locStr, &localStatus);
352 UResourceBundle *currencySpcRes = ures_getByKeyWithFallback(currencyResource,
353 gCurrencySpacingTag, NULL, &localStatus);
354
355 if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) {
356 const char* keywords[UNUM_CURRENCY_SPACING_COUNT] = {
357 gCurrencyMatchTag, gCurrencySudMatchTag, gCurrencyInsertBtnTag
358 };
359 localStatus = U_ZERO_ERROR;
360 UResourceBundle *dataRes = ures_getByKeyWithFallback(currencySpcRes,
361 gBeforeCurrencyTag, NULL, &localStatus);
362 if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) {
363 localStatus = U_ZERO_ERROR;
364 for (int32_t i = 0; i < UNUM_CURRENCY_SPACING_COUNT; i++) {
365 currencySpcBeforeSym[i] = ures_getStringByKey(dataRes, keywords[i],
366 NULL, &localStatus);
367 }
368 ures_close(dataRes);
369 }
370 dataRes = ures_getByKeyWithFallback(currencySpcRes,
371 gAfterCurrencyTag, NULL, &localStatus);
372 if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) {
373 localStatus = U_ZERO_ERROR;
374 for (int32_t i = 0; i < UNUM_CURRENCY_SPACING_COUNT; i++) {
375 currencySpcAfterSym[i] = ures_getStringByKey(dataRes, keywords[i],
376 NULL, &localStatus);
377 }
378 ures_close(dataRes);
379 }
380 ures_close(currencySpcRes);
381 ures_close(currencyResource);
382 }
383 }
384 ures_close(resource);
385 ures_close(numberElementsRes);
386
387 }
388
389 void
initialize()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] = INTL_CURRENCY_SYMBOL_STR;
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
getLocale(ULocDataLocaleType type,UErrorCode & status) const426 DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
427 U_LOCALE_BASED(locBased, *this);
428 return locBased.getLocale(type, status);
429 }
430
431 const UnicodeString&
getPatternForCurrencySpacing(UCurrencySpacing type,UBool beforeCurrency,UErrorCode & status) const432 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
setPatternForCurrencySpacing(UCurrencySpacing type,UBool beforeCurrency,const UnicodeString & pattern)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
460