|
1 /* |
|
2 ******************************************************************************** |
|
3 * Copyright (C) 2005-2013, International Business Machines |
|
4 * Corporation and others. All Rights Reserved. |
|
5 ******************************************************************************** |
|
6 * |
|
7 * File WINNMFMT.CPP |
|
8 * |
|
9 ******************************************************************************** |
|
10 */ |
|
11 |
|
12 #include "unicode/utypes.h" |
|
13 |
|
14 #if U_PLATFORM_USES_ONLY_WIN32_API |
|
15 |
|
16 #if !UCONFIG_NO_FORMATTING |
|
17 |
|
18 #include "winnmfmt.h" |
|
19 |
|
20 #include "unicode/format.h" |
|
21 #include "unicode/numfmt.h" |
|
22 #include "unicode/locid.h" |
|
23 #include "unicode/ustring.h" |
|
24 |
|
25 #include "cmemory.h" |
|
26 #include "uassert.h" |
|
27 #include "locmap.h" |
|
28 |
|
29 # define WIN32_LEAN_AND_MEAN |
|
30 # define VC_EXTRALEAN |
|
31 # define NOUSER |
|
32 # define NOSERVICE |
|
33 # define NOIME |
|
34 # define NOMCX |
|
35 #include <windows.h> |
|
36 #include <stdio.h> |
|
37 |
|
38 U_NAMESPACE_BEGIN |
|
39 |
|
40 union FormatInfo |
|
41 { |
|
42 NUMBERFMTW number; |
|
43 CURRENCYFMTW currency; |
|
44 }; |
|
45 |
|
46 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Win32NumberFormat) |
|
47 |
|
48 #define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type)) |
|
49 #define DELETE_ARRAY(array) uprv_free((void *) (array)) |
|
50 |
|
51 #define STACK_BUFFER_SIZE 32 |
|
52 |
|
53 /* |
|
54 * Turns a string of the form "3;2;0" into the grouping UINT |
|
55 * needed for NUMBERFMT and CURRENCYFMT. If the string does not |
|
56 * end in ";0" then the return value should be multiplied by 10. |
|
57 * (e.g. "3" => 30, "3;2" => 320) |
|
58 */ |
|
59 static UINT getGrouping(const char *grouping) |
|
60 { |
|
61 UINT g = 0; |
|
62 const char *s; |
|
63 |
|
64 for (s = grouping; *s != '\0'; s += 1) { |
|
65 if (*s > '0' && *s < '9') { |
|
66 g = g * 10 + (*s - '0'); |
|
67 } else if (*s != ';') { |
|
68 break; |
|
69 } |
|
70 } |
|
71 |
|
72 if (*s != '0') { |
|
73 g *= 10; |
|
74 } |
|
75 |
|
76 return g; |
|
77 } |
|
78 |
|
79 static void getNumberFormat(NUMBERFMTW *fmt, int32_t lcid) |
|
80 { |
|
81 char buf[10]; |
|
82 |
|
83 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_IDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT)); |
|
84 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->LeadingZero, sizeof(UINT)); |
|
85 |
|
86 GetLocaleInfoA(lcid, LOCALE_SGROUPING, buf, 10); |
|
87 fmt->Grouping = getGrouping(buf); |
|
88 |
|
89 fmt->lpDecimalSep = NEW_ARRAY(UChar, 6); |
|
90 GetLocaleInfoW(lcid, LOCALE_SDECIMAL, fmt->lpDecimalSep, 6); |
|
91 |
|
92 fmt->lpThousandSep = NEW_ARRAY(UChar, 6); |
|
93 GetLocaleInfoW(lcid, LOCALE_STHOUSAND, fmt->lpThousandSep, 6); |
|
94 |
|
95 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_INEGNUMBER, (LPWSTR) &fmt->NegativeOrder, sizeof(UINT)); |
|
96 } |
|
97 |
|
98 static void freeNumberFormat(NUMBERFMTW *fmt) |
|
99 { |
|
100 if (fmt != NULL) { |
|
101 DELETE_ARRAY(fmt->lpThousandSep); |
|
102 DELETE_ARRAY(fmt->lpDecimalSep); |
|
103 } |
|
104 } |
|
105 |
|
106 static void getCurrencyFormat(CURRENCYFMTW *fmt, int32_t lcid) |
|
107 { |
|
108 char buf[10]; |
|
109 |
|
110 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ICURRDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT)); |
|
111 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->LeadingZero, sizeof(UINT)); |
|
112 |
|
113 GetLocaleInfoA(lcid, LOCALE_SMONGROUPING, buf, sizeof(buf)); |
|
114 fmt->Grouping = getGrouping(buf); |
|
115 |
|
116 fmt->lpDecimalSep = NEW_ARRAY(UChar, 6); |
|
117 GetLocaleInfoW(lcid, LOCALE_SMONDECIMALSEP, fmt->lpDecimalSep, 6); |
|
118 |
|
119 fmt->lpThousandSep = NEW_ARRAY(UChar, 6); |
|
120 GetLocaleInfoW(lcid, LOCALE_SMONTHOUSANDSEP, fmt->lpThousandSep, 6); |
|
121 |
|
122 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_INEGCURR, (LPWSTR) &fmt->NegativeOrder, sizeof(UINT)); |
|
123 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ICURRENCY, (LPWSTR) &fmt->PositiveOrder, sizeof(UINT)); |
|
124 |
|
125 fmt->lpCurrencySymbol = NEW_ARRAY(UChar, 8); |
|
126 GetLocaleInfoW(lcid, LOCALE_SCURRENCY, (LPWSTR) fmt->lpCurrencySymbol, 8); |
|
127 } |
|
128 |
|
129 static void freeCurrencyFormat(CURRENCYFMTW *fmt) |
|
130 { |
|
131 if (fmt != NULL) { |
|
132 DELETE_ARRAY(fmt->lpCurrencySymbol); |
|
133 DELETE_ARRAY(fmt->lpThousandSep); |
|
134 DELETE_ARRAY(fmt->lpDecimalSep); |
|
135 } |
|
136 } |
|
137 |
|
138 Win32NumberFormat::Win32NumberFormat(const Locale &locale, UBool currency, UErrorCode &status) |
|
139 : NumberFormat(), fCurrency(currency), fFractionDigitsSet(FALSE), fFormatInfo(NULL) |
|
140 { |
|
141 if (!U_FAILURE(status)) { |
|
142 fLCID = locale.getLCID(); |
|
143 |
|
144 // Resolve actual locale to be used later |
|
145 UErrorCode tmpsts = U_ZERO_ERROR; |
|
146 char tmpLocID[ULOC_FULLNAME_CAPACITY]; |
|
147 int32_t len = uloc_getLocaleForLCID(fLCID, tmpLocID, sizeof(tmpLocID)/sizeof(tmpLocID[0]) - 1, &tmpsts); |
|
148 if (U_SUCCESS(tmpsts)) { |
|
149 tmpLocID[len] = 0; |
|
150 fLocale = Locale((const char*)tmpLocID); |
|
151 } |
|
152 |
|
153 fFormatInfo = (FormatInfo*)uprv_malloc(sizeof(FormatInfo)); |
|
154 |
|
155 if (fCurrency) { |
|
156 getCurrencyFormat(&fFormatInfo->currency, fLCID); |
|
157 } else { |
|
158 getNumberFormat(&fFormatInfo->number, fLCID); |
|
159 } |
|
160 } |
|
161 } |
|
162 |
|
163 Win32NumberFormat::Win32NumberFormat(const Win32NumberFormat &other) |
|
164 : NumberFormat(other), fFormatInfo((FormatInfo*)uprv_malloc(sizeof(FormatInfo))) |
|
165 { |
|
166 if (fFormatInfo != NULL) { |
|
167 uprv_memset(fFormatInfo, 0, sizeof(*fFormatInfo)); |
|
168 } |
|
169 *this = other; |
|
170 } |
|
171 |
|
172 Win32NumberFormat::~Win32NumberFormat() |
|
173 { |
|
174 if (fFormatInfo != NULL) { |
|
175 if (fCurrency) { |
|
176 freeCurrencyFormat(&fFormatInfo->currency); |
|
177 } else { |
|
178 freeNumberFormat(&fFormatInfo->number); |
|
179 } |
|
180 |
|
181 uprv_free(fFormatInfo); |
|
182 } |
|
183 } |
|
184 |
|
185 Win32NumberFormat &Win32NumberFormat::operator=(const Win32NumberFormat &other) |
|
186 { |
|
187 NumberFormat::operator=(other); |
|
188 |
|
189 this->fCurrency = other.fCurrency; |
|
190 this->fLocale = other.fLocale; |
|
191 this->fLCID = other.fLCID; |
|
192 this->fFractionDigitsSet = other.fFractionDigitsSet; |
|
193 |
|
194 if (fCurrency) { |
|
195 freeCurrencyFormat(&fFormatInfo->currency); |
|
196 getCurrencyFormat(&fFormatInfo->currency, fLCID); |
|
197 } else { |
|
198 freeNumberFormat(&fFormatInfo->number); |
|
199 getNumberFormat(&fFormatInfo->number, fLCID); |
|
200 } |
|
201 |
|
202 return *this; |
|
203 } |
|
204 |
|
205 Format *Win32NumberFormat::clone(void) const |
|
206 { |
|
207 return new Win32NumberFormat(*this); |
|
208 } |
|
209 |
|
210 UnicodeString& Win32NumberFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos) const |
|
211 { |
|
212 return format(getMaximumFractionDigits(), appendTo, L"%.16f", number); |
|
213 } |
|
214 |
|
215 UnicodeString& Win32NumberFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos) const |
|
216 { |
|
217 return format(getMinimumFractionDigits(), appendTo, L"%I32d", number); |
|
218 } |
|
219 |
|
220 UnicodeString& Win32NumberFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos) const |
|
221 { |
|
222 return format(getMinimumFractionDigits(), appendTo, L"%I64d", number); |
|
223 } |
|
224 |
|
225 void Win32NumberFormat::parse(const UnicodeString& text, Formattable& result, ParsePosition& parsePosition) const |
|
226 { |
|
227 UErrorCode status = U_ZERO_ERROR; |
|
228 NumberFormat *nf = fCurrency? NumberFormat::createCurrencyInstance(fLocale, status) : NumberFormat::createInstance(fLocale, status); |
|
229 |
|
230 nf->parse(text, result, parsePosition); |
|
231 delete nf; |
|
232 } |
|
233 void Win32NumberFormat::setMaximumFractionDigits(int32_t newValue) |
|
234 { |
|
235 fFractionDigitsSet = TRUE; |
|
236 NumberFormat::setMaximumFractionDigits(newValue); |
|
237 } |
|
238 |
|
239 void Win32NumberFormat::setMinimumFractionDigits(int32_t newValue) |
|
240 { |
|
241 fFractionDigitsSet = TRUE; |
|
242 NumberFormat::setMinimumFractionDigits(newValue); |
|
243 } |
|
244 |
|
245 UnicodeString &Win32NumberFormat::format(int32_t numDigits, UnicodeString &appendTo, wchar_t *fmt, ...) const |
|
246 { |
|
247 wchar_t nStackBuffer[STACK_BUFFER_SIZE]; |
|
248 wchar_t *nBuffer = nStackBuffer; |
|
249 va_list args; |
|
250 int result; |
|
251 |
|
252 nBuffer[0] = 0x0000; |
|
253 |
|
254 /* Due to the arguments causing a result to be <= 23 characters (+2 for NULL and minus), |
|
255 we don't need to reallocate the buffer. */ |
|
256 va_start(args, fmt); |
|
257 result = _vsnwprintf(nBuffer, STACK_BUFFER_SIZE, fmt, args); |
|
258 va_end(args); |
|
259 |
|
260 /* Just to make sure of the above statement, we add this assert */ |
|
261 U_ASSERT(result >=0); |
|
262 // The following code is not used because _vscwprintf isn't available on MinGW at the moment. |
|
263 /*if (result < 0) { |
|
264 int newLength; |
|
265 |
|
266 va_start(args, fmt); |
|
267 newLength = _vscwprintf(fmt, args); |
|
268 va_end(args); |
|
269 |
|
270 nBuffer = NEW_ARRAY(UChar, newLength + 1); |
|
271 |
|
272 va_start(args, fmt); |
|
273 result = _vsnwprintf(nBuffer, newLength + 1, fmt, args); |
|
274 va_end(args); |
|
275 }*/ |
|
276 |
|
277 // vswprintf is sensitive to the locale set by setlocale. For some locales |
|
278 // it doesn't use "." as the decimal separator, which is what GetNumberFormatW |
|
279 // and GetCurrencyFormatW both expect to see. |
|
280 // |
|
281 // To fix this, we scan over the string and replace the first non-digits, except |
|
282 // for a leading "-", with a "." |
|
283 // |
|
284 // Note: (nBuffer[0] == L'-') will evaluate to 1 if there is a leading '-' in the |
|
285 // number, and 0 otherwise. |
|
286 for (wchar_t *p = &nBuffer[nBuffer[0] == L'-']; *p != L'\0'; p += 1) { |
|
287 if (*p < L'0' || *p > L'9') { |
|
288 *p = L'.'; |
|
289 break; |
|
290 } |
|
291 } |
|
292 |
|
293 UChar stackBuffer[STACK_BUFFER_SIZE]; |
|
294 UChar *buffer = stackBuffer; |
|
295 FormatInfo formatInfo; |
|
296 |
|
297 formatInfo = *fFormatInfo; |
|
298 buffer[0] = 0x0000; |
|
299 |
|
300 if (fCurrency) { |
|
301 if (fFractionDigitsSet) { |
|
302 formatInfo.currency.NumDigits = (UINT) numDigits; |
|
303 } |
|
304 |
|
305 if (!isGroupingUsed()) { |
|
306 formatInfo.currency.Grouping = 0; |
|
307 } |
|
308 |
|
309 result = GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, buffer, STACK_BUFFER_SIZE); |
|
310 |
|
311 if (result == 0) { |
|
312 DWORD lastError = GetLastError(); |
|
313 |
|
314 if (lastError == ERROR_INSUFFICIENT_BUFFER) { |
|
315 int newLength = GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, NULL, 0); |
|
316 |
|
317 buffer = NEW_ARRAY(UChar, newLength); |
|
318 buffer[0] = 0x0000; |
|
319 GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, buffer, newLength); |
|
320 } |
|
321 } |
|
322 } else { |
|
323 if (fFractionDigitsSet) { |
|
324 formatInfo.number.NumDigits = (UINT) numDigits; |
|
325 } |
|
326 |
|
327 if (!isGroupingUsed()) { |
|
328 formatInfo.number.Grouping = 0; |
|
329 } |
|
330 |
|
331 result = GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, buffer, STACK_BUFFER_SIZE); |
|
332 |
|
333 if (result == 0) { |
|
334 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { |
|
335 int newLength = GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, NULL, 0); |
|
336 |
|
337 buffer = NEW_ARRAY(UChar, newLength); |
|
338 buffer[0] = 0x0000; |
|
339 GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, buffer, newLength); |
|
340 } |
|
341 } |
|
342 } |
|
343 |
|
344 appendTo.append(buffer, (int32_t) wcslen(buffer)); |
|
345 |
|
346 if (buffer != stackBuffer) { |
|
347 DELETE_ARRAY(buffer); |
|
348 } |
|
349 |
|
350 /*if (nBuffer != nStackBuffer) { |
|
351 DELETE_ARRAY(nBuffer); |
|
352 }*/ |
|
353 |
|
354 return appendTo; |
|
355 } |
|
356 |
|
357 U_NAMESPACE_END |
|
358 |
|
359 #endif /* #if !UCONFIG_NO_FORMATTING */ |
|
360 |
|
361 #endif // U_PLATFORM_USES_ONLY_WIN32_API |