• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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