2 ********************************************************************************
3 * Copyright (C) 2005-2006, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 ********************************************************************************
9 ********************************************************************************
12 #include "unicode/utypes.h"
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
)
100 DELETE_ARRAY(fmt
->lpThousandSep
);
101 DELETE_ARRAY(fmt
->lpDecimalSep
);
104 static void getCurrencyFormat(CURRENCYFMTW
*fmt
, int32_t lcid
)
108 GetLocaleInfoW(lcid
, LOCALE_RETURN_NUMBER
|LOCALE_ICURRDIGITS
, (LPWSTR
) &fmt
->NumDigits
, sizeof(UINT
));
109 GetLocaleInfoW(lcid
, LOCALE_RETURN_NUMBER
|LOCALE_ILZERO
, (LPWSTR
) &fmt
->LeadingZero
, sizeof(UINT
));
111 GetLocaleInfoA(lcid
, LOCALE_SMONGROUPING
, buf
, sizeof(buf
));
112 fmt
->Grouping
= getGrouping(buf
);
114 fmt
->lpDecimalSep
= NEW_ARRAY(UChar
, 6);
115 GetLocaleInfoW(lcid
, LOCALE_SMONDECIMALSEP
, fmt
->lpDecimalSep
, 6);
117 fmt
->lpThousandSep
= NEW_ARRAY(UChar
, 6);
118 GetLocaleInfoW(lcid
, LOCALE_SMONTHOUSANDSEP
, fmt
->lpThousandSep
, 6);
120 GetLocaleInfoW(lcid
, LOCALE_RETURN_NUMBER
|LOCALE_INEGCURR
, (LPWSTR
) &fmt
->NegativeOrder
, sizeof(UINT
));
121 GetLocaleInfoW(lcid
, LOCALE_RETURN_NUMBER
|LOCALE_ICURRENCY
, (LPWSTR
) &fmt
->PositiveOrder
, sizeof(UINT
));
123 fmt
->lpCurrencySymbol
= NEW_ARRAY(UChar
, 8);
124 GetLocaleInfoW(lcid
, LOCALE_SCURRENCY
, (LPWSTR
) fmt
->lpCurrencySymbol
, 8);
127 static void freeCurrencyFormat(CURRENCYFMTW
*fmt
)
129 DELETE_ARRAY(fmt
->lpCurrencySymbol
);
130 DELETE_ARRAY(fmt
->lpThousandSep
);
131 DELETE_ARRAY(fmt
->lpDecimalSep
);
134 // TODO: keep locale too?
135 Win32NumberFormat::Win32NumberFormat(const Locale
&locale
, UBool currency
, UErrorCode
&status
)
136 : NumberFormat(), fCurrency(currency
), fFractionDigitsSet(FALSE
)
138 if (!U_FAILURE(status
)) {
139 fLCID
= locale
.getLCID();
141 fFormatInfo
= (FormatInfo
*)uprv_malloc(sizeof(FormatInfo
));
144 getCurrencyFormat(&fFormatInfo
->currency
, fLCID
);
146 getNumberFormat(&fFormatInfo
->number
, fLCID
);
151 Win32NumberFormat::Win32NumberFormat(const Win32NumberFormat
&other
)
152 : NumberFormat(other
)
157 Win32NumberFormat::~Win32NumberFormat()
160 freeCurrencyFormat(&fFormatInfo
->currency
);
162 freeNumberFormat(&fFormatInfo
->number
);
165 uprv_free(fFormatInfo
);
168 Win32NumberFormat
&Win32NumberFormat::operator=(const Win32NumberFormat
&other
)
170 NumberFormat::operator=(other
);
172 this->fCurrency
= other
.fCurrency
;
173 this->fLCID
= other
.fLCID
;
174 this->fFractionDigitsSet
= other
.fFractionDigitsSet
;
177 freeCurrencyFormat(&fFormatInfo
->currency
);
178 getCurrencyFormat(&fFormatInfo
->currency
, fLCID
);
180 freeNumberFormat(&fFormatInfo
->number
);
181 getNumberFormat(&fFormatInfo
->number
, fLCID
);
187 Format
*Win32NumberFormat::clone(void) const
189 return new Win32NumberFormat(*this);
192 UnicodeString
& Win32NumberFormat::format(double number
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
194 return format(getMaximumFractionDigits(), appendTo
, L
"%.16f", number
);
197 UnicodeString
& Win32NumberFormat::format(int32_t number
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
199 return format(getMinimumFractionDigits(), appendTo
, L
"%I32d", number
);
202 UnicodeString
& Win32NumberFormat::format(int64_t number
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
204 return format(getMinimumFractionDigits(), appendTo
, L
"%I64d", number
);
207 // TODO: cache Locale and NumberFormat? Could keep locale passed to constructor...
208 void Win32NumberFormat::parse(const UnicodeString
& text
, Formattable
& result
, ParsePosition
& parsePosition
) const
210 UErrorCode status
= U_ZERO_ERROR
;
211 Locale
loc(uprv_convertToPosix(fLCID
, &status
));
212 NumberFormat
*nf
= fCurrency
? NumberFormat::createCurrencyInstance(loc
, status
) : NumberFormat::createInstance(loc
, status
);
214 nf
->parse(text
, result
, parsePosition
);
217 void Win32NumberFormat::setMaximumFractionDigits(int32_t newValue
)
219 fFractionDigitsSet
= TRUE
;
220 NumberFormat::setMaximumFractionDigits(newValue
);
223 void Win32NumberFormat::setMinimumFractionDigits(int32_t newValue
)
225 fFractionDigitsSet
= TRUE
;
226 NumberFormat::setMinimumFractionDigits(newValue
);
229 UnicodeString
&Win32NumberFormat::format(int32_t numDigits
, UnicodeString
&appendTo
, wchar_t *fmt
, ...) const
231 wchar_t nStackBuffer
[STACK_BUFFER_SIZE
];
232 wchar_t *nBuffer
= nStackBuffer
;
238 /* Due to the arguments causing a result to be <= 23 characters (+2 for NULL and minus),
239 we don't need to reallocate the buffer. */
241 result
= _vsnwprintf(nBuffer
, STACK_BUFFER_SIZE
, fmt
, args
);
244 /* Just to make sure of the above statement, we add this assert */
245 U_ASSERT(result
>=0);
246 // The following code is not used because _vscwprintf isn't available on MinGW at the moment.
251 newLength = _vscwprintf(fmt, args);
254 nBuffer = NEW_ARRAY(UChar, newLength + 1);
257 result = _vsnwprintf(nBuffer, newLength + 1, fmt, args);
261 // vswprintf is sensitive to the locale set by setlocale. For some locales
262 // it doesn't use "." as the decimal separator, which is what GetNumberFormatW
263 // and GetCurrencyFormatW both expect to see.
265 // To fix this, we scan over the string and replace the first non-digits, except
266 // for a leading "-", with a "."
268 // Note: (nBuffer[0] == L'-') will evaluate to 1 if there is a leading '-' in the
269 // number, and 0 otherwise.
270 for (wchar_t *p
= &nBuffer
[nBuffer
[0] == L
'-']; *p
!= L
'\0'; p
+= 1) {
271 if (*p
< L
'0' || *p
> L
'9') {
277 UChar stackBuffer
[STACK_BUFFER_SIZE
];
278 UChar
*buffer
= stackBuffer
;
279 FormatInfo formatInfo
;
281 formatInfo
= *fFormatInfo
;
285 if (fFractionDigitsSet
) {
286 formatInfo
.currency
.NumDigits
= (UINT
) numDigits
;
289 if (!isGroupingUsed()) {
290 formatInfo
.currency
.Grouping
= 0;
293 result
= GetCurrencyFormatW(fLCID
, 0, nBuffer
, &formatInfo
.currency
, buffer
, STACK_BUFFER_SIZE
);
296 DWORD lastError
= GetLastError();
298 if (lastError
== ERROR_INSUFFICIENT_BUFFER
) {
299 int newLength
= GetCurrencyFormatW(fLCID
, 0, nBuffer
, &formatInfo
.currency
, NULL
, 0);
301 buffer
= NEW_ARRAY(UChar
, newLength
);
303 GetCurrencyFormatW(fLCID
, 0, nBuffer
, &formatInfo
.currency
, buffer
, newLength
);
307 if (fFractionDigitsSet
) {
308 formatInfo
.number
.NumDigits
= (UINT
) numDigits
;
311 if (!isGroupingUsed()) {
312 formatInfo
.number
.Grouping
= 0;
315 result
= GetNumberFormatW(fLCID
, 0, nBuffer
, &formatInfo
.number
, buffer
, STACK_BUFFER_SIZE
);
318 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
) {
319 int newLength
= GetNumberFormatW(fLCID
, 0, nBuffer
, &formatInfo
.number
, NULL
, 0);
321 buffer
= NEW_ARRAY(UChar
, newLength
);
323 GetNumberFormatW(fLCID
, 0, nBuffer
, &formatInfo
.number
, buffer
, newLength
);
328 appendTo
.append(buffer
, (int32_t) wcslen(buffer
));
330 if (buffer
!= stackBuffer
) {
331 DELETE_ARRAY(buffer
);
334 /*if (nBuffer != nStackBuffer) {
335 DELETE_ARRAY(nBuffer);
343 #endif /* #if !UCONFIG_NO_FORMATTING */
345 #endif // #ifdef U_WINDOWS