Home | History | Annotate | Download | only in numfmt
      1 /********************************************************************
      2  * COPYRIGHT:
      3  * Copyright (c) 1999-2014, International Business Machines Corporation and
      4  * others. All Rights Reserved.
      5  ********************************************************************/
      6 
      7 #include "unicode/utypes.h"
      8 #include "unicode/unistr.h"
      9 #include "unicode/numfmt.h"
     10 #include "unicode/dcfmtsym.h"
     11 #include "unicode/decimfmt.h"
     12 #include "unicode/locid.h"
     13 #include "unicode/uclean.h"
     14 #include "util.h"
     15 #include <stdio.h>
     16 #include <stdlib.h>
     17 #include <string.h>
     18 
     19 extern "C" void capi();
     20 void cppapi();
     21 
     22 static void
     23 showCurrencyFormatting(UBool useICU26API);
     24 
     25 int main(int argc, char **argv) {
     26     printf("%s output is in UTF-8\n", argv[0]);
     27 
     28     printf("C++ API\n");
     29     cppapi();
     30 
     31     printf("C API\n");
     32     capi();
     33 
     34     showCurrencyFormatting(FALSE);
     35     showCurrencyFormatting(TRUE);
     36 
     37     u_cleanup();    // Release any additional storage held by ICU.
     38 
     39     printf("Exiting successfully\n");
     40     return 0;
     41 }
     42 
     43 /**
     44  * Sample code for the C++ API to NumberFormat.
     45  */
     46 void cppapi() {
     47     Locale us("en", "US");
     48     UErrorCode status = U_ZERO_ERROR;
     49 
     50     // Create a number formatter for the US locale
     51     NumberFormat *fmt = NumberFormat::createInstance(us, status);
     52     check(status, "NumberFormat::createInstance");
     53 
     54     // Parse a string.  The string uses the digits '0' through '9'
     55     // and the decimal separator '.', standard in the US locale
     56     UnicodeString str("9876543210.123");
     57     Formattable result;
     58     fmt->parse(str, result, status);
     59     check(status, "NumberFormat::parse");
     60 
     61     printf("NumberFormat::parse(\""); // Display the result
     62     uprintf(str);
     63     printf("\") => ");
     64     uprintf(formattableToString(result));
     65     printf("\n");
     66 
     67     // Take the number parsed above, and use the formatter to
     68     // format it.
     69     str.remove(); // format() will APPEND to this string
     70     fmt->format(result, str, status);
     71     check(status, "NumberFormat::format");
     72 
     73     printf("NumberFormat::format("); // Display the result
     74     uprintf(formattableToString(result));
     75     printf(") => \"");
     76     uprintf(str);
     77     printf("\"\n");
     78 
     79     delete fmt; // Release the storage used by the formatter
     80 
     81 }
     82 
     83 // currency formatting ----------------------------------------------------- ***
     84 
     85 /*
     86  * Set a currency on a NumberFormat with pre-ICU 2.6 APIs.
     87  * This is a "hack" that will not work properly for all cases because
     88  * only ICU 2.6 introduced a more complete framework and data for this.
     89  *
     90  * @param nf The NumberFormat on which to set the currency; takes effect on
     91  *           currency-formatting NumberFormat instances.
     92  *           This must actually be a DecimalFormat instance.
     93  *           The display style of the output is controlled by nf (its pattern,
     94  *           usually from the display locale ID used to create this instance)
     95  *           while the currency symbol and number of decimals are set for
     96  *           the currency.
     97  * @param currency The 3-letter ISO 4217 currency code, NUL-terminated.
     98  * @param errorCode ICU error code, must pass U_SUCCESS() on input.
     99  */
    100 static void
    101 setNumberFormatCurrency_2_4(NumberFormat &nf, const char *currency, UErrorCode &errorCode) {
    102     // argument checking
    103     if(U_FAILURE(errorCode)) {
    104         return;
    105     }
    106     if(currency==NULL || strlen(currency)!=3) {
    107         errorCode=U_ILLEGAL_ARGUMENT_ERROR;
    108         return;
    109     }
    110 
    111     // check that the formatter is a DecimalFormat instance
    112     // necessary because we will cast to the DecimalFormat subclass to set
    113     // the currency symbol
    114     DecimalFormat *dnf=dynamic_cast<DecimalFormat *>(&nf);
    115     if(dnf==NULL) {
    116         errorCode=U_ILLEGAL_ARGUMENT_ERROR;
    117         return;
    118     }
    119 
    120     // map the currency code to a locale ID
    121     // only the currencies in this array are supported
    122     // it would be possible to map to a locale ID, instantiate a currency
    123     // formatter for that and copy its values, but that would be slower,
    124     // and we have to hardcode something here anyway
    125     static const struct {
    126         // ISO currency ID
    127         const char *currency;
    128 
    129         // fractionDigits==minimumFractionDigits==maximumFractionDigits
    130         // for these currencies
    131         int32_t fractionDigits;
    132 
    133         /*
    134          * Set the rounding increment to 0 if it is implied with the number of
    135          * fraction digits. Setting an explicit rounding increment makes
    136          * number formatting slower.
    137          * In other words, set it to something other than 0 only for unusual
    138          * cases like "nickel rounding" (0.05) when the increment differs from
    139          * 10^(-maximumFractionDigits).
    140          */
    141         double roundingIncrement;
    142 
    143         // Unicode string with the desired currency display symbol or name
    144         UChar symbol[16];
    145     } currencyMap[]={
    146         { "USD", 2, 0.0, { 0x24, 0 } },
    147         { "GBP", 2, 0.0, { 0xa3, 0 } },
    148         { "EUR", 2, 0.0, { 0x20ac, 0 } },
    149         { "JPY", 0, 0.0, { 0xa5, 0 } }
    150     };
    151 
    152     int32_t i;
    153 
    154     for(i=0; i<UPRV_LENGTHOF(currencyMap); ++i) {
    155         if(strcmp(currency, currencyMap[i].currency)==0) {
    156             break;
    157         }
    158     }
    159     if(i==UPRV_LENGTHOF(currencyMap)) {
    160         // a more specific error code would be useful in a real application
    161         errorCode=U_UNSUPPORTED_ERROR;
    162         return;
    163     }
    164 
    165     // set the currency-related data into the caller's formatter
    166 
    167     nf.setMinimumFractionDigits(currencyMap[i].fractionDigits);
    168     nf.setMaximumFractionDigits(currencyMap[i].fractionDigits);
    169 
    170     dnf->setRoundingIncrement(currencyMap[i].roundingIncrement);
    171 
    172     DecimalFormatSymbols symbols(*dnf->getDecimalFormatSymbols());
    173     symbols.setSymbol(DecimalFormatSymbols::kCurrencySymbol, currencyMap[i].symbol);
    174     dnf->setDecimalFormatSymbols(symbols); // do not adopt symbols: Jitterbug 2889
    175 }
    176 
    177 /*
    178  * Set a currency on a NumberFormat with ICU 2.6 APIs.
    179  *
    180  * @param nf The NumberFormat on which to set the currency; takes effect on
    181  *           currency-formatting NumberFormat instances.
    182  *           The display style of the output is controlled by nf (its pattern,
    183  *           usually from the display locale ID used to create this instance)
    184  *           while the currency symbol and number of decimals are set for
    185  *           the currency.
    186  * @param currency The 3-letter ISO 4217 currency code, NUL-terminated.
    187  * @param errorCode ICU error code, must pass U_SUCCESS() on input.
    188  */
    189 static void
    190 setNumberFormatCurrency_2_6(NumberFormat &nf, const char *currency, UErrorCode &errorCode) {
    191     if(U_FAILURE(errorCode)) {
    192         return;
    193     }
    194     if(currency==NULL || strlen(currency)!=3) {
    195         errorCode=U_ILLEGAL_ARGUMENT_ERROR;
    196         return;
    197     }
    198 
    199     // invariant-character conversion to UChars (see utypes.h and putil.h)
    200     UChar uCurrency[4];
    201     u_charsToUChars(currency, uCurrency, 4);
    202 
    203     // set the currency
    204     // in ICU 3.0 this API (which was @draft ICU 2.6) gained a UErrorCode& argument
    205 #if (U_ICU_VERSION_MAJOR_NUM < 3)
    206     nf.setCurrency(uCurrency);
    207 #else
    208     nf.setCurrency(uCurrency, errorCode);
    209 #endif
    210 }
    211 
    212 static const char *const
    213 sampleLocaleIDs[]={
    214     // use locale IDs complete with country code to be sure to
    215     // pick up number/currency format patterns
    216     "en_US", "en_GB", "de_DE", "ja_JP", "fr_FR", "hi_IN"
    217 };
    218 
    219 static const char *const
    220 sampleCurrencies[]={
    221     "USD", "GBP", "EUR", "JPY"
    222 };
    223 
    224 static void
    225 showCurrencyFormatting(UBool useICU26API) {
    226     NumberFormat *nf;
    227     int32_t i, j;
    228 
    229     UnicodeString output;
    230 
    231     UErrorCode errorCode;
    232 
    233     // TODO: Using printf() here assumes that the runtime encoding is ASCII-friendly
    234     // and can therefore be mixed with UTF-8
    235 
    236     for(i=0; i<UPRV_LENGTHOF(sampleLocaleIDs); ++i) {
    237         printf("show currency formatting (method for %s) in the locale \"%s\"\n",
    238                 useICU26API ? "ICU 2.6" : "before ICU 2.6",
    239                 sampleLocaleIDs[i]);
    240 
    241         // get a currency formatter for this locale ID
    242         errorCode=U_ZERO_ERROR;
    243         nf=NumberFormat::createCurrencyInstance(sampleLocaleIDs[i], errorCode);
    244         if(U_FAILURE(errorCode)) {
    245             printf("NumberFormat::createCurrencyInstance(%s) failed - %s\n",
    246                     sampleLocaleIDs[i], u_errorName(errorCode));
    247             continue;
    248         }
    249 
    250         for(j=0; j<UPRV_LENGTHOF(sampleCurrencies); ++j) {
    251             printf("  - format currency \"%s\": ", sampleCurrencies[j]);
    252 
    253             // set the actual currency to be formatted
    254             if(useICU26API) {
    255                 setNumberFormatCurrency_2_6(*nf, sampleCurrencies[j], errorCode);
    256             } else {
    257                 setNumberFormatCurrency_2_4(*nf, sampleCurrencies[j], errorCode);
    258             }
    259             if(U_FAILURE(errorCode)) {
    260                 printf("setNumberFormatCurrency(%s) failed - %s\n",
    261                         sampleCurrencies[j], u_errorName(errorCode));
    262                 continue;
    263             }
    264 
    265             // output=formatted currency value
    266             output.remove();
    267             nf->format(12345678.93, output);
    268             output+=(UChar)0x0a; // '\n'
    269             uprintf(output);
    270         }
    271     }
    272 }
    273