1/*
2*******************************************************************************
3* Copyright (C) 1997-2015, 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 "unicode/utf16.h"
33#include "ucurrimp.h"
34#include "cstring.h"
35#include "locbased.h"
36#include "uresimp.h"
37#include "ureslocs.h"
38
39// *****************************************************************************
40// class DecimalFormatSymbols
41// *****************************************************************************
42
43U_NAMESPACE_BEGIN
44
45UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormatSymbols)
46
47static const char gNumberElements[] = "NumberElements";
48static const char gCurrencySpacingTag[] = "currencySpacing";
49static const char gBeforeCurrencyTag[] = "beforeCurrency";
50static const char gAfterCurrencyTag[] = "afterCurrency";
51static const char gCurrencyMatchTag[] = "currencyMatch";
52static const char gCurrencySudMatchTag[] = "surroundingMatch";
53static const char gCurrencyInsertBtnTag[] = "insertBetween";
54
55
56static const UChar INTL_CURRENCY_SYMBOL_STR[] = {0xa4, 0xa4, 0};
57
58// -------------------------------------
59// Initializes this with the decimal format symbols in the default locale.
60
61DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status)
62    : UObject(),
63    locale()
64{
65    initialize(locale, status, TRUE);
66}
67
68// -------------------------------------
69// Initializes this with the decimal format symbols in the desired locale.
70
71DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status)
72    : UObject(),
73    locale(loc)
74{
75    initialize(locale, status);
76}
77
78DecimalFormatSymbols::DecimalFormatSymbols()
79        : UObject(),
80          locale(Locale::getRoot()),
81          currPattern(NULL) {
82    *validLocale = *actualLocale = 0;
83    initialize();
84}
85
86DecimalFormatSymbols*
87DecimalFormatSymbols::createWithLastResortData(UErrorCode& status) {
88    if (U_FAILURE(status)) { return NULL; }
89    DecimalFormatSymbols* sym = new DecimalFormatSymbols();
90    if (sym == NULL) {
91        status = U_MEMORY_ALLOCATION_ERROR;
92    }
93    return sym;
94}
95
96// -------------------------------------
97
98DecimalFormatSymbols::~DecimalFormatSymbols()
99{
100}
101
102// -------------------------------------
103// copy constructor
104
105DecimalFormatSymbols::DecimalFormatSymbols(const DecimalFormatSymbols &source)
106    : UObject(source)
107{
108    *this = source;
109}
110
111// -------------------------------------
112// assignment operator
113
114DecimalFormatSymbols&
115DecimalFormatSymbols::operator=(const DecimalFormatSymbols& rhs)
116{
117    if (this != &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]);
121        }
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]);
125        }
126        locale = rhs.locale;
127        uprv_strcpy(validLocale, rhs.validLocale);
128        uprv_strcpy(actualLocale, rhs.actualLocale);
129        fIsCustomCurrencySymbol = rhs.fIsCustomCurrencySymbol;
130        fIsCustomIntlCurrencySymbol = rhs.fIsCustomIntlCurrencySymbol;
131    }
132    return *this;
133}
134
135// -------------------------------------
136
137UBool
138DecimalFormatSymbols::operator==(const DecimalFormatSymbols& that) const
139{
140    if (this == &that) {
141        return TRUE;
142    }
143    if (fIsCustomCurrencySymbol != that.fIsCustomCurrencySymbol) {
144        return FALSE;
145    }
146    if (fIsCustomIntlCurrencySymbol != that.fIsCustomIntlCurrencySymbol) {
147        return FALSE;
148    }
149    for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) {
150        if(fSymbols[(ENumberFormatSymbol)i] != that.fSymbols[(ENumberFormatSymbol)i]) {
151            return FALSE;
152        }
153    }
154    for(int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; ++i) {
155        if(currencySpcBeforeSym[i] != that.currencySpcBeforeSym[i]) {
156            return FALSE;
157        }
158        if(currencySpcAfterSym[i] != that.currencySpcAfterSym[i]) {
159            return FALSE;
160        }
161    }
162    return locale == that.locale &&
163        uprv_strcmp(validLocale, that.validLocale) == 0 &&
164        uprv_strcmp(actualLocale, that.actualLocale) == 0;
165}
166
167// -------------------------------------
168
169void
170DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool useLastResortData)
171{
172    static const char *gNumberElementKeys[kFormatSymbolCount] = {
173        "decimal",
174        "group",
175        "list",
176        "percentSign",
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 */
179        "minusSign",
180        "plusSign",
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 */
183        "currencyDecimal",
184        "exponential",
185        "perMille",
186        NULL, /* Escape padding character - not in CLDR */
187        "infinity",
188        "nan",
189        NULL, /* Significant digit symbol - not in CLDR */
190        "currencyGroup",
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 */
201    };
202
203    static const char *gLatn =  "latn";
204    static const char *gSymbols = "symbols";
205    const char *nsName;
206    const UChar *sym = NULL;
207    int32_t len = 0;
208
209    *validLocale = *actualLocale = 0;
210    currPattern = NULL;
211    if (U_FAILURE(status))
212        return;
213
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));
218
219    if (U_FAILURE(status)) {
220        if ( useLastResortData ) {
221            status = U_USING_DEFAULT_WARNING;
222            initialize();
223        }
224        return;
225    }
226
227    // First initialize all the symbols to the fallbacks for anything we can't find
228    initialize();
229
230    //
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
233    //
234
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);
246        }
247    } else {
248        nsName = gLatn;
249    }
250
251    UBool isLatn = !uprv_strcmp(nsName,gLatn);
252
253    UErrorCode nlStatus = U_ZERO_ERROR;
254    LocalUResourceBundlePointer nonLatnSymbols;
255    if ( !isLatn ) {
256        nonLatnSymbols.adoptInstead(
257            ures_getByKeyWithFallback(numberElementsRes.getAlias(), nsName, NULL, &nlStatus));
258        ures_getByKeyWithFallback(nonLatnSymbols.getAlias(), gSymbols, nonLatnSymbols.getAlias(), &nlStatus);
259    }
260
261    LocalUResourceBundlePointer latnSymbols(
262        ures_getByKeyWithFallback(numberElementsRes.getAlias(), gLatn, NULL, &status));
263    ures_getByKeyWithFallback(latnSymbols.getAlias(), gSymbols, latnSymbols.getAlias(), &status);
264
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;
270            if ( !isLatn ) {
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);
279                }
280            } else {
281                    sym = ures_getStringByKeyWithFallback(latnSymbols.getAlias(),
282                                                          gNumberElementKeys[i], &len, &localStatus);
283            }
284
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;
291                }
292            }
293        }
294    }
295
296    // If monetary decimal or grouping were not explicitly set, then set them to be the
297    // same as their non-monetary counterparts.
298
299    if ( !kMonetaryDecimalSet ) {
300        setSymbol(kMonetarySeparatorSymbol,fSymbols[kDecimalSeparatorSymbol]);
301    }
302    if ( !kMonetaryGroupingSet ) {
303        setSymbol(kMonetaryGroupingSeparatorSymbol,fSymbols[kGroupingSeparatorSymbol]);
304    }
305
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
310    UChar curriso[4];
311    UnicodeString tempStr;
312    ucurr_forLocale(locStr, curriso, 4, &internalStatus);
313
314    uprv_getStaticCurrencyName(curriso, locStr, tempStr, internalStatus);
315    if (U_SUCCESS(internalStatus)) {
316        fSymbols[kIntlCurrencySymbol].setTo(curriso, -1);
317        fSymbols[kCurrencySymbol] = tempStr;
318    }
319    /* else use the default values. */
320
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));
326
327    //load the currency data
328    UChar ucc[4]={0}; //Currency Codes are always 3 chars long
329    int32_t uccLen = 4;
330    const char* locName = loc.getName();
331    UErrorCode localStatus = U_ZERO_ERROR;
332    uccLen = ucurr_forLocale(locName, ucc, uccLen, &localStatus);
333
334    if(U_SUCCESS(localStatus) && uccLen > 0) {
335        char cc[4]={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;
345            currPattern =
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;
356            }
357        }
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. */
360    }
361        // else ignore the error if no currency
362
363    // Currency Spacing.
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));
369
370    if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) {
371        const char* keywords[UNUM_CURRENCY_SPACING_COUNT] = {
372            gCurrencyMatchTag, gCurrencySudMatchTag, gCurrencyInsertBtnTag
373        };
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);
383            }
384        }
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);
393            }
394        }
395    }
396}
397
398void
399DecimalFormatSymbols::initialize() {
400    /*
401     * These strings used to be in static arrays, but the HP/UX aCC compiler
402     * cannot initialize a static array with class constructors.
403     *  markus 2000may25
404     */
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;
435
436}
437
438Locale
439DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
440    U_LOCALE_BASED(locBased, *this);
441    return locBased.getLocale(type, status);
442}
443
444const UnicodeString&
445DecimalFormatSymbols::getPatternForCurrencySpacing(UCurrencySpacing type,
446                                                 UBool beforeCurrency,
447                                                 UErrorCode& status) const {
448    if (U_FAILURE(status)) {
449      return fNoSymbol;  // always empty.
450    }
451    if (beforeCurrency) {
452      return currencySpcBeforeSym[(int32_t)type];
453    } else {
454      return currencySpcAfterSym[(int32_t)type];
455    }
456}
457
458void
459DecimalFormatSymbols::setPatternForCurrencySpacing(UCurrencySpacing type,
460                                                   UBool beforeCurrency,
461                                             const UnicodeString& pattern) {
462  if (beforeCurrency) {
463    currencySpcBeforeSym[(int32_t)type] = pattern;
464  } else {
465    currencySpcAfterSym[(int32_t)type] =  pattern;
466  }
467}
468U_NAMESPACE_END
469
470#endif /* #if !UCONFIG_NO_FORMATTING */
471
472//eof
473