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 
main(int argc,char ** argv)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  */
cppapi()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
setNumberFormatCurrency_2_4(NumberFormat & nf,const char * currency,UErrorCode & errorCode)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
setNumberFormatCurrency_2_6(NumberFormat & nf,const char * currency,UErrorCode & errorCode)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
showCurrencyFormatting(UBool useICU26API)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