Home | History | Annotate | Download | only in i18n
      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 
     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     }
    130     return *this;
    131 }
    132 
    133 // -------------------------------------
    134 
    135 UBool
    136 DecimalFormatSymbols::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 
    161 void
    162 DecimalFormatSymbols::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 
    389 void
    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].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 
    425 Locale
    426 DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
    427     U_LOCALE_BASED(locBased, *this);
    428     return locBased.getLocale(type, status);
    429 }
    430 
    431 const UnicodeString&
    432 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
    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