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
19extern "C" void capi();
20void cppapi();
21
22static void
23showCurrencyFormatting(UBool useICU26API);
24
25int 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 */
46void 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 */
100static void
101setNumberFormatCurrency_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 */
189static void
190setNumberFormatCurrency_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
212static const char *const
213sampleLocaleIDs[]={
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
219static const char *const
220sampleCurrencies[]={
221    "USD", "GBP", "EUR", "JPY"
222};
223
224static void
225showCurrencyFormatting(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