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->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 // TODO: keep locale too? 139 Win32NumberFormat::Win32NumberFormat(const Locale &locale, UBool currency, UErrorCode &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, ParsePosition& parsePosition) const 218 { 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); 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 &appendTo, 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 MinGW 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 GetNumberFormatW 272 // and GetCurrencyFormatW both expect to see. 273 // 274 // To fix this, we scan over the string and replace the first non-digits, except 275 // for a leading "-", with a "." 276 // 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') { 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, buffer, 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, &formatInfo.currency, NULL, 0); 309 310 buffer = NEW_ARRAY(UChar, newLength); 311 buffer[0] = 0x0000; 312 GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, buffer, 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 355