2 ********************************************************************************
3 * Copyright (C) 2005-2011, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 ********************************************************************************
9 ********************************************************************************
12 #include "unicode/utypes.h"
14 #if U_PLATFORM_USES_ONLY_WIN32_API
16 #if !UCONFIG_NO_FORMATTING
20 #include "unicode/format.h"
21 #include "unicode/numfmt.h"
22 #include "unicode/locid.h"
23 #include "unicode/ustring.h"
29 # define WIN32_LEAN_AND_MEAN
43 CURRENCYFMTW currency
;
46 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Win32NumberFormat
)
48 #define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type))
49 #define DELETE_ARRAY(array) uprv_free((void *) (array))
51 #define STACK_BUFFER_SIZE 32
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)
59 static UINT
getGrouping(const char *grouping
)
64 for (s
= grouping
; *s
!= '\0'; s
+= 1) {
65 if (*s
> '0' && *s
< '9') {
66 g
= g
* 10 + (*s
- '0');
67 } else if (*s
!= ';') {
79 static void getNumberFormat(NUMBERFMTW
*fmt
, int32_t lcid
)
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
));
86 GetLocaleInfoA(lcid
, LOCALE_SGROUPING
, buf
, 10);
87 fmt
->Grouping
= getGrouping(buf
);
89 fmt
->lpDecimalSep
= NEW_ARRAY(UChar
, 6);
90 GetLocaleInfoW(lcid
, LOCALE_SDECIMAL
, fmt
->lpDecimalSep
, 6);
92 fmt
->lpThousandSep
= NEW_ARRAY(UChar
, 6);
93 GetLocaleInfoW(lcid
, LOCALE_STHOUSAND
, fmt
->lpThousandSep
, 6);
95 GetLocaleInfoW(lcid
, LOCALE_RETURN_NUMBER
|LOCALE_INEGNUMBER
, (LPWSTR
) &fmt
->NegativeOrder
, sizeof(UINT
));
98 static void freeNumberFormat(NUMBERFMTW
*fmt
)
101 DELETE_ARRAY(fmt
->lpThousandSep
);
102 DELETE_ARRAY(fmt
->lpDecimalSep
);
106 static void getCurrencyFormat(CURRENCYFMTW
*fmt
, int32_t lcid
)
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
));
113 GetLocaleInfoA(lcid
, LOCALE_SMONGROUPING
, buf
, sizeof(buf
));
114 fmt
->Grouping
= getGrouping(buf
);
116 fmt
->lpDecimalSep
= NEW_ARRAY(UChar
, 6);
117 GetLocaleInfoW(lcid
, LOCALE_SMONDECIMALSEP
, fmt
->lpDecimalSep
, 6);
119 fmt
->lpThousandSep
= NEW_ARRAY(UChar
, 6);
120 GetLocaleInfoW(lcid
, LOCALE_SMONTHOUSANDSEP
, fmt
->lpThousandSep
, 6);
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
));
125 fmt
->lpCurrencySymbol
= NEW_ARRAY(UChar
, 8);
126 GetLocaleInfoW(lcid
, LOCALE_SCURRENCY
, (LPWSTR
) fmt
->lpCurrencySymbol
, 8);
129 static void freeCurrencyFormat(CURRENCYFMTW
*fmt
)
132 DELETE_ARRAY(fmt
->lpCurrencySymbol
);
133 DELETE_ARRAY(fmt
->lpThousandSep
);
134 DELETE_ARRAY(fmt
->lpDecimalSep
);
138 // TODO: keep locale too?
139 Win32NumberFormat::Win32NumberFormat(const Locale
&locale
, UBool currency
, UErrorCode
&status
)
140 : NumberFormat(), fCurrency(currency
), fFractionDigitsSet(FALSE
), fFormatInfo(NULL
)
142 if (!U_FAILURE(status
)) {
143 fLCID
= locale
.getLCID();
145 fFormatInfo
= (FormatInfo
*)uprv_malloc(sizeof(FormatInfo
));
148 getCurrencyFormat(&fFormatInfo
->currency
, fLCID
);
150 getNumberFormat(&fFormatInfo
->number
, fLCID
);
155 Win32NumberFormat::Win32NumberFormat(const Win32NumberFormat
&other
)
156 : NumberFormat(other
), fFormatInfo((FormatInfo
*)uprv_malloc(sizeof(FormatInfo
)))
158 if (fFormatInfo
!= NULL
) {
159 uprv_memset(fFormatInfo
, 0, sizeof(*fFormatInfo
));
164 Win32NumberFormat::~Win32NumberFormat()
166 if (fFormatInfo
!= NULL
) {
168 freeCurrencyFormat(&fFormatInfo
->currency
);
170 freeNumberFormat(&fFormatInfo
->number
);
173 uprv_free(fFormatInfo
);
177 Win32NumberFormat
&Win32NumberFormat::operator=(const Win32NumberFormat
&other
)
179 NumberFormat::operator=(other
);
181 this->fCurrency
= other
.fCurrency
;
182 this->fLCID
= other
.fLCID
;
183 this->fFractionDigitsSet
= other
.fFractionDigitsSet
;
186 freeCurrencyFormat(&fFormatInfo
->currency
);
187 getCurrencyFormat(&fFormatInfo
->currency
, fLCID
);
189 freeNumberFormat(&fFormatInfo
->number
);
190 getNumberFormat(&fFormatInfo
->number
, fLCID
);
196 Format
*Win32NumberFormat::clone(void) const
198 return new Win32NumberFormat(*this);
201 UnicodeString
& Win32NumberFormat::format(double number
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
203 return format(getMaximumFractionDigits(), appendTo
, L
"%.16f", number
);
206 UnicodeString
& Win32NumberFormat::format(int32_t number
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
208 return format(getMinimumFractionDigits(), appendTo
, L
"%I32d", number
);
211 UnicodeString
& Win32NumberFormat::format(int64_t number
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
213 return format(getMinimumFractionDigits(), appendTo
, L
"%I64d", number
);
216 // TODO: cache Locale and NumberFormat? Could keep locale passed to constructor...
217 void Win32NumberFormat::parse(const UnicodeString
& text
, Formattable
& result
, ParsePosition
& parsePosition
) const
219 UErrorCode status
= U_ZERO_ERROR
;
220 Locale
loc(uprv_convertToPosix(fLCID
, &status
));
221 NumberFormat
*nf
= fCurrency
? NumberFormat::createCurrencyInstance(loc
, status
) : NumberFormat::createInstance(loc
, status
);
223 nf
->parse(text
, result
, parsePosition
);
226 void Win32NumberFormat::setMaximumFractionDigits(int32_t newValue
)
228 fFractionDigitsSet
= TRUE
;
229 NumberFormat::setMaximumFractionDigits(newValue
);
232 void Win32NumberFormat::setMinimumFractionDigits(int32_t newValue
)
234 fFractionDigitsSet
= TRUE
;
235 NumberFormat::setMinimumFractionDigits(newValue
);
238 UnicodeString
&Win32NumberFormat::format(int32_t numDigits
, UnicodeString
&appendTo
, wchar_t *fmt
, ...) const
240 wchar_t nStackBuffer
[STACK_BUFFER_SIZE
];
241 wchar_t *nBuffer
= nStackBuffer
;
247 /* Due to the arguments causing a result to be <= 23 characters (+2 for NULL and minus),
248 we don't need to reallocate the buffer. */
250 result
= _vsnwprintf(nBuffer
, STACK_BUFFER_SIZE
, fmt
, args
);
253 /* Just to make sure of the above statement, we add this assert */
254 U_ASSERT(result
>=0);
255 // The following code is not used because _vscwprintf isn't available on MinGW at the moment.
260 newLength = _vscwprintf(fmt, args);
263 nBuffer = NEW_ARRAY(UChar, newLength + 1);
266 result = _vsnwprintf(nBuffer, newLength + 1, fmt, args);
270 // vswprintf is sensitive to the locale set by setlocale. For some locales
271 // it doesn't use "." as the decimal separator, which is what GetNumberFormatW
272 // and GetCurrencyFormatW both expect to see.
274 // To fix this, we scan over the string and replace the first non-digits, except
275 // for a leading "-", with a "."
277 // Note: (nBuffer[0] == L'-') will evaluate to 1 if there is a leading '-' in the
278 // number, and 0 otherwise.
279 for (wchar_t *p
= &nBuffer
[nBuffer
[0] == L
'-']; *p
!= L
'\0'; p
+= 1) {
280 if (*p
< L
'0' || *p
> L
'9') {
286 UChar stackBuffer
[STACK_BUFFER_SIZE
];
287 UChar
*buffer
= stackBuffer
;
288 FormatInfo formatInfo
;
290 formatInfo
= *fFormatInfo
;
294 if (fFractionDigitsSet
) {
295 formatInfo
.currency
.NumDigits
= (UINT
) numDigits
;
298 if (!isGroupingUsed()) {
299 formatInfo
.currency
.Grouping
= 0;
302 result
= GetCurrencyFormatW(fLCID
, 0, nBuffer
, &formatInfo
.currency
, buffer
, STACK_BUFFER_SIZE
);
305 DWORD lastError
= GetLastError();
307 if (lastError
== ERROR_INSUFFICIENT_BUFFER
) {
308 int newLength
= GetCurrencyFormatW(fLCID
, 0, nBuffer
, &formatInfo
.currency
, NULL
, 0);
310 buffer
= NEW_ARRAY(UChar
, newLength
);
312 GetCurrencyFormatW(fLCID
, 0, nBuffer
, &formatInfo
.currency
, buffer
, newLength
);
316 if (fFractionDigitsSet
) {
317 formatInfo
.number
.NumDigits
= (UINT
) numDigits
;
320 if (!isGroupingUsed()) {
321 formatInfo
.number
.Grouping
= 0;
324 result
= GetNumberFormatW(fLCID
, 0, nBuffer
, &formatInfo
.number
, buffer
, STACK_BUFFER_SIZE
);
327 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
) {
328 int newLength
= GetNumberFormatW(fLCID
, 0, nBuffer
, &formatInfo
.number
, NULL
, 0);
330 buffer
= NEW_ARRAY(UChar
, newLength
);
332 GetNumberFormatW(fLCID
, 0, nBuffer
, &formatInfo
.number
, buffer
, newLength
);
337 appendTo
.append(buffer
, (int32_t) wcslen(buffer
));
339 if (buffer
!= stackBuffer
) {
340 DELETE_ARRAY(buffer
);
343 /*if (nBuffer != nStackBuffer) {
344 DELETE_ARRAY(nBuffer);
352 #endif /* #if !UCONFIG_NO_FORMATTING */
354 #endif // U_PLATFORM_USES_ONLY_WIN32_API