1/*
2*******************************************************************************
3* Copyright (C) 1997-2013, 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    }
130    return *this;
131}
132
133// -------------------------------------
134
135UBool
136DecimalFormatSymbols::operator==(const DecimalFormatSymbols& that) const
137{
138    if (this == &that) {
139        return TRUE;
140    }
141    for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) {
142        if(fSymbols[(ENumberFormatSymbol)i] != that.fSymbols[(ENumberFormatSymbol)i]) {
143            return FALSE;
144        }
145    }
146    for(int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; ++i) {
147        if(currencySpcBeforeSym[i] != that.currencySpcBeforeSym[i]) {
148            return FALSE;
149        }
150        if(currencySpcAfterSym[i] != that.currencySpcAfterSym[i]) {
151            return FALSE;
152        }
153    }
154    return locale == that.locale &&
155        uprv_strcmp(validLocale, that.validLocale) == 0 &&
156        uprv_strcmp(actualLocale, that.actualLocale) == 0;
157}
158
159// -------------------------------------
160
161void
162DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool useLastResortData)
163{
164    static const char *gNumberElementKeys[kFormatSymbolCount] = {
165        "decimal",
166        "group",
167        "list",
168        "percentSign",
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 */
171        "minusSign",
172        "plusSign",
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 */
175        "currencyDecimal",
176        "exponential",
177        "perMille",
178        NULL, /* Escape padding character - not in CLDR */
179        "infinity",
180        "nan",
181        NULL, /* Significant digit symbol - not in CLDR */
182        "currencyGroup",
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    };
193
194    static const char *gLatn =  "latn";
195    static const char *gSymbols = "symbols";
196    const char *nsName;
197    const UChar *sym = NULL;
198    int32_t len = 0;
199
200    *validLocale = *actualLocale = 0;
201    currPattern = NULL;
202    if (U_FAILURE(status))
203        return;
204
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));
209
210    if (U_FAILURE(status)) {
211        if ( useLastResortData ) {
212            status = U_USING_DEFAULT_WARNING;
213            initialize();
214        }
215        return;
216    }
217
218    // First initialize all the symbols to the fallbacks for anything we can't find
219    initialize();
220
221    //
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
224    //
225
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);
237        }
238    } else {
239        nsName = gLatn;
240    }
241
242    UBool isLatn = !uprv_strcmp(nsName,gLatn);
243
244    UErrorCode nlStatus = U_ZERO_ERROR;
245    LocalUResourceBundlePointer nonLatnSymbols;
246    if ( !isLatn ) {
247        nonLatnSymbols.adoptInstead(
248            ures_getByKeyWithFallback(numberElementsRes.getAlias(), nsName, NULL, &nlStatus));
249        ures_getByKeyWithFallback(nonLatnSymbols.getAlias(), gSymbols, nonLatnSymbols.getAlias(), &nlStatus);
250    }
251
252    LocalUResourceBundlePointer latnSymbols(
253        ures_getByKeyWithFallback(numberElementsRes.getAlias(), gLatn, NULL, &status));
254    ures_getByKeyWithFallback(latnSymbols.getAlias(), gSymbols, latnSymbols.getAlias(), &status);
255
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;
261            if ( !isLatn ) {
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);
270                }
271            } else {
272                    sym = ures_getStringByKeyWithFallback(latnSymbols.getAlias(),
273                                                          gNumberElementKeys[i], &len, &localStatus);
274            }
275
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;
282                }
283            }
284        }
285    }
286
287    // If monetary decimal or grouping were not explicitly set, then set them to be the
288    // same as their non-monetary counterparts.
289
290    if ( !kMonetaryDecimalSet ) {
291        setSymbol(kMonetarySeparatorSymbol,fSymbols[kDecimalSeparatorSymbol]);
292    }
293    if ( !kMonetaryGroupingSet ) {
294        setSymbol(kMonetaryGroupingSeparatorSymbol,fSymbols[kGroupingSeparatorSymbol]);
295    }
296
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
301    UChar curriso[4];
302    UnicodeString tempStr;
303    ucurr_forLocale(locStr, curriso, 4, &internalStatus);
304
305    uprv_getStaticCurrencyName(curriso, locStr, tempStr, internalStatus);
306    if (U_SUCCESS(internalStatus)) {
307        fSymbols[kIntlCurrencySymbol].setTo(curriso, -1);
308        fSymbols[kCurrencySymbol] = tempStr;
309    }
310    /* else use the default values. */
311
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));
317
318    //load the currency data
319    UChar ucc[4]={0}; //Currency Codes are always 3 chars long
320    int32_t uccLen = 4;
321    const char* locName = loc.getName();
322    UErrorCode localStatus = U_ZERO_ERROR;
323    uccLen = ucurr_forLocale(locName, ucc, uccLen, &localStatus);
324
325    if(U_SUCCESS(localStatus) && uccLen > 0) {
326        char cc[4]={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;
336            currPattern =
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;
347            }
348        }
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. */
351    }
352        // else ignore the error if no currency
353
354    // Currency Spacing.
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));
360
361    if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) {
362        const char* keywords[UNUM_CURRENCY_SPACING_COUNT] = {
363            gCurrencyMatchTag, gCurrencySudMatchTag, gCurrencyInsertBtnTag
364        };
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);
374            }
375        }
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);
384            }
385        }
386    }
387}
388
389void
390DecimalFormatSymbols::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].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(); //
423}
424
425Locale
426DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
427    U_LOCALE_BASED(locBased, *this);
428    return locBased.getLocale(type, status);
429}
430
431const UnicodeString&
432DecimalFormatSymbols::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
445void
446DecimalFormatSymbols::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}
455U_NAMESPACE_END
456
457#endif /* #if !UCONFIG_NO_FORMATTING */
458
459//eof
460