2 *******************************************************************************
3 * Copyright (C) 1997-2014, 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
);
133 // -------------------------------------
136 DecimalFormatSymbols::operator==(const DecimalFormatSymbols
& that
) const
141 for(int32_t i
= 0; i
< (int32_t)kFormatSymbolCount
; ++i
) {
142 if(fSymbols
[(ENumberFormatSymbol
)i
] != that
.fSymbols
[(ENumberFormatSymbol
)i
]) {
146 for(int32_t i
= 0; i
< (int32_t)UNUM_CURRENCY_SPACING_COUNT
; ++i
) {
147 if(currencySpcBeforeSym
[i
] != that
.currencySpcBeforeSym
[i
]) {
150 if(currencySpcAfterSym
[i
] != that
.currencySpcAfterSym
[i
]) {
154 return locale
== that
.locale
&&
155 uprv_strcmp(validLocale
, that
.validLocale
) == 0 &&
156 uprv_strcmp(actualLocale
, that
.actualLocale
) == 0;
159 // -------------------------------------
162 DecimalFormatSymbols::initialize(const Locale
& loc
, UErrorCode
& status
, UBool useLastResortData
)
164 static const char *gNumberElementKeys
[kFormatSymbolCount
] = {
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 */
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 */
178 NULL
, /* Escape padding character - not in CLDR */
181 NULL
, /* Significant digit symbol - not in CLDR */
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 "superscriptingExponent", /* Multiplication (x) symbol for exponents */
195 static const char *gLatn
= "latn";
196 static const char *gSymbols
= "symbols";
198 const UChar
*sym
= NULL
;
201 *validLocale
= *actualLocale
= 0;
203 if (U_FAILURE(status
))
206 const char* locStr
= loc
.getName();
207 LocalUResourceBundlePointer
resource(ures_open(NULL
, locStr
, &status
));
208 LocalUResourceBundlePointer
numberElementsRes(
209 ures_getByKeyWithFallback(resource
.getAlias(), gNumberElements
, NULL
, &status
));
211 if (U_FAILURE(status
)) {
212 if ( useLastResortData
) {
213 status
= U_USING_DEFAULT_WARNING
;
219 // First initialize all the symbols to the fallbacks for anything we can't find
223 // Next get the numbering system for this locale and set zero digit
224 // and the digit string based on the numbering system for the locale
227 LocalPointer
<NumberingSystem
> ns(NumberingSystem::createInstance(loc
, status
));
228 if (U_SUCCESS(status
) && ns
->getRadix() == 10 && !ns
->isAlgorithmic()) {
229 nsName
= ns
->getName();
230 UnicodeString
digitString(ns
->getDescription());
231 int32_t digitIndex
= 0;
232 UChar32 digit
= digitString
.char32At(0);
233 fSymbols
[kZeroDigitSymbol
].setTo(digit
);
234 for (int32_t i
= kOneDigitSymbol
; i
<= kNineDigitSymbol
; ++i
) {
235 digitIndex
+= U16_LENGTH(digit
);
236 digit
= digitString
.char32At(digitIndex
);
237 fSymbols
[i
].setTo(digit
);
243 UBool isLatn
= !uprv_strcmp(nsName
,gLatn
);
245 UErrorCode nlStatus
= U_ZERO_ERROR
;
246 LocalUResourceBundlePointer nonLatnSymbols
;
248 nonLatnSymbols
.adoptInstead(
249 ures_getByKeyWithFallback(numberElementsRes
.getAlias(), nsName
, NULL
, &nlStatus
));
250 ures_getByKeyWithFallback(nonLatnSymbols
.getAlias(), gSymbols
, nonLatnSymbols
.getAlias(), &nlStatus
);
253 LocalUResourceBundlePointer
latnSymbols(
254 ures_getByKeyWithFallback(numberElementsRes
.getAlias(), gLatn
, NULL
, &status
));
255 ures_getByKeyWithFallback(latnSymbols
.getAlias(), gSymbols
, latnSymbols
.getAlias(), &status
);
257 UBool kMonetaryDecimalSet
= FALSE
;
258 UBool kMonetaryGroupingSet
= FALSE
;
259 for(int32_t i
= 0; i
<kFormatSymbolCount
; i
++) {
260 if ( gNumberElementKeys
[i
] != NULL
) {
261 UErrorCode localStatus
= U_ZERO_ERROR
;
263 sym
= ures_getStringByKeyWithFallback(nonLatnSymbols
.getAlias(),
264 gNumberElementKeys
[i
], &len
, &localStatus
);
265 // If we can't find the symbol in the numbering system specific resources,
266 // use the "latn" numbering system as the fallback.
267 if ( U_FAILURE(localStatus
) ) {
268 localStatus
= U_ZERO_ERROR
;
269 sym
= ures_getStringByKeyWithFallback(latnSymbols
.getAlias(),
270 gNumberElementKeys
[i
], &len
, &localStatus
);
273 sym
= ures_getStringByKeyWithFallback(latnSymbols
.getAlias(),
274 gNumberElementKeys
[i
], &len
, &localStatus
);
277 if ( U_SUCCESS(localStatus
) ) {
278 setSymbol((ENumberFormatSymbol
)i
, UnicodeString(TRUE
, sym
, len
));
279 if ( i
== kMonetarySeparatorSymbol
) {
280 kMonetaryDecimalSet
= TRUE
;
281 } else if ( i
== kMonetaryGroupingSeparatorSymbol
) {
282 kMonetaryGroupingSet
= TRUE
;
288 // If monetary decimal or grouping were not explicitly set, then set them to be the
289 // same as their non-monetary counterparts.
291 if ( !kMonetaryDecimalSet
) {
292 setSymbol(kMonetarySeparatorSymbol
,fSymbols
[kDecimalSeparatorSymbol
]);
294 if ( !kMonetaryGroupingSet
) {
295 setSymbol(kMonetaryGroupingSeparatorSymbol
,fSymbols
[kGroupingSeparatorSymbol
]);
298 // Obtain currency data from the currency API. This is strictly
299 // for backward compatibility; we don't use DecimalFormatSymbols
300 // for currency data anymore.
301 UErrorCode internalStatus
= U_ZERO_ERROR
; // don't propagate failures out
303 UnicodeString tempStr
;
304 ucurr_forLocale(locStr
, curriso
, 4, &internalStatus
);
306 uprv_getStaticCurrencyName(curriso
, locStr
, tempStr
, internalStatus
);
307 if (U_SUCCESS(internalStatus
)) {
308 fSymbols
[kIntlCurrencySymbol
].setTo(curriso
, -1);
309 fSymbols
[kCurrencySymbol
] = tempStr
;
311 /* else use the default values. */
313 U_LOCALE_BASED(locBased
, *this);
314 locBased
.setLocaleIDs(ures_getLocaleByType(numberElementsRes
.getAlias(),
315 ULOC_VALID_LOCALE
, &status
),
316 ures_getLocaleByType(numberElementsRes
.getAlias(),
317 ULOC_ACTUAL_LOCALE
, &status
));
319 //load the currency data
320 UChar ucc
[4]={0}; //Currency Codes are always 3 chars long
322 const char* locName
= loc
.getName();
323 UErrorCode localStatus
= U_ZERO_ERROR
;
324 uccLen
= ucurr_forLocale(locName
, ucc
, uccLen
, &localStatus
);
326 if(U_SUCCESS(localStatus
) && uccLen
> 0) {
328 u_UCharsToChars(ucc
, cc
, uccLen
);
329 /* An explicit currency was requested */
330 LocalUResourceBundlePointer
currencyResource(ures_open(U_ICUDATA_CURR
, locStr
, &localStatus
));
331 LocalUResourceBundlePointer
currency(
332 ures_getByKeyWithFallback(currencyResource
.getAlias(), "Currencies", NULL
, &localStatus
));
333 ures_getByKeyWithFallback(currency
.getAlias(), cc
, currency
.getAlias(), &localStatus
);
334 if(U_SUCCESS(localStatus
) && ures_getSize(currency
.getAlias())>2) { // the length is 3 if more data is present
335 ures_getByIndex(currency
.getAlias(), 2, currency
.getAlias(), &localStatus
);
336 int32_t currPatternLen
= 0;
338 ures_getStringByIndex(currency
.getAlias(), (int32_t)0, &currPatternLen
, &localStatus
);
339 UnicodeString decimalSep
=
340 ures_getUnicodeStringByIndex(currency
.getAlias(), (int32_t)1, &localStatus
);
341 UnicodeString groupingSep
=
342 ures_getUnicodeStringByIndex(currency
.getAlias(), (int32_t)2, &localStatus
);
343 if(U_SUCCESS(localStatus
)){
344 fSymbols
[kMonetaryGroupingSeparatorSymbol
] = groupingSep
;
345 fSymbols
[kMonetarySeparatorSymbol
] = decimalSep
;
346 //pattern.setTo(TRUE, currPattern, currPatternLen);
347 status
= localStatus
;
350 /* else An explicit currency was requested and is unknown or locale data is malformed. */
351 /* ucurr_* API will get the correct value later on. */
353 // else ignore the error if no currency
356 localStatus
= U_ZERO_ERROR
;
357 LocalUResourceBundlePointer
currencyResource(ures_open(U_ICUDATA_CURR
, locStr
, &localStatus
));
358 LocalUResourceBundlePointer
currencySpcRes(
359 ures_getByKeyWithFallback(currencyResource
.getAlias(),
360 gCurrencySpacingTag
, NULL
, &localStatus
));
362 if (localStatus
== U_USING_FALLBACK_WARNING
|| U_SUCCESS(localStatus
)) {
363 const char* keywords
[UNUM_CURRENCY_SPACING_COUNT
] = {
364 gCurrencyMatchTag
, gCurrencySudMatchTag
, gCurrencyInsertBtnTag
366 localStatus
= U_ZERO_ERROR
;
367 LocalUResourceBundlePointer
dataRes(
368 ures_getByKeyWithFallback(currencySpcRes
.getAlias(),
369 gBeforeCurrencyTag
, NULL
, &localStatus
));
370 if (localStatus
== U_USING_FALLBACK_WARNING
|| U_SUCCESS(localStatus
)) {
371 localStatus
= U_ZERO_ERROR
;
372 for (int32_t i
= 0; i
< UNUM_CURRENCY_SPACING_COUNT
; i
++) {
373 currencySpcBeforeSym
[i
] =
374 ures_getUnicodeStringByKey(dataRes
.getAlias(), keywords
[i
], &localStatus
);
377 dataRes
.adoptInstead(
378 ures_getByKeyWithFallback(currencySpcRes
.getAlias(),
379 gAfterCurrencyTag
, NULL
, &localStatus
));
380 if (localStatus
== U_USING_FALLBACK_WARNING
|| U_SUCCESS(localStatus
)) {
381 localStatus
= U_ZERO_ERROR
;
382 for (int32_t i
= 0; i
< UNUM_CURRENCY_SPACING_COUNT
; i
++) {
383 currencySpcAfterSym
[i
] =
384 ures_getUnicodeStringByKey(dataRes
.getAlias(), keywords
[i
], &localStatus
);
391 DecimalFormatSymbols::initialize() {
393 * These strings used to be in static arrays, but the HP/UX aCC compiler
394 * cannot initialize a static array with class constructors.
397 fSymbols
[kDecimalSeparatorSymbol
] = (UChar
)0x2e; // '.' decimal separator
398 fSymbols
[kGroupingSeparatorSymbol
].remove(); // group (thousands) separator
399 fSymbols
[kPatternSeparatorSymbol
] = (UChar
)0x3b; // ';' pattern separator
400 fSymbols
[kPercentSymbol
] = (UChar
)0x25; // '%' percent sign
401 fSymbols
[kZeroDigitSymbol
] = (UChar
)0x30; // '0' native 0 digit
402 fSymbols
[kOneDigitSymbol
] = (UChar
)0x31; // '1' native 1 digit
403 fSymbols
[kTwoDigitSymbol
] = (UChar
)0x32; // '2' native 2 digit
404 fSymbols
[kThreeDigitSymbol
] = (UChar
)0x33; // '3' native 3 digit
405 fSymbols
[kFourDigitSymbol
] = (UChar
)0x34; // '4' native 4 digit
406 fSymbols
[kFiveDigitSymbol
] = (UChar
)0x35; // '5' native 5 digit
407 fSymbols
[kSixDigitSymbol
] = (UChar
)0x36; // '6' native 6 digit
408 fSymbols
[kSevenDigitSymbol
] = (UChar
)0x37; // '7' native 7 digit
409 fSymbols
[kEightDigitSymbol
] = (UChar
)0x38; // '8' native 8 digit
410 fSymbols
[kNineDigitSymbol
] = (UChar
)0x39; // '9' native 9 digit
411 fSymbols
[kDigitSymbol
] = (UChar
)0x23; // '#' pattern digit
412 fSymbols
[kPlusSignSymbol
] = (UChar
)0x002b; // '+' plus sign
413 fSymbols
[kMinusSignSymbol
] = (UChar
)0x2d; // '-' minus sign
414 fSymbols
[kCurrencySymbol
] = (UChar
)0xa4; // 'OX' currency symbol
415 fSymbols
[kIntlCurrencySymbol
].setTo(TRUE
, INTL_CURRENCY_SYMBOL_STR
, 2);
416 fSymbols
[kMonetarySeparatorSymbol
] = (UChar
)0x2e; // '.' monetary decimal separator
417 fSymbols
[kExponentialSymbol
] = (UChar
)0x45; // 'E' exponential
418 fSymbols
[kPerMillSymbol
] = (UChar
)0x2030; // '%o' per mill
419 fSymbols
[kPadEscapeSymbol
] = (UChar
)0x2a; // '*' pad escape symbol
420 fSymbols
[kInfinitySymbol
] = (UChar
)0x221e; // 'oo' infinite
421 fSymbols
[kNaNSymbol
] = (UChar
)0xfffd; // SUB NaN
422 fSymbols
[kSignificantDigitSymbol
] = (UChar
)0x0040; // '@' significant digit
423 fSymbols
[kMonetaryGroupingSeparatorSymbol
].remove(); //
424 fSymbols
[kExponentMultiplicationSymbol
] = (UChar
)0xd7; // 'x' multiplication symbol for exponents
428 DecimalFormatSymbols::getLocale(ULocDataLocaleType type
, UErrorCode
& status
) const {
429 U_LOCALE_BASED(locBased
, *this);
430 return locBased
.getLocale(type
, status
);
434 DecimalFormatSymbols::getPatternForCurrencySpacing(UCurrencySpacing type
,
435 UBool beforeCurrency
,
436 UErrorCode
& status
) const {
437 if (U_FAILURE(status
)) {
438 return fNoSymbol
; // always empty.
440 if (beforeCurrency
) {
441 return currencySpcBeforeSym
[(int32_t)type
];
443 return currencySpcAfterSym
[(int32_t)type
];
448 DecimalFormatSymbols::setPatternForCurrencySpacing(UCurrencySpacing type
,
449 UBool beforeCurrency
,
450 const UnicodeString
& pattern
) {
451 if (beforeCurrency
) {
452 currencySpcBeforeSym
[(int32_t)type
] = pattern
;
454 currencySpcAfterSym
[(int32_t)type
] = pattern
;
459 #endif /* #if !UCONFIG_NO_FORMATTING */