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