OLD | NEW |
(Empty) | |
| 1 /* |
| 2 ******************************************************************************** |
| 3 * Copyright (C) 2005-2007, 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 #ifdef U_WINDOWS |
| 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->Num
Digits, sizeof(UINT)); |
| 84 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->Lea
dingZero, 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->Lead
ingZero, 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->N
egativeOrder, sizeof(UINT)); |
| 123 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ICURRENCY, (LPWSTR) &fmt->P
ositiveOrder, 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 // TODO: keep locale too? |
| 139 Win32NumberFormat::Win32NumberFormat(const Locale &locale, UBool currency, UErro
rCode &status) |
| 140 : NumberFormat(), fCurrency(currency), fFractionDigitsSet(FALSE), fFormatInfo(
NULL) |
| 141 { |
| 142 if (!U_FAILURE(status)) { |
| 143 fLCID = locale.getLCID(); |
| 144 |
| 145 fFormatInfo = (FormatInfo*)uprv_malloc(sizeof(FormatInfo)); |
| 146 |
| 147 if (fCurrency) { |
| 148 getCurrencyFormat(&fFormatInfo->currency, fLCID); |
| 149 } else { |
| 150 getNumberFormat(&fFormatInfo->number, fLCID); |
| 151 } |
| 152 } |
| 153 } |
| 154 |
| 155 Win32NumberFormat::Win32NumberFormat(const Win32NumberFormat &other) |
| 156 : NumberFormat(other), fFormatInfo((FormatInfo*)uprv_malloc(sizeof(FormatInfo)
)) |
| 157 { |
| 158 if (fFormatInfo != NULL) { |
| 159 uprv_memset(fFormatInfo, 0, sizeof(*fFormatInfo)); |
| 160 } |
| 161 *this = other; |
| 162 } |
| 163 |
| 164 Win32NumberFormat::~Win32NumberFormat() |
| 165 { |
| 166 if (fFormatInfo != NULL) { |
| 167 if (fCurrency) { |
| 168 freeCurrencyFormat(&fFormatInfo->currency); |
| 169 } else { |
| 170 freeNumberFormat(&fFormatInfo->number); |
| 171 } |
| 172 |
| 173 uprv_free(fFormatInfo); |
| 174 } |
| 175 } |
| 176 |
| 177 Win32NumberFormat &Win32NumberFormat::operator=(const Win32NumberFormat &other) |
| 178 { |
| 179 NumberFormat::operator=(other); |
| 180 |
| 181 this->fCurrency = other.fCurrency; |
| 182 this->fLCID = other.fLCID; |
| 183 this->fFractionDigitsSet = other.fFractionDigitsSet; |
| 184 |
| 185 if (fCurrency) { |
| 186 freeCurrencyFormat(&fFormatInfo->currency); |
| 187 getCurrencyFormat(&fFormatInfo->currency, fLCID); |
| 188 } else { |
| 189 freeNumberFormat(&fFormatInfo->number); |
| 190 getNumberFormat(&fFormatInfo->number, fLCID); |
| 191 } |
| 192 |
| 193 return *this; |
| 194 } |
| 195 |
| 196 Format *Win32NumberFormat::clone(void) const |
| 197 { |
| 198 return new Win32NumberFormat(*this); |
| 199 } |
| 200 |
| 201 UnicodeString& Win32NumberFormat::format(double number, UnicodeString& appendTo,
FieldPosition& pos) const |
| 202 { |
| 203 return format(getMaximumFractionDigits(), appendTo, L"%.16f", number); |
| 204 } |
| 205 |
| 206 UnicodeString& Win32NumberFormat::format(int32_t number, UnicodeString& appendTo
, FieldPosition& pos) const |
| 207 { |
| 208 return format(getMinimumFractionDigits(), appendTo, L"%I32d", number); |
| 209 } |
| 210 |
| 211 UnicodeString& Win32NumberFormat::format(int64_t number, UnicodeString& appendTo
, FieldPosition& pos) const |
| 212 { |
| 213 return format(getMinimumFractionDigits(), appendTo, L"%I64d", number); |
| 214 } |
| 215 |
| 216 // TODO: cache Locale and NumberFormat? Could keep locale passed to constructor.
.. |
| 217 void Win32NumberFormat::parse(const UnicodeString& text, Formattable& result, Pa
rsePosition& parsePosition) const |
| 218 { |
| 219 UErrorCode status = U_ZERO_ERROR; |
| 220 Locale loc(uprv_convertToPosix(fLCID, &status)); |
| 221 NumberFormat *nf = fCurrency? NumberFormat::createCurrencyInstance(loc, stat
us) : NumberFormat::createInstance(loc, status); |
| 222 |
| 223 nf->parse(text, result, parsePosition); |
| 224 delete nf; |
| 225 } |
| 226 void Win32NumberFormat::setMaximumFractionDigits(int32_t newValue) |
| 227 { |
| 228 fFractionDigitsSet = TRUE; |
| 229 NumberFormat::setMaximumFractionDigits(newValue); |
| 230 } |
| 231 |
| 232 void Win32NumberFormat::setMinimumFractionDigits(int32_t newValue) |
| 233 { |
| 234 fFractionDigitsSet = TRUE; |
| 235 NumberFormat::setMinimumFractionDigits(newValue); |
| 236 } |
| 237 |
| 238 UnicodeString &Win32NumberFormat::format(int32_t numDigits, UnicodeString &appen
dTo, wchar_t *fmt, ...) const |
| 239 { |
| 240 wchar_t nStackBuffer[STACK_BUFFER_SIZE]; |
| 241 wchar_t *nBuffer = nStackBuffer; |
| 242 va_list args; |
| 243 int result; |
| 244 |
| 245 nBuffer[0] = 0x0000; |
| 246 |
| 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. */ |
| 249 va_start(args, fmt); |
| 250 result = _vsnwprintf(nBuffer, STACK_BUFFER_SIZE, fmt, args); |
| 251 va_end(args); |
| 252 |
| 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 Min
GW at the moment. |
| 256 /*if (result < 0) { |
| 257 int newLength; |
| 258 |
| 259 va_start(args, fmt); |
| 260 newLength = _vscwprintf(fmt, args); |
| 261 va_end(args); |
| 262 |
| 263 nBuffer = NEW_ARRAY(UChar, newLength + 1); |
| 264 |
| 265 va_start(args, fmt); |
| 266 result = _vsnwprintf(nBuffer, newLength + 1, fmt, args); |
| 267 va_end(args); |
| 268 }*/ |
| 269 |
| 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 GetNumberForma
tW |
| 272 // and GetCurrencyFormatW both expect to see. |
| 273 // |
| 274 // To fix this, we scan over the string and replace the first non-digits, ex
cept |
| 275 // for a leading "-", with a "." |
| 276 // |
| 277 // Note: (nBuffer[0] == L'-') will evaluate to 1 if there is a leading '-' i
n 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') { |
| 281 *p = L'.'; |
| 282 break; |
| 283 } |
| 284 } |
| 285 |
| 286 UChar stackBuffer[STACK_BUFFER_SIZE]; |
| 287 UChar *buffer = stackBuffer; |
| 288 FormatInfo formatInfo; |
| 289 |
| 290 formatInfo = *fFormatInfo; |
| 291 buffer[0] = 0x0000; |
| 292 |
| 293 if (fCurrency) { |
| 294 if (fFractionDigitsSet) { |
| 295 formatInfo.currency.NumDigits = (UINT) numDigits; |
| 296 } |
| 297 |
| 298 if (!isGroupingUsed()) { |
| 299 formatInfo.currency.Grouping = 0; |
| 300 } |
| 301 |
| 302 result = GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, buf
fer, STACK_BUFFER_SIZE); |
| 303 |
| 304 if (result == 0) { |
| 305 DWORD lastError = GetLastError(); |
| 306 |
| 307 if (lastError == ERROR_INSUFFICIENT_BUFFER) { |
| 308 int newLength = GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInf
o.currency, NULL, 0); |
| 309 |
| 310 buffer = NEW_ARRAY(UChar, newLength); |
| 311 buffer[0] = 0x0000; |
| 312 GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, buf
fer, newLength); |
| 313 } |
| 314 } |
| 315 } else { |
| 316 if (fFractionDigitsSet) { |
| 317 formatInfo.number.NumDigits = (UINT) numDigits; |
| 318 } |
| 319 |
| 320 if (!isGroupingUsed()) { |
| 321 formatInfo.number.Grouping = 0; |
| 322 } |
| 323 |
| 324 result = GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, buffer,
STACK_BUFFER_SIZE); |
| 325 |
| 326 if (result == 0) { |
| 327 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { |
| 328 int newLength = GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.
number, NULL, 0); |
| 329 |
| 330 buffer = NEW_ARRAY(UChar, newLength); |
| 331 buffer[0] = 0x0000; |
| 332 GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, buffer,
newLength); |
| 333 } |
| 334 } |
| 335 } |
| 336 |
| 337 appendTo.append(buffer, (int32_t) wcslen(buffer)); |
| 338 |
| 339 if (buffer != stackBuffer) { |
| 340 DELETE_ARRAY(buffer); |
| 341 } |
| 342 |
| 343 /*if (nBuffer != nStackBuffer) { |
| 344 DELETE_ARRAY(nBuffer); |
| 345 }*/ |
| 346 |
| 347 return appendTo; |
| 348 } |
| 349 |
| 350 U_NAMESPACE_END |
| 351 |
| 352 #endif /* #if !UCONFIG_NO_FORMATTING */ |
| 353 |
| 354 #endif // #ifdef U_WINDOWS |
OLD | NEW |