Home | History | Annotate | Download | only in i18n
      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 
     43 U_NAMESPACE_BEGIN
     44 
     45 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormatSymbols)
     46 
     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";
     54 
     55 
     56 static const UChar INTL_CURRENCY_SYMBOL_STR[] = {0xa4, 0xa4, 0};
     57 
     58 // -------------------------------------
     59 // Initializes this with the decimal format symbols in the default locale.
     60 
     61 DecimalFormatSymbols::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 
     71 DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status)
     72     : UObject(),
     73     locale(loc)
     74 {
     75     initialize(locale, status);
     76 }
     77 
     78 DecimalFormatSymbols::DecimalFormatSymbols()
     79         : UObject(),
     80           locale(Locale::getRoot()),
     81           currPattern(NULL) {
     82     *validLocale = *actualLocale = 0;
     83     initialize();
     84 }
     85 
     86 DecimalFormatSymbols*
     87 DecimalFormatSymbols::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 
     98 DecimalFormatSymbols::~DecimalFormatSymbols()
     99 {
    100 }
    101 
    102 // -------------------------------------
    103 // copy constructor
    104 
    105 DecimalFormatSymbols::DecimalFormatSymbols(const DecimalFormatSymbols &source)
    106     : UObject(source)
    107 {
    108     *this = source;
    109 }
    110 
    111 // -------------------------------------
    112 // assignment operator
    113 
    114 DecimalFormatSymbols&
    115 DecimalFormatSymbols::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 
    137 UBool
    138 DecimalFormatSymbols::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 
    169 void
    170 DecimalFormatSymbols::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 
    398 void
    399 DecimalFormatSymbols::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 
    438 Locale
    439 DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
    440     U_LOCALE_BASED(locBased, *this);
    441     return locBased.getLocale(type, status);
    442 }
    443 
    444 const UnicodeString&
    445 DecimalFormatSymbols::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 
    458 void
    459 DecimalFormatSymbols::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 }
    468 U_NAMESPACE_END
    469 
    470 #endif /* #if !UCONFIG_NO_FORMATTING */
    471 
    472 //eof
    473