2 *******************************************************************************
3 * Copyright (C) 1997-2013, 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 */
194 static const char *gLatn
= "latn";
195 static const char *gSymbols
= "symbols";
197 const UChar
*sym
= NULL
;
200 *validLocale
= *actualLocale
= 0;
202 if (U_FAILURE(status
))
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
));
210 if (U_FAILURE(status
)) {
211 if ( useLastResortData
) {
212 status
= U_USING_DEFAULT_WARNING
;
218 // First initialize all the symbols to the fallbacks for anything we can't find
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
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
);
242 UBool isLatn
= !uprv_strcmp(nsName
,gLatn
);
244 UErrorCode nlStatus
= U_ZERO_ERROR
;
245 LocalUResourceBundlePointer nonLatnSymbols
;
247 nonLatnSymbols
.adoptInstead(
248 ures_getByKeyWithFallback(numberElementsRes
.getAlias(), nsName
, NULL
, &nlStatus
));
249 ures_getByKeyWithFallback(nonLatnSymbols
.getAlias(), gSymbols
, nonLatnSymbols
.getAlias(), &nlStatus
);
252 LocalUResourceBundlePointer
latnSymbols(
253 ures_getByKeyWithFallback(numberElementsRes
.getAlias(), gLatn
, NULL
, &status
));
254 ures_getByKeyWithFallback(latnSymbols
.getAlias(), gSymbols
, latnSymbols
.getAlias(), &status
);
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
;
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
);
272 sym
= ures_getStringByKeyWithFallback(latnSymbols
.getAlias(),
273 gNumberElementKeys
[i
], &len
, &localStatus
);
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
;
287 // If monetary decimal or grouping were not explicitly set, then set them to be the
288 // same as their non-monetary counterparts.
290 if ( !kMonetaryDecimalSet
) {
291 setSymbol(kMonetarySeparatorSymbol
,fSymbols
[kDecimalSeparatorSymbol
]);
293 if ( !kMonetaryGroupingSet
) {
294 setSymbol(kMonetaryGroupingSeparatorSymbol
,fSymbols
[kGroupingSeparatorSymbol
]);
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
302 UnicodeString tempStr
;
303 ucurr_forLocale(locStr
, curriso
, 4, &internalStatus
);
305 uprv_getStaticCurrencyName(curriso
, locStr
, tempStr
, internalStatus
);
306 if (U_SUCCESS(internalStatus
)) {
307 fSymbols
[kIntlCurrencySymbol
].setTo(curriso
, -1);
308 fSymbols
[kCurrencySymbol
] = tempStr
;
310 /* else use the default values. */
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
));
318 //load the currency data
319 UChar ucc
[4]={0}; //Currency Codes are always 3 chars long
321 const char* locName
= loc
.getName();
322 UErrorCode localStatus
= U_ZERO_ERROR
;
323 uccLen
= ucurr_forLocale(locName
, ucc
, uccLen
, &localStatus
);
325 if(U_SUCCESS(localStatus
) && uccLen
> 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;
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
;
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. */
352 // else ignore the error if no currency
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
));
361 if (localStatus
== U_USING_FALLBACK_WARNING
|| U_SUCCESS(localStatus
)) {
362 const char* keywords
[UNUM_CURRENCY_SPACING_COUNT
] = {
363 gCurrencyMatchTag
, gCurrencySudMatchTag
, gCurrencyInsertBtnTag
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
);
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
);
390 DecimalFormatSymbols::initialize() {
392 * These strings used to be in static arrays, but the HP/UX aCC compiler
393 * cannot initialize a static array with class constructors.
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(); //
426 DecimalFormatSymbols::getLocale(ULocDataLocaleType type
, UErrorCode
& status
) const {
427 U_LOCALE_BASED(locBased
, *this);
428 return locBased
.getLocale(type
, status
);
432 DecimalFormatSymbols::getPatternForCurrencySpacing(UCurrencySpacing type
,
433 UBool beforeCurrency
,
434 UErrorCode
& status
) const {
435 if (U_FAILURE(status
)) {
436 return fNoSymbol
; // always empty.
438 if (beforeCurrency
) {
439 return currencySpcBeforeSym
[(int32_t)type
];
441 return currencySpcAfterSym
[(int32_t)type
];
446 DecimalFormatSymbols::setPatternForCurrencySpacing(UCurrencySpacing type
,
447 UBool beforeCurrency
,
448 const UnicodeString
& pattern
) {
449 if (beforeCurrency
) {
450 currencySpcBeforeSym
[(int32_t)type
] = pattern
;
452 currencySpcAfterSym
[(int32_t)type
] = pattern
;
457 #endif /* #if !UCONFIG_NO_FORMATTING */