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