Home | History | Annotate | Download | only in i18n
      1 /*
      2 *******************************************************************************
      3 * Copyright (C) 1997-2009, 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 // *****************************************************************************
     36 // class DecimalFormatSymbols
     37 // *****************************************************************************
     38 
     39 U_NAMESPACE_BEGIN
     40 
     41 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormatSymbols)
     42 
     43 static const char gNumberElements[] = "NumberElements";
     44 static const char gCurrencySpacingTag[] = "currencySpacing";
     45 static const char gBeforeCurrencyTag[] = "beforeCurrency";
     46 static const char gAfterCurrencyTag[] = "afterCurrency";
     47 static const char gCurrencyMatchTag[] = "currencyMatch";
     48 static const char gCurrencySudMatchTag[] = "surroundingMatch";
     49 static const char gCurrencyInsertBtnTag[] = "insertBetween";
     50 
     51 static const UChar INTL_CURRENCY_SYMBOL_STR[] = {0xa4, 0xa4, 0};
     52 
     53 // -------------------------------------
     54 // Initializes this with the decimal format symbols in the default locale.
     55 
     56 DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status)
     57     : UObject(),
     58     locale()
     59 {
     60     initialize(locale, status, TRUE);
     61 }
     62 
     63 // -------------------------------------
     64 // Initializes this with the decimal format symbols in the desired locale.
     65 
     66 DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status)
     67     : UObject(),
     68     locale(loc)
     69 {
     70     initialize(locale, status);
     71 }
     72 
     73 // -------------------------------------
     74 
     75 DecimalFormatSymbols::~DecimalFormatSymbols()
     76 {
     77 }
     78 
     79 // -------------------------------------
     80 // copy constructor
     81 
     82 DecimalFormatSymbols::DecimalFormatSymbols(const DecimalFormatSymbols &source)
     83     : UObject(source)
     84 {
     85     *this = source;
     86 }
     87 
     88 // -------------------------------------
     89 // assignment operator
     90 
     91 DecimalFormatSymbols&
     92 DecimalFormatSymbols::operator=(const DecimalFormatSymbols& rhs)
     93 {
     94     if (this != &rhs) {
     95         for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) {
     96             // fastCopyFrom is safe, see docs on fSymbols
     97             fSymbols[(ENumberFormatSymbol)i].fastCopyFrom(rhs.fSymbols[(ENumberFormatSymbol)i]);
     98         }
     99         for(int32_t i = 0; i < (int32_t)kCurrencySpacingCount; ++i) {
    100             currencySpcBeforeSym[i].fastCopyFrom(rhs.currencySpcBeforeSym[i]);
    101             currencySpcAfterSym[i].fastCopyFrom(rhs.currencySpcAfterSym[i]);
    102         }
    103         locale = rhs.locale;
    104         uprv_strcpy(validLocale, rhs.validLocale);
    105         uprv_strcpy(actualLocale, rhs.actualLocale);
    106     }
    107     return *this;
    108 }
    109 
    110 // -------------------------------------
    111 
    112 UBool
    113 DecimalFormatSymbols::operator==(const DecimalFormatSymbols& that) const
    114 {
    115     if (this == &that) {
    116         return TRUE;
    117     }
    118     for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) {
    119         if(fSymbols[(ENumberFormatSymbol)i] != that.fSymbols[(ENumberFormatSymbol)i]) {
    120             return FALSE;
    121         }
    122     }
    123     for(int32_t i = 0; i < (int32_t)kCurrencySpacingCount; ++i) {
    124         if(currencySpcBeforeSym[i] != that.currencySpcBeforeSym[i]) {
    125             return FALSE;
    126         }
    127         if(currencySpcAfterSym[i] != that.currencySpcAfterSym[i]) {
    128             return FALSE;
    129         }
    130     }
    131     return locale == that.locale &&
    132         uprv_strcmp(validLocale, that.validLocale) == 0 &&
    133         uprv_strcmp(actualLocale, that.actualLocale) == 0;
    134 }
    135 
    136 // -------------------------------------
    137 
    138 void
    139 DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status,
    140                                  UBool useLastResortData)
    141 {
    142     *validLocale = *actualLocale = 0;
    143     currPattern = NULL;
    144     if (U_FAILURE(status))
    145         return;
    146 
    147     const char* locStr = loc.getName();
    148     UResourceBundle *resource = ures_open((char *)0, locStr, &status);
    149     UResourceBundle *numberElementsRes = ures_getByKey(resource, gNumberElements, NULL, &status);
    150     if (U_FAILURE(status))
    151     {
    152         // Initializes with last resort data if necessary.
    153         if (useLastResortData)
    154         {
    155             status = U_USING_FALLBACK_WARNING;
    156             initialize();
    157         }
    158     }
    159     else {
    160         // Gets the number element array.
    161         int32_t numberElementsLength = ures_getSize(numberElementsRes);
    162 
    163         if (numberElementsLength > (int32_t)kFormatSymbolCount) {
    164             /* Warning: Invalid format. Array too large. */
    165             numberElementsLength = (int32_t)kFormatSymbolCount;
    166         }
    167         // If the array size is too small, something is wrong with the resource
    168         // bundle, returns the failure error code.
    169         if (numberElementsLength != 12 || U_FAILURE(status)) {
    170             status = U_INVALID_FORMAT_ERROR;
    171         }
    172         else {
    173             const UChar *numberElements[kFormatSymbolCount];
    174             int32_t numberElementsStrLen[kFormatSymbolCount];
    175             int32_t i = 0;
    176             for(i = 0; i<numberElementsLength; i++) {
    177                 numberElements[i] = ures_getStringByIndex(numberElementsRes, i, &numberElementsStrLen[i], &status);
    178             }
    179 
    180             if (U_SUCCESS(status)) {
    181                 initialize(numberElements, numberElementsStrLen, numberElementsLength);
    182 
    183                 // Attempt to set the zero digit based on the numbering system for the locale requested
    184                 //
    185                 NumberingSystem* ns = NumberingSystem::createInstance(loc,status);
    186                 if (U_SUCCESS(status) && ns->getRadix() == 10 && !ns->isAlgorithmic()) {
    187                     UnicodeString zeroDigit(ns->getDescription(),0,1);
    188                     setSymbol(kZeroDigitSymbol,zeroDigit);
    189                 }
    190                 if (ns) {
    191                     delete ns;
    192                 }
    193 
    194                 // Obtain currency data from the currency API.  This is strictly
    195                 // for backward compatibility; we don't use DecimalFormatSymbols
    196                 // for currency data anymore.
    197                 UErrorCode internalStatus = U_ZERO_ERROR; // don't propagate failures out
    198                 UChar curriso[4];
    199                 UnicodeString tempStr;
    200                 ucurr_forLocale(locStr, curriso, 4, &internalStatus);
    201 
    202                 // Reuse numberElements[0] as a temporary buffer
    203                 uprv_getStaticCurrencyName(curriso, locStr, tempStr, internalStatus);
    204                 if (U_SUCCESS(internalStatus)) {
    205                     fSymbols[kIntlCurrencySymbol] = curriso;
    206                     fSymbols[kCurrencySymbol] = tempStr;
    207                 }
    208                 /* else use the default values. */
    209             }
    210 
    211             U_LOCALE_BASED(locBased, *this);
    212             locBased.setLocaleIDs(ures_getLocaleByType(numberElementsRes,
    213                                        ULOC_VALID_LOCALE, &status),
    214                                   ures_getLocaleByType(numberElementsRes,
    215                                        ULOC_ACTUAL_LOCALE, &status));
    216         }
    217         //load the currency data
    218         UChar ucc[4]={0}; //Currency Codes are always 3 chars long
    219         int32_t uccLen = 4;
    220         const char* locName = loc.getName();
    221         UErrorCode localStatus = U_ZERO_ERROR;
    222         uccLen = ucurr_forLocale(locName, ucc, uccLen, &localStatus);
    223         if(U_SUCCESS(localStatus) && uccLen > 0) {
    224             char cc[4]={0};
    225             u_UCharsToChars(ucc, cc, uccLen);
    226             /* An explicit currency was requested */
    227             UResourceBundle *currency = ures_getByKeyWithFallback(resource, "Currencies", NULL, &localStatus);
    228             currency = ures_getByKeyWithFallback(currency, cc, currency, &localStatus);
    229             if(U_SUCCESS(localStatus) && ures_getSize(currency)>2) { // the length is 3 if more data is present
    230                 currency = ures_getByIndex(currency, 2, currency, &localStatus);
    231                 int32_t currPatternLen = 0;
    232                 currPattern = ures_getStringByIndex(currency, (int32_t)0, &currPatternLen, &localStatus);
    233                 UnicodeString decimalSep = ures_getStringByIndex(currency, (int32_t)1, NULL, &localStatus);
    234                 UnicodeString groupingSep = ures_getStringByIndex(currency, (int32_t)2, NULL, &localStatus);
    235                 if(U_SUCCESS(localStatus)){
    236                     fSymbols[kMonetaryGroupingSeparatorSymbol] = groupingSep;
    237                     fSymbols[kMonetarySeparatorSymbol] = decimalSep;
    238                     //pattern.setTo(TRUE, currPattern, currPatternLen);
    239                     status = localStatus;
    240                 }
    241             }
    242             ures_close(currency);
    243             /* else An explicit currency was requested and is unknown or locale data is malformed. */
    244             /* ucurr_* API will get the correct value later on. */
    245         }
    246         // else ignore the error if no currency
    247     }
    248     ures_close(numberElementsRes);
    249 
    250     // Currency Spacing.
    251     UErrorCode localStatus = U_ZERO_ERROR;
    252     UResourceBundle *currencySpcRes = ures_getByKeyWithFallback(resource,
    253                                        gCurrencySpacingTag, NULL, &localStatus);
    254 
    255     if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) {
    256         const char* keywords[kCurrencySpacingCount] = {
    257             gCurrencyMatchTag, gCurrencySudMatchTag, gCurrencyInsertBtnTag
    258         };
    259         localStatus = U_ZERO_ERROR;
    260         UResourceBundle *dataRes = ures_getByKeyWithFallback(currencySpcRes,
    261                                    gBeforeCurrencyTag, NULL, &localStatus);
    262         if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) {
    263             localStatus = U_ZERO_ERROR;
    264             for (int32_t i = 0; i < kCurrencySpacingCount; i++) {
    265               currencySpcBeforeSym[i] = ures_getStringByKey(dataRes, keywords[i],
    266                                                         NULL, &localStatus);
    267             }
    268             ures_close(dataRes);
    269         }
    270         dataRes = ures_getByKeyWithFallback(currencySpcRes,
    271                                   gAfterCurrencyTag, NULL, &localStatus);
    272         if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) {
    273             localStatus = U_ZERO_ERROR;
    274             for (int32_t i = 0; i < kCurrencySpacingCount; i++) {
    275               currencySpcAfterSym[i] = ures_getStringByKey(dataRes, keywords[i],
    276                                                             NULL, &localStatus);
    277             }
    278             ures_close(dataRes);
    279         }
    280         ures_close(currencySpcRes);
    281     }
    282     ures_close(resource);
    283 }
    284 
    285 // Initializes the DecimalFormatSymbol instance with the data obtained
    286 // from ResourceBundle in the desired locale.
    287 
    288 void
    289 DecimalFormatSymbols::initialize(const UChar** numberElements, int32_t *numberElementsStrLen, int32_t numberElementsLength)
    290 {
    291     static const int8_t TYPE_MAPPING[][2] = {
    292         {kDecimalSeparatorSymbol, 0},
    293         {kGroupingSeparatorSymbol, 1},
    294         {kPatternSeparatorSymbol, 2},
    295         {kPercentSymbol, 3},
    296         {kZeroDigitSymbol, 4},
    297         {kDigitSymbol, 5},
    298         {kMinusSignSymbol, 6},
    299         {kExponentialSymbol, 7},
    300         {kPerMillSymbol, 8},
    301         {kInfinitySymbol, 9},
    302         {kNaNSymbol, 10},
    303         {kPlusSignSymbol, 11},
    304         {kMonetarySeparatorSymbol, 0}
    305     };
    306     int32_t idx;
    307 
    308     for (idx = 0; idx < (int32_t)(sizeof(TYPE_MAPPING)/sizeof(TYPE_MAPPING[0])); idx++) {
    309         if (TYPE_MAPPING[idx][1] < numberElementsLength) {
    310             fSymbols[TYPE_MAPPING[idx][0]].setTo(TRUE, numberElements[TYPE_MAPPING[idx][1]], numberElementsStrLen[TYPE_MAPPING[idx][1]]);
    311         }
    312     }
    313 
    314     // Default values until it's set later on.
    315     fSymbols[kCurrencySymbol] = (UChar)0xa4;            // 'OX' currency symbol
    316     fSymbols[kIntlCurrencySymbol] = INTL_CURRENCY_SYMBOL_STR;
    317     // TODO: read from locale data, if this makes it into CLDR
    318     fSymbols[kSignificantDigitSymbol] = (UChar)0x0040;  // '@' significant digit
    319     fSymbols[kPadEscapeSymbol] = (UChar)0x002a; // TODO: '*' Hard coded for now; get from resource later
    320     fSymbols[kMonetaryGroupingSeparatorSymbol] = fSymbols[kGroupingSeparatorSymbol];
    321 }
    322 
    323 // initialize with default values
    324 void
    325 DecimalFormatSymbols::initialize() {
    326     /*
    327      * These strings used to be in static arrays, but the HP/UX aCC compiler
    328      * cannot initialize a static array with class constructors.
    329      *  markus 2000may25
    330      */
    331     fSymbols[kDecimalSeparatorSymbol] = (UChar)0x2e;    // '.' decimal separator
    332     fSymbols[kGroupingSeparatorSymbol].remove();        //     group (thousands) separator
    333     fSymbols[kPatternSeparatorSymbol] = (UChar)0x3b;    // ';' pattern separator
    334     fSymbols[kPercentSymbol] = (UChar)0x25;             // '%' percent sign
    335     fSymbols[kZeroDigitSymbol] = (UChar)0x30;           // '0' native 0 digit
    336     fSymbols[kDigitSymbol] = (UChar)0x23;               // '#' pattern digit
    337     fSymbols[kPlusSignSymbol] = (UChar)0x002b;          // '+' plus sign
    338     fSymbols[kMinusSignSymbol] = (UChar)0x2d;           // '-' minus sign
    339     fSymbols[kCurrencySymbol] = (UChar)0xa4;            // 'OX' currency symbol
    340     fSymbols[kIntlCurrencySymbol] = INTL_CURRENCY_SYMBOL_STR;
    341     fSymbols[kMonetarySeparatorSymbol] = (UChar)0x2e;   // '.' monetary decimal separator
    342     fSymbols[kExponentialSymbol] = (UChar)0x45;         // 'E' exponential
    343     fSymbols[kPerMillSymbol] = (UChar)0x2030;           // '%o' per mill
    344     fSymbols[kPadEscapeSymbol] = (UChar)0x2a;           // '*' pad escape symbol
    345     fSymbols[kInfinitySymbol] = (UChar)0x221e;          // 'oo' infinite
    346     fSymbols[kNaNSymbol] = (UChar)0xfffd;               // SUB NaN
    347     fSymbols[kSignificantDigitSymbol] = (UChar)0x0040;  // '@' significant digit
    348 }
    349 
    350 Locale
    351 DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
    352     U_LOCALE_BASED(locBased, *this);
    353     return locBased.getLocale(type, status);
    354 }
    355 
    356 const UnicodeString&
    357 DecimalFormatSymbols::getPatternForCurrencySpacing(ECurrencySpacing type,
    358                                                  UBool beforeCurrency,
    359                                                  UErrorCode& status) const {
    360     if (U_FAILURE(status)) {
    361       return fNoSymbol;  // always empty.
    362     }
    363     if (beforeCurrency) {
    364       return currencySpcBeforeSym[(int32_t)type];
    365     } else {
    366       return currencySpcAfterSym[(int32_t)type];
    367     }
    368 }
    369 
    370 void
    371 DecimalFormatSymbols::setPatternForCurrencySpacing(ECurrencySpacing type,
    372                                                    UBool beforeCurrency,
    373                                              const UnicodeString& pattern) {
    374   if (beforeCurrency) {
    375     currencySpcBeforeSym[(int32_t)type] = pattern;
    376   } else {
    377     currencySpcAfterSym[(int32_t)type] =  pattern;
    378   }
    379 }
    380 U_NAMESPACE_END
    381 
    382 #endif /* #if !UCONFIG_NO_FORMATTING */
    383 
    384 //eof
    385