1/********************************************************************
2 *   Copyright (C) 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
23extern "C" void capi();
24void cppapi();
25
26static void
27showCurrencyFormatting(UBool useICU26API);
28
29int 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 */
50void 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 */
104static void
105setNumberFormatCurrency_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 */
193static void
194setNumberFormatCurrency_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
216static const char *const
217sampleLocaleIDs[]={
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
223static const char *const
224sampleCurrencies[]={
225    "USD", "GBP", "EUR", "JPY"
226};
227
228static void
229showCurrencyFormatting(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