2 *******************************************************************************
3 * Copyright (C) 1997-2010, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
9 * Modification History:
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 ********************************************************************************
20 #include "unicode/utypes.h"
22 #if !UCONFIG_NO_FORMATTING
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"
37 // *****************************************************************************
38 // class DecimalFormatSymbols
39 // *****************************************************************************
43 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormatSymbols
)
45 static const char gNumberElements
[] = "NumberElements";
46 static const char gCurrencySpacingTag
[] = "currencySpacing";
47 static const char gBeforeCurrencyTag
[] = "beforeCurrency";
48 static const char gAfterCurrencyTag
[] = "afterCurrency";
49 static const char gCurrencyMatchTag
[] = "currencyMatch";
50 static const char gCurrencySudMatchTag
[] = "surroundingMatch";
51 static const char gCurrencyInsertBtnTag
[] = "insertBetween";
54 static const UChar INTL_CURRENCY_SYMBOL_STR
[] = {0xa4, 0xa4, 0};
56 // -------------------------------------
57 // Initializes this with the decimal format symbols in the default locale.
59 DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode
& status
)
63 initialize(locale
, status
, TRUE
);
66 // -------------------------------------
67 // Initializes this with the decimal format symbols in the desired locale.
69 DecimalFormatSymbols::DecimalFormatSymbols(const Locale
& loc
, UErrorCode
& status
)
73 initialize(locale
, status
);
76 // -------------------------------------
78 DecimalFormatSymbols::~DecimalFormatSymbols()
82 // -------------------------------------
85 DecimalFormatSymbols::DecimalFormatSymbols(const DecimalFormatSymbols
&source
)
91 // -------------------------------------
92 // assignment operator
95 DecimalFormatSymbols::operator=(const DecimalFormatSymbols
& rhs
)
98 for(int32_t i
= 0; i
< (int32_t)kFormatSymbolCount
; ++i
) {
99 // fastCopyFrom is safe, see docs on fSymbols
100 fSymbols
[(ENumberFormatSymbol
)i
].fastCopyFrom(rhs
.fSymbols
[(ENumberFormatSymbol
)i
]);
102 for(int32_t i
= 0; i
< (int32_t)kCurrencySpacingCount
; ++i
) {
103 currencySpcBeforeSym
[i
].fastCopyFrom(rhs
.currencySpcBeforeSym
[i
]);
104 currencySpcAfterSym
[i
].fastCopyFrom(rhs
.currencySpcAfterSym
[i
]);
107 uprv_strcpy(validLocale
, rhs
.validLocale
);
108 uprv_strcpy(actualLocale
, rhs
.actualLocale
);
113 // -------------------------------------
116 DecimalFormatSymbols::operator==(const DecimalFormatSymbols
& that
) const
121 for(int32_t i
= 0; i
< (int32_t)kFormatSymbolCount
; ++i
) {
122 if(fSymbols
[(ENumberFormatSymbol
)i
] != that
.fSymbols
[(ENumberFormatSymbol
)i
]) {
126 for(int32_t i
= 0; i
< (int32_t)kCurrencySpacingCount
; ++i
) {
127 if(currencySpcBeforeSym
[i
] != that
.currencySpcBeforeSym
[i
]) {
130 if(currencySpcAfterSym
[i
] != that
.currencySpcAfterSym
[i
]) {
134 return locale
== that
.locale
&&
135 uprv_strcmp(validLocale
, that
.validLocale
) == 0 &&
136 uprv_strcmp(actualLocale
, that
.actualLocale
) == 0;
139 // -------------------------------------
142 DecimalFormatSymbols::initialize(const Locale
& loc
, UErrorCode
& status
, UBool useLastResortData
)
144 static const char *gNumberElementKeys
[kFormatSymbolCount
] = {
149 NULL
, /* Native zero digit is deprecated from CLDR - get it from the numbering system */
150 NULL
, /* Pattern digit character is deprecated from CLDR - use # by default always */
153 NULL
, /* currency symbol - We don't really try to load this directly from CLDR until we know the currency */
154 NULL
, /* intl currency symbol - We don't really try to load this directly from CLDR until we know the currency */
158 NULL
, /* Escape padding character - not in CLDR */
161 NULL
, /* Significant digit symbol - not in CLDR */
163 NULL
, /* one digit - get it from the numbering system */
164 NULL
, /* two digit - get it from the numbering system */
165 NULL
, /* three digit - get it from the numbering system */
166 NULL
, /* four digit - get it from the numbering system */
167 NULL
, /* five digit - get it from the numbering system */
168 NULL
, /* six digit - get it from the numbering system */
169 NULL
, /* seven digit - get it from the numbering system */
170 NULL
, /* eight digit - get it from the numbering system */
171 NULL
, /* nine digit - get it from the numbering system */
174 static const char *gLatn
= "latn";
175 static const char *gSymbols
= "symbols";
177 const UChar
*sym
= NULL
;
180 *validLocale
= *actualLocale
= 0;
182 if (U_FAILURE(status
))
185 const char* locStr
= loc
.getName();
186 UResourceBundle
*resource
= ures_open((char *)0, locStr
, &status
);
187 UResourceBundle
*numberElementsRes
= ures_getByKeyWithFallback(resource
, gNumberElements
, NULL
, &status
);
189 if (U_FAILURE(status
)) {
190 if ( useLastResortData
) {
191 status
= U_USING_FALLBACK_WARNING
;
197 // First initialize all the symbols to the fallbacks for anything we can't find
201 // Next get the numbering system for this locale and set zero digit
202 // and the digit string based on the numbering system for the locale
205 NumberingSystem
* ns
= NumberingSystem::createInstance(loc
,status
);
206 if (U_SUCCESS(status
) && ns
->getRadix() == 10 && !ns
->isAlgorithmic()) {
207 nsName
= ns
->getName();
208 UnicodeString
*DigitString
= new UnicodeString(ns
->getDescription());
209 setSymbol(kZeroDigitSymbol
,DigitString
->charAt(0),FALSE
);
210 setSymbol(kOneDigitSymbol
,DigitString
->charAt(1),FALSE
);
211 setSymbol(kTwoDigitSymbol
,DigitString
->charAt(2),FALSE
);
212 setSymbol(kThreeDigitSymbol
,DigitString
->charAt(3),FALSE
);
213 setSymbol(kFourDigitSymbol
,DigitString
->charAt(4),FALSE
);
214 setSymbol(kFiveDigitSymbol
,DigitString
->charAt(5),FALSE
);
215 setSymbol(kSixDigitSymbol
,DigitString
->charAt(6),FALSE
);
216 setSymbol(kSevenDigitSymbol
,DigitString
->charAt(7),FALSE
);
217 setSymbol(kEightDigitSymbol
,DigitString
->charAt(8),FALSE
);
218 setSymbol(kNineDigitSymbol
,DigitString
->charAt(9),FALSE
);
224 UBool isLatn
= !uprv_strcmp(nsName
,gLatn
);
226 UErrorCode nlStatus
= U_ZERO_ERROR
;
227 UResourceBundle
*nonLatnSymbols
= NULL
;
229 nonLatnSymbols
= ures_getByKeyWithFallback(numberElementsRes
, nsName
, NULL
, &nlStatus
);
230 nonLatnSymbols
= ures_getByKeyWithFallback(nonLatnSymbols
, gSymbols
, nonLatnSymbols
, &nlStatus
);
233 UResourceBundle
*latnSymbols
= ures_getByKeyWithFallback(numberElementsRes
, gLatn
, NULL
, &status
);
234 latnSymbols
= ures_getByKeyWithFallback(latnSymbols
, gSymbols
, latnSymbols
, &status
);
236 UBool kMonetaryDecimalSet
= FALSE
;
237 UBool kMonetaryGroupingSet
= FALSE
;
238 for(int32_t i
= 0; i
<kFormatSymbolCount
; i
++) {
239 if ( gNumberElementKeys
[i
] != NULL
) {
240 UErrorCode localStatus
= U_ZERO_ERROR
;
242 sym
= ures_getStringByKeyWithFallback(nonLatnSymbols
,gNumberElementKeys
[i
],&len
,&localStatus
);
243 // If we can't find the symbol in the numbering system specific resources,
244 // use the "latn" numbering system as the fallback.
245 if ( U_FAILURE(localStatus
) ) {
246 localStatus
= U_ZERO_ERROR
;
247 sym
= ures_getStringByKeyWithFallback(latnSymbols
,gNumberElementKeys
[i
],&len
,&localStatus
);
250 sym
= ures_getStringByKeyWithFallback(latnSymbols
,gNumberElementKeys
[i
],&len
,&localStatus
);
253 if ( U_SUCCESS(localStatus
) ) {
254 setSymbol((ENumberFormatSymbol
)i
,sym
);
255 if ( i
== kMonetarySeparatorSymbol
) {
256 kMonetaryDecimalSet
= TRUE
;
257 } else if ( i
== kMonetaryGroupingSeparatorSymbol
) {
258 kMonetaryGroupingSet
= TRUE
;
264 ures_close(latnSymbols
);
266 ures_close(nonLatnSymbols
);
269 // If monetary decimal or grouping were not explicitly set, then set them to be the
270 // same as their non-monetary counterparts.
272 if ( !kMonetaryDecimalSet
) {
273 setSymbol(kMonetarySeparatorSymbol
,fSymbols
[kDecimalSeparatorSymbol
]);
275 if ( !kMonetaryGroupingSet
) {
276 setSymbol(kMonetaryGroupingSeparatorSymbol
,fSymbols
[kGroupingSeparatorSymbol
]);
283 // Obtain currency data from the currency API. This is strictly
284 // for backward compatibility; we don't use DecimalFormatSymbols
285 // for currency data anymore.
286 UErrorCode internalStatus
= U_ZERO_ERROR
; // don't propagate failures out
288 UnicodeString tempStr
;
289 ucurr_forLocale(locStr
, curriso
, 4, &internalStatus
);
291 // Reuse numberElements[0] as a temporary buffer
292 uprv_getStaticCurrencyName(curriso
, locStr
, tempStr
, internalStatus
);
293 if (U_SUCCESS(internalStatus
)) {
294 fSymbols
[kIntlCurrencySymbol
] = curriso
;
295 fSymbols
[kCurrencySymbol
] = tempStr
;
297 /* else use the default values. */
299 U_LOCALE_BASED(locBased
, *this);
300 locBased
.setLocaleIDs(ures_getLocaleByType(numberElementsRes
,
301 ULOC_VALID_LOCALE
, &status
),
302 ures_getLocaleByType(numberElementsRes
,
303 ULOC_ACTUAL_LOCALE
, &status
));
305 //load the currency data
306 UChar ucc
[4]={0}; //Currency Codes are always 3 chars long
308 const char* locName
= loc
.getName();
309 UErrorCode localStatus
= U_ZERO_ERROR
;
310 uccLen
= ucurr_forLocale(locName
, ucc
, uccLen
, &localStatus
);
312 if(U_SUCCESS(localStatus
) && uccLen
> 0) {
314 u_UCharsToChars(ucc
, cc
, uccLen
);
315 /* An explicit currency was requested */
316 UResourceBundle
*currencyResource
= ures_open(U_ICUDATA_CURR
, locStr
, &localStatus
);
317 UResourceBundle
*currency
= ures_getByKeyWithFallback(currencyResource
, "Currencies", NULL
, &localStatus
);
318 currency
= ures_getByKeyWithFallback(currency
, cc
, currency
, &localStatus
);
319 if(U_SUCCESS(localStatus
) && ures_getSize(currency
)>2) { // the length is 3 if more data is present
320 currency
= ures_getByIndex(currency
, 2, currency
, &localStatus
);
321 int32_t currPatternLen
= 0;
322 currPattern
= ures_getStringByIndex(currency
, (int32_t)0, &currPatternLen
, &localStatus
);
323 UnicodeString decimalSep
= ures_getStringByIndex(currency
, (int32_t)1, NULL
, &localStatus
);
324 UnicodeString groupingSep
= ures_getStringByIndex(currency
, (int32_t)2, NULL
, &localStatus
);
325 if(U_SUCCESS(localStatus
)){
326 fSymbols
[kMonetaryGroupingSeparatorSymbol
] = groupingSep
;
327 fSymbols
[kMonetarySeparatorSymbol
] = decimalSep
;
328 //pattern.setTo(TRUE, currPattern, currPatternLen);
329 status
= localStatus
;
332 ures_close(currency
);
333 ures_close(currencyResource
);
334 /* else An explicit currency was requested and is unknown or locale data is malformed. */
335 /* ucurr_* API will get the correct value later on. */
337 // else ignore the error if no currency
340 localStatus
= U_ZERO_ERROR
;
341 UResourceBundle
*currencyResource
= ures_open(U_ICUDATA_CURR
, locStr
, &localStatus
);
342 UResourceBundle
*currencySpcRes
= ures_getByKeyWithFallback(currencyResource
,
343 gCurrencySpacingTag
, NULL
, &localStatus
);
345 if (localStatus
== U_USING_FALLBACK_WARNING
|| U_SUCCESS(localStatus
)) {
346 const char* keywords
[kCurrencySpacingCount
] = {
347 gCurrencyMatchTag
, gCurrencySudMatchTag
, gCurrencyInsertBtnTag
349 localStatus
= U_ZERO_ERROR
;
350 UResourceBundle
*dataRes
= ures_getByKeyWithFallback(currencySpcRes
,
351 gBeforeCurrencyTag
, NULL
, &localStatus
);
352 if (localStatus
== U_USING_FALLBACK_WARNING
|| U_SUCCESS(localStatus
)) {
353 localStatus
= U_ZERO_ERROR
;
354 for (int32_t i
= 0; i
< kCurrencySpacingCount
; i
++) {
355 currencySpcBeforeSym
[i
] = ures_getStringByKey(dataRes
, keywords
[i
],
360 dataRes
= ures_getByKeyWithFallback(currencySpcRes
,
361 gAfterCurrencyTag
, NULL
, &localStatus
);
362 if (localStatus
== U_USING_FALLBACK_WARNING
|| U_SUCCESS(localStatus
)) {
363 localStatus
= U_ZERO_ERROR
;
364 for (int32_t i
= 0; i
< kCurrencySpacingCount
; i
++) {
365 currencySpcAfterSym
[i
] = ures_getStringByKey(dataRes
, keywords
[i
],
370 ures_close(currencySpcRes
);
371 ures_close(currencyResource
);
374 ures_close(resource
);
375 ures_close(numberElementsRes
);
380 DecimalFormatSymbols::initialize() {
382 * These strings used to be in static arrays, but the HP/UX aCC compiler
383 * cannot initialize a static array with class constructors.
386 fSymbols
[kDecimalSeparatorSymbol
] = (UChar
)0x2e; // '.' decimal separator
387 fSymbols
[kGroupingSeparatorSymbol
].remove(); // group (thousands) separator
388 fSymbols
[kPatternSeparatorSymbol
] = (UChar
)0x3b; // ';' pattern separator
389 fSymbols
[kPercentSymbol
] = (UChar
)0x25; // '%' percent sign
390 fSymbols
[kZeroDigitSymbol
] = (UChar
)0x30; // '0' native 0 digit
391 fSymbols
[kOneDigitSymbol
] = (UChar
)0x31; // '1' native 1 digit
392 fSymbols
[kTwoDigitSymbol
] = (UChar
)0x32; // '2' native 2 digit
393 fSymbols
[kThreeDigitSymbol
] = (UChar
)0x33; // '3' native 3 digit
394 fSymbols
[kFourDigitSymbol
] = (UChar
)0x34; // '4' native 4 digit
395 fSymbols
[kFiveDigitSymbol
] = (UChar
)0x35; // '5' native 5 digit
396 fSymbols
[kSixDigitSymbol
] = (UChar
)0x36; // '6' native 6 digit
397 fSymbols
[kSevenDigitSymbol
] = (UChar
)0x37; // '7' native 7 digit
398 fSymbols
[kEightDigitSymbol
] = (UChar
)0x38; // '8' native 8 digit
399 fSymbols
[kNineDigitSymbol
] = (UChar
)0x39; // '9' native 9 digit
400 fSymbols
[kDigitSymbol
] = (UChar
)0x23; // '#' pattern digit
401 fSymbols
[kPlusSignSymbol
] = (UChar
)0x002b; // '+' plus sign
402 fSymbols
[kMinusSignSymbol
] = (UChar
)0x2d; // '-' minus sign
403 fSymbols
[kCurrencySymbol
] = (UChar
)0xa4; // 'OX' currency symbol
404 fSymbols
[kIntlCurrencySymbol
] = INTL_CURRENCY_SYMBOL_STR
;
405 fSymbols
[kMonetarySeparatorSymbol
] = (UChar
)0x2e; // '.' monetary decimal separator
406 fSymbols
[kExponentialSymbol
] = (UChar
)0x45; // 'E' exponential
407 fSymbols
[kPerMillSymbol
] = (UChar
)0x2030; // '%o' per mill
408 fSymbols
[kPadEscapeSymbol
] = (UChar
)0x2a; // '*' pad escape symbol
409 fSymbols
[kInfinitySymbol
] = (UChar
)0x221e; // 'oo' infinite
410 fSymbols
[kNaNSymbol
] = (UChar
)0xfffd; // SUB NaN
411 fSymbols
[kSignificantDigitSymbol
] = (UChar
)0x0040; // '@' significant digit
412 fSymbols
[kMonetaryGroupingSeparatorSymbol
].remove(); //
416 DecimalFormatSymbols::getLocale(ULocDataLocaleType type
, UErrorCode
& status
) const {
417 U_LOCALE_BASED(locBased
, *this);
418 return locBased
.getLocale(type
, status
);
422 DecimalFormatSymbols::getPatternForCurrencySpacing(ECurrencySpacing type
,
423 UBool beforeCurrency
,
424 UErrorCode
& status
) const {
425 if (U_FAILURE(status
)) {
426 return fNoSymbol
; // always empty.
428 if (beforeCurrency
) {
429 return currencySpcBeforeSym
[(int32_t)type
];
431 return currencySpcAfterSym
[(int32_t)type
];
436 DecimalFormatSymbols::setPatternForCurrencySpacing(ECurrencySpacing type
,
437 UBool beforeCurrency
,
438 const UnicodeString
& pattern
) {
439 if (beforeCurrency
) {
440 currencySpcBeforeSym
[(int32_t)type
] = pattern
;
442 currencySpcAfterSym
[(int32_t)type
] = pattern
;
447 #endif /* #if !UCONFIG_NO_FORMATTING */