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