2 *******************************************************************************
3 * Copyright (C) 1997-2015, 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"
31 #include "unicode/unum.h"
32 #include "unicode/utf16.h"
39 // *****************************************************************************
40 // class DecimalFormatSymbols
41 // *****************************************************************************
45 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormatSymbols
)
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";
56 static const UChar INTL_CURRENCY_SYMBOL_STR
[] = {0xa4, 0xa4, 0};
58 // -------------------------------------
59 // Initializes this with the decimal format symbols in the default locale.
61 DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode
& status
)
65 initialize(locale
, status
, TRUE
);
68 // -------------------------------------
69 // Initializes this with the decimal format symbols in the desired locale.
71 DecimalFormatSymbols::DecimalFormatSymbols(const Locale
& loc
, UErrorCode
& status
)
75 initialize(locale
, status
);
78 DecimalFormatSymbols::DecimalFormatSymbols()
80 locale(Locale::getRoot()),
82 *validLocale
= *actualLocale
= 0;
87 DecimalFormatSymbols::createWithLastResortData(UErrorCode
& status
) {
88 if (U_FAILURE(status
)) { return NULL
; }
89 DecimalFormatSymbols
* sym
= new DecimalFormatSymbols();
91 status
= U_MEMORY_ALLOCATION_ERROR
;
96 // -------------------------------------
98 DecimalFormatSymbols::~DecimalFormatSymbols()
102 // -------------------------------------
105 DecimalFormatSymbols::DecimalFormatSymbols(const DecimalFormatSymbols
&source
)
111 // -------------------------------------
112 // assignment operator
114 DecimalFormatSymbols
&
115 DecimalFormatSymbols::operator=(const DecimalFormatSymbols
& 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
]);
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
]);
127 uprv_strcpy(validLocale
, rhs
.validLocale
);
128 uprv_strcpy(actualLocale
, rhs
.actualLocale
);
129 fIsCustomCurrencySymbol
= rhs
.fIsCustomCurrencySymbol
;
130 fIsCustomIntlCurrencySymbol
= rhs
.fIsCustomIntlCurrencySymbol
;
135 // -------------------------------------
138 DecimalFormatSymbols::operator==(const DecimalFormatSymbols
& that
) const
143 if (fIsCustomCurrencySymbol
!= that
.fIsCustomCurrencySymbol
) {
146 if (fIsCustomIntlCurrencySymbol
!= that
.fIsCustomIntlCurrencySymbol
) {
149 for(int32_t i
= 0; i
< (int32_t)kFormatSymbolCount
; ++i
) {
150 if(fSymbols
[(ENumberFormatSymbol
)i
] != that
.fSymbols
[(ENumberFormatSymbol
)i
]) {
154 for(int32_t i
= 0; i
< (int32_t)UNUM_CURRENCY_SPACING_COUNT
; ++i
) {
155 if(currencySpcBeforeSym
[i
] != that
.currencySpcBeforeSym
[i
]) {
158 if(currencySpcAfterSym
[i
] != that
.currencySpcAfterSym
[i
]) {
162 return locale
== that
.locale
&&
163 uprv_strcmp(validLocale
, that
.validLocale
) == 0 &&
164 uprv_strcmp(actualLocale
, that
.actualLocale
) == 0;
167 // -------------------------------------
170 DecimalFormatSymbols::initialize(const Locale
& loc
, UErrorCode
& status
, UBool useLastResortData
)
172 static const char *gNumberElementKeys
[kFormatSymbolCount
] = {
177 NULL
, /* Native zero digit is deprecated from CLDR - get it from the numbering system */
178 NULL
, /* Pattern digit character is deprecated from CLDR - use # by default always */
181 NULL
, /* currency symbol - We don't really try to load this directly from CLDR until we know the currency */
182 NULL
, /* intl currency symbol - We don't really try to load this directly from CLDR until we know the currency */
186 NULL
, /* Escape padding character - not in CLDR */
189 NULL
, /* Significant digit symbol - not in CLDR */
191 NULL
, /* one digit - get it from the numbering system */
192 NULL
, /* two digit - get it from the numbering system */
193 NULL
, /* three digit - get it from the numbering system */
194 NULL
, /* four digit - get it from the numbering system */
195 NULL
, /* five digit - get it from the numbering system */
196 NULL
, /* six digit - get it from the numbering system */
197 NULL
, /* seven digit - get it from the numbering system */
198 NULL
, /* eight digit - get it from the numbering system */
199 NULL
, /* nine digit - get it from the numbering system */
200 "superscriptingExponent", /* Multiplication (x) symbol for exponents */
203 static const char *gLatn
= "latn";
204 static const char *gSymbols
= "symbols";
206 const UChar
*sym
= NULL
;
209 *validLocale
= *actualLocale
= 0;
211 if (U_FAILURE(status
))
214 const char* locStr
= loc
.getName();
215 LocalUResourceBundlePointer
resource(ures_open(NULL
, locStr
, &status
));
216 LocalUResourceBundlePointer
numberElementsRes(
217 ures_getByKeyWithFallback(resource
.getAlias(), gNumberElements
, NULL
, &status
));
219 if (U_FAILURE(status
)) {
220 if ( useLastResortData
) {
221 status
= U_USING_DEFAULT_WARNING
;
227 // First initialize all the symbols to the fallbacks for anything we can't find
231 // Next get the numbering system for this locale and set zero digit
232 // and the digit string based on the numbering system for the locale
235 LocalPointer
<NumberingSystem
> ns(NumberingSystem::createInstance(loc
, status
));
236 if (U_SUCCESS(status
) && ns
->getRadix() == 10 && !ns
->isAlgorithmic()) {
237 nsName
= ns
->getName();
238 UnicodeString
digitString(ns
->getDescription());
239 int32_t digitIndex
= 0;
240 UChar32 digit
= digitString
.char32At(0);
241 fSymbols
[kZeroDigitSymbol
].setTo(digit
);
242 for (int32_t i
= kOneDigitSymbol
; i
<= kNineDigitSymbol
; ++i
) {
243 digitIndex
+= U16_LENGTH(digit
);
244 digit
= digitString
.char32At(digitIndex
);
245 fSymbols
[i
].setTo(digit
);
251 UBool isLatn
= !uprv_strcmp(nsName
,gLatn
);
253 UErrorCode nlStatus
= U_ZERO_ERROR
;
254 LocalUResourceBundlePointer nonLatnSymbols
;
256 nonLatnSymbols
.adoptInstead(
257 ures_getByKeyWithFallback(numberElementsRes
.getAlias(), nsName
, NULL
, &nlStatus
));
258 ures_getByKeyWithFallback(nonLatnSymbols
.getAlias(), gSymbols
, nonLatnSymbols
.getAlias(), &nlStatus
);
261 LocalUResourceBundlePointer
latnSymbols(
262 ures_getByKeyWithFallback(numberElementsRes
.getAlias(), gLatn
, NULL
, &status
));
263 ures_getByKeyWithFallback(latnSymbols
.getAlias(), gSymbols
, latnSymbols
.getAlias(), &status
);
265 UBool kMonetaryDecimalSet
= FALSE
;
266 UBool kMonetaryGroupingSet
= FALSE
;
267 for(int32_t i
= 0; i
<kFormatSymbolCount
; i
++) {
268 if ( gNumberElementKeys
[i
] != NULL
) {
269 UErrorCode localStatus
= U_ZERO_ERROR
;
271 sym
= ures_getStringByKeyWithFallback(nonLatnSymbols
.getAlias(),
272 gNumberElementKeys
[i
], &len
, &localStatus
);
273 // If we can't find the symbol in the numbering system specific resources,
274 // use the "latn" numbering system as the fallback.
275 if ( U_FAILURE(localStatus
) ) {
276 localStatus
= U_ZERO_ERROR
;
277 sym
= ures_getStringByKeyWithFallback(latnSymbols
.getAlias(),
278 gNumberElementKeys
[i
], &len
, &localStatus
);
281 sym
= ures_getStringByKeyWithFallback(latnSymbols
.getAlias(),
282 gNumberElementKeys
[i
], &len
, &localStatus
);
285 if ( U_SUCCESS(localStatus
) ) {
286 setSymbol((ENumberFormatSymbol
)i
, UnicodeString(TRUE
, sym
, len
));
287 if ( i
== kMonetarySeparatorSymbol
) {
288 kMonetaryDecimalSet
= TRUE
;
289 } else if ( i
== kMonetaryGroupingSeparatorSymbol
) {
290 kMonetaryGroupingSet
= TRUE
;
296 // If monetary decimal or grouping were not explicitly set, then set them to be the
297 // same as their non-monetary counterparts.
299 if ( !kMonetaryDecimalSet
) {
300 setSymbol(kMonetarySeparatorSymbol
,fSymbols
[kDecimalSeparatorSymbol
]);
302 if ( !kMonetaryGroupingSet
) {
303 setSymbol(kMonetaryGroupingSeparatorSymbol
,fSymbols
[kGroupingSeparatorSymbol
]);
306 // Obtain currency data from the currency API. This is strictly
307 // for backward compatibility; we don't use DecimalFormatSymbols
308 // for currency data anymore.
309 UErrorCode internalStatus
= U_ZERO_ERROR
; // don't propagate failures out
311 UnicodeString tempStr
;
312 ucurr_forLocale(locStr
, curriso
, 4, &internalStatus
);
314 uprv_getStaticCurrencyName(curriso
, locStr
, tempStr
, internalStatus
);
315 if (U_SUCCESS(internalStatus
)) {
316 fSymbols
[kIntlCurrencySymbol
].setTo(curriso
, -1);
317 fSymbols
[kCurrencySymbol
] = tempStr
;
319 /* else use the default values. */
321 U_LOCALE_BASED(locBased
, *this);
322 locBased
.setLocaleIDs(ures_getLocaleByType(numberElementsRes
.getAlias(),
323 ULOC_VALID_LOCALE
, &status
),
324 ures_getLocaleByType(numberElementsRes
.getAlias(),
325 ULOC_ACTUAL_LOCALE
, &status
));
327 //load the currency data
328 UChar ucc
[4]={0}; //Currency Codes are always 3 chars long
330 const char* locName
= loc
.getName();
331 UErrorCode localStatus
= U_ZERO_ERROR
;
332 uccLen
= ucurr_forLocale(locName
, ucc
, uccLen
, &localStatus
);
334 if(U_SUCCESS(localStatus
) && uccLen
> 0) {
336 u_UCharsToChars(ucc
, cc
, uccLen
);
337 /* An explicit currency was requested */
338 LocalUResourceBundlePointer
currencyResource(ures_open(U_ICUDATA_CURR
, locStr
, &localStatus
));
339 LocalUResourceBundlePointer
currency(
340 ures_getByKeyWithFallback(currencyResource
.getAlias(), "Currencies", NULL
, &localStatus
));
341 ures_getByKeyWithFallback(currency
.getAlias(), cc
, currency
.getAlias(), &localStatus
);
342 if(U_SUCCESS(localStatus
) && ures_getSize(currency
.getAlias())>2) { // the length is 3 if more data is present
343 ures_getByIndex(currency
.getAlias(), 2, currency
.getAlias(), &localStatus
);
344 int32_t currPatternLen
= 0;
346 ures_getStringByIndex(currency
.getAlias(), (int32_t)0, &currPatternLen
, &localStatus
);
347 UnicodeString decimalSep
=
348 ures_getUnicodeStringByIndex(currency
.getAlias(), (int32_t)1, &localStatus
);
349 UnicodeString groupingSep
=
350 ures_getUnicodeStringByIndex(currency
.getAlias(), (int32_t)2, &localStatus
);
351 if(U_SUCCESS(localStatus
)){
352 fSymbols
[kMonetaryGroupingSeparatorSymbol
] = groupingSep
;
353 fSymbols
[kMonetarySeparatorSymbol
] = decimalSep
;
354 //pattern.setTo(TRUE, currPattern, currPatternLen);
355 status
= localStatus
;
358 /* else An explicit currency was requested and is unknown or locale data is malformed. */
359 /* ucurr_* API will get the correct value later on. */
361 // else ignore the error if no currency
364 localStatus
= U_ZERO_ERROR
;
365 LocalUResourceBundlePointer
currencyResource(ures_open(U_ICUDATA_CURR
, locStr
, &localStatus
));
366 LocalUResourceBundlePointer
currencySpcRes(
367 ures_getByKeyWithFallback(currencyResource
.getAlias(),
368 gCurrencySpacingTag
, NULL
, &localStatus
));
370 if (localStatus
== U_USING_FALLBACK_WARNING
|| U_SUCCESS(localStatus
)) {
371 const char* keywords
[UNUM_CURRENCY_SPACING_COUNT
] = {
372 gCurrencyMatchTag
, gCurrencySudMatchTag
, gCurrencyInsertBtnTag
374 localStatus
= U_ZERO_ERROR
;
375 LocalUResourceBundlePointer
dataRes(
376 ures_getByKeyWithFallback(currencySpcRes
.getAlias(),
377 gBeforeCurrencyTag
, NULL
, &localStatus
));
378 if (localStatus
== U_USING_FALLBACK_WARNING
|| U_SUCCESS(localStatus
)) {
379 localStatus
= U_ZERO_ERROR
;
380 for (int32_t i
= 0; i
< UNUM_CURRENCY_SPACING_COUNT
; i
++) {
381 currencySpcBeforeSym
[i
] =
382 ures_getUnicodeStringByKey(dataRes
.getAlias(), keywords
[i
], &localStatus
);
385 dataRes
.adoptInstead(
386 ures_getByKeyWithFallback(currencySpcRes
.getAlias(),
387 gAfterCurrencyTag
, NULL
, &localStatus
));
388 if (localStatus
== U_USING_FALLBACK_WARNING
|| U_SUCCESS(localStatus
)) {
389 localStatus
= U_ZERO_ERROR
;
390 for (int32_t i
= 0; i
< UNUM_CURRENCY_SPACING_COUNT
; i
++) {
391 currencySpcAfterSym
[i
] =
392 ures_getUnicodeStringByKey(dataRes
.getAlias(), keywords
[i
], &localStatus
);
399 DecimalFormatSymbols::initialize() {
401 * These strings used to be in static arrays, but the HP/UX aCC compiler
402 * cannot initialize a static array with class constructors.
405 fSymbols
[kDecimalSeparatorSymbol
] = (UChar
)0x2e; // '.' decimal separator
406 fSymbols
[kGroupingSeparatorSymbol
].remove(); // group (thousands) separator
407 fSymbols
[kPatternSeparatorSymbol
] = (UChar
)0x3b; // ';' pattern separator
408 fSymbols
[kPercentSymbol
] = (UChar
)0x25; // '%' percent sign
409 fSymbols
[kZeroDigitSymbol
] = (UChar
)0x30; // '0' native 0 digit
410 fSymbols
[kOneDigitSymbol
] = (UChar
)0x31; // '1' native 1 digit
411 fSymbols
[kTwoDigitSymbol
] = (UChar
)0x32; // '2' native 2 digit
412 fSymbols
[kThreeDigitSymbol
] = (UChar
)0x33; // '3' native 3 digit
413 fSymbols
[kFourDigitSymbol
] = (UChar
)0x34; // '4' native 4 digit
414 fSymbols
[kFiveDigitSymbol
] = (UChar
)0x35; // '5' native 5 digit
415 fSymbols
[kSixDigitSymbol
] = (UChar
)0x36; // '6' native 6 digit
416 fSymbols
[kSevenDigitSymbol
] = (UChar
)0x37; // '7' native 7 digit
417 fSymbols
[kEightDigitSymbol
] = (UChar
)0x38; // '8' native 8 digit
418 fSymbols
[kNineDigitSymbol
] = (UChar
)0x39; // '9' native 9 digit
419 fSymbols
[kDigitSymbol
] = (UChar
)0x23; // '#' pattern digit
420 fSymbols
[kPlusSignSymbol
] = (UChar
)0x002b; // '+' plus sign
421 fSymbols
[kMinusSignSymbol
] = (UChar
)0x2d; // '-' minus sign
422 fSymbols
[kCurrencySymbol
] = (UChar
)0xa4; // 'OX' currency symbol
423 fSymbols
[kIntlCurrencySymbol
].setTo(TRUE
, INTL_CURRENCY_SYMBOL_STR
, 2);
424 fSymbols
[kMonetarySeparatorSymbol
] = (UChar
)0x2e; // '.' monetary decimal separator
425 fSymbols
[kExponentialSymbol
] = (UChar
)0x45; // 'E' exponential
426 fSymbols
[kPerMillSymbol
] = (UChar
)0x2030; // '%o' per mill
427 fSymbols
[kPadEscapeSymbol
] = (UChar
)0x2a; // '*' pad escape symbol
428 fSymbols
[kInfinitySymbol
] = (UChar
)0x221e; // 'oo' infinite
429 fSymbols
[kNaNSymbol
] = (UChar
)0xfffd; // SUB NaN
430 fSymbols
[kSignificantDigitSymbol
] = (UChar
)0x0040; // '@' significant digit
431 fSymbols
[kMonetaryGroupingSeparatorSymbol
].remove(); //
432 fSymbols
[kExponentMultiplicationSymbol
] = (UChar
)0xd7; // 'x' multiplication symbol for exponents
433 fIsCustomCurrencySymbol
= FALSE
;
434 fIsCustomIntlCurrencySymbol
= FALSE
;
439 DecimalFormatSymbols::getLocale(ULocDataLocaleType type
, UErrorCode
& status
) const {
440 U_LOCALE_BASED(locBased
, *this);
441 return locBased
.getLocale(type
, status
);
445 DecimalFormatSymbols::getPatternForCurrencySpacing(UCurrencySpacing type
,
446 UBool beforeCurrency
,
447 UErrorCode
& status
) const {
448 if (U_FAILURE(status
)) {
449 return fNoSymbol
; // always empty.
451 if (beforeCurrency
) {
452 return currencySpcBeforeSym
[(int32_t)type
];
454 return currencySpcAfterSym
[(int32_t)type
];
459 DecimalFormatSymbols::setPatternForCurrencySpacing(UCurrencySpacing type
,
460 UBool beforeCurrency
,
461 const UnicodeString
& pattern
) {
462 if (beforeCurrency
) {
463 currencySpcBeforeSym
[(int32_t)type
] = pattern
;
465 currencySpcAfterSym
[(int32_t)type
] = pattern
;
470 #endif /* #if !UCONFIG_NO_FORMATTING */