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 "ucurrimp.h"
32#include "cstring.h"
33#include "locbased.h"
34#include "uresimp.h"
35#include "ureslocs.h"
36
37// *****************************************************************************
38// class DecimalFormatSymbols
39// *****************************************************************************
40
41U_NAMESPACE_BEGIN
42
43UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormatSymbols)
44
45static const char gNumberElements[] = "NumberElements";
46static const char gCurrencySpacingTag[] = "currencySpacing";
47static const char gBeforeCurrencyTag[] = "beforeCurrency";
48static const char gAfterCurrencyTag[] = "afterCurrency";
49static const char gCurrencyMatchTag[] = "currencyMatch";
50static const char gCurrencySudMatchTag[] = "surroundingMatch";
51static const char gCurrencyInsertBtnTag[] = "insertBetween";
52
53
54static const UChar INTL_CURRENCY_SYMBOL_STR[] = {0xa4, 0xa4, 0};
55
56// -------------------------------------
57// Initializes this with the decimal format symbols in the default locale.
58
59DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status)
60    : UObject(),
61    locale()
62{
63    initialize(locale, status, TRUE);
64}
65
66// -------------------------------------
67// Initializes this with the decimal format symbols in the desired locale.
68
69DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status)
70    : UObject(),
71    locale(loc)
72{
73    initialize(locale, status);
74}
75
76// -------------------------------------
77
78DecimalFormatSymbols::~DecimalFormatSymbols()
79{
80}
81
82// -------------------------------------
83// copy constructor
84
85DecimalFormatSymbols::DecimalFormatSymbols(const DecimalFormatSymbols &source)
86    : UObject(source)
87{
88    *this = source;
89}
90
91// -------------------------------------
92// assignment operator
93
94DecimalFormatSymbols&
95DecimalFormatSymbols::operator=(const DecimalFormatSymbols& rhs)
96{
97    if (this != &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]);
101        }
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]);
105        }
106        locale = rhs.locale;
107        uprv_strcpy(validLocale, rhs.validLocale);
108        uprv_strcpy(actualLocale, rhs.actualLocale);
109    }
110    return *this;
111}
112
113// -------------------------------------
114
115UBool
116DecimalFormatSymbols::operator==(const DecimalFormatSymbols& that) const
117{
118    if (this == &that) {
119        return TRUE;
120    }
121    for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) {
122        if(fSymbols[(ENumberFormatSymbol)i] != that.fSymbols[(ENumberFormatSymbol)i]) {
123            return FALSE;
124        }
125    }
126    for(int32_t i = 0; i < (int32_t)kCurrencySpacingCount; ++i) {
127        if(currencySpcBeforeSym[i] != that.currencySpcBeforeSym[i]) {
128            return FALSE;
129        }
130        if(currencySpcAfterSym[i] != that.currencySpcAfterSym[i]) {
131            return FALSE;
132        }
133    }
134    return locale == that.locale &&
135        uprv_strcmp(validLocale, that.validLocale) == 0 &&
136        uprv_strcmp(actualLocale, that.actualLocale) == 0;
137}
138
139// -------------------------------------
140
141void
142DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool useLastResortData)
143{
144    static const char *gNumberElementKeys[kFormatSymbolCount] = {
145        "decimal",
146        "group",
147        "list",
148        "percentSign",
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 */
151        "minusSign",
152        "plusSign",
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 */
155        "currencyDecimal",
156        "exponential",
157        "perMille",
158        NULL, /* Escape padding character - not in CLDR */
159        "infinity",
160        "nan",
161        NULL, /* Significant digit symbol - not in CLDR */
162        "currencyGroup",
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 */
172    };
173
174    static const char *gLatn =  "latn";
175    static const char *gSymbols = "symbols";
176    const char *nsName;
177    const UChar *sym = NULL;
178    int32_t len = 0;
179
180    *validLocale = *actualLocale = 0;
181    currPattern = NULL;
182    if (U_FAILURE(status))
183        return;
184
185    const char* locStr = loc.getName();
186    UResourceBundle *resource = ures_open((char *)0, locStr, &status);
187    UResourceBundle *numberElementsRes = ures_getByKeyWithFallback(resource, gNumberElements, NULL, &status);
188
189    if (U_FAILURE(status)) {
190        if ( useLastResortData ) {
191            status = U_USING_FALLBACK_WARNING;
192            initialize();
193        }
194        return;
195    } else {
196
197        // First initialize all the symbols to the fallbacks for anything we can't find
198        initialize();
199
200        //
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
203        //
204
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);
219            delete DigitString;
220        } else {
221            nsName = gLatn;
222        }
223
224        UBool isLatn = !uprv_strcmp(nsName,gLatn);
225
226        UErrorCode nlStatus = U_ZERO_ERROR;
227        UResourceBundle *nonLatnSymbols = NULL;
228        if ( !isLatn ) {
229            nonLatnSymbols = ures_getByKeyWithFallback(numberElementsRes, nsName, NULL, &nlStatus);
230            nonLatnSymbols = ures_getByKeyWithFallback(nonLatnSymbols, gSymbols, nonLatnSymbols, &nlStatus);
231        }
232
233        UResourceBundle *latnSymbols = ures_getByKeyWithFallback(numberElementsRes, gLatn, NULL, &status);
234        latnSymbols = ures_getByKeyWithFallback(latnSymbols, gSymbols, latnSymbols, &status);
235
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;
241                if ( !isLatn ) {
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);
248                    }
249                } else {
250                        sym = ures_getStringByKeyWithFallback(latnSymbols,gNumberElementKeys[i],&len,&localStatus);
251                }
252
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;
259                    }
260                }
261            }
262        }
263
264        ures_close(latnSymbols);
265        if ( !isLatn ) {
266            ures_close(nonLatnSymbols);
267        }
268
269        // If monetary decimal or grouping were not explicitly set, then set them to be the
270        // same as their non-monetary counterparts.
271
272        if ( !kMonetaryDecimalSet ) {
273            setSymbol(kMonetarySeparatorSymbol,fSymbols[kDecimalSeparatorSymbol]);
274        }
275        if ( !kMonetaryGroupingSet ) {
276            setSymbol(kMonetaryGroupingSeparatorSymbol,fSymbols[kGroupingSeparatorSymbol]);
277        }
278
279        if (ns) {
280            delete ns;
281        }
282
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
287        UChar curriso[4];
288        UnicodeString tempStr;
289        ucurr_forLocale(locStr, curriso, 4, &internalStatus);
290
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;
296        }
297        /* else use the default values. */
298
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));
304
305        //load the currency data
306        UChar ucc[4]={0}; //Currency Codes are always 3 chars long
307        int32_t uccLen = 4;
308        const char* locName = loc.getName();
309        UErrorCode localStatus = U_ZERO_ERROR;
310        uccLen = ucurr_forLocale(locName, ucc, uccLen, &localStatus);
311
312        if(U_SUCCESS(localStatus) && uccLen > 0) {
313            char cc[4]={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;
330                }
331            }
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. */
336        }
337            // else ignore the error if no currency
338
339        // Currency Spacing.
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);
344
345        if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) {
346            const char* keywords[kCurrencySpacingCount] = {
347                gCurrencyMatchTag, gCurrencySudMatchTag, gCurrencyInsertBtnTag
348            };
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],
356                                                            NULL, &localStatus);
357                }
358                ures_close(dataRes);
359            }
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],
366                                                                NULL, &localStatus);
367                }
368                ures_close(dataRes);
369            }
370            ures_close(currencySpcRes);
371            ures_close(currencyResource);
372        }
373    }
374    ures_close(resource);
375    ures_close(numberElementsRes);
376
377}
378
379void
380DecimalFormatSymbols::initialize() {
381    /*
382     * These strings used to be in static arrays, but the HP/UX aCC compiler
383     * cannot initialize a static array with class constructors.
384     *  markus 2000may25
385     */
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(); //
413}
414
415Locale
416DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
417    U_LOCALE_BASED(locBased, *this);
418    return locBased.getLocale(type, status);
419}
420
421const UnicodeString&
422DecimalFormatSymbols::getPatternForCurrencySpacing(ECurrencySpacing type,
423                                                 UBool beforeCurrency,
424                                                 UErrorCode& status) const {
425    if (U_FAILURE(status)) {
426      return fNoSymbol;  // always empty.
427    }
428    if (beforeCurrency) {
429      return currencySpcBeforeSym[(int32_t)type];
430    } else {
431      return currencySpcAfterSym[(int32_t)type];
432    }
433}
434
435void
436DecimalFormatSymbols::setPatternForCurrencySpacing(ECurrencySpacing type,
437                                                   UBool beforeCurrency,
438                                             const UnicodeString& pattern) {
439  if (beforeCurrency) {
440    currencySpcBeforeSym[(int32_t)type] = pattern;
441  } else {
442    currencySpcAfterSym[(int32_t)type] =  pattern;
443  }
444}
445U_NAMESPACE_END
446
447#endif /* #if !UCONFIG_NO_FORMATTING */
448
449//eof
450