1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/winnmfmt.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,361 @@ 1.4 +/* 1.5 +******************************************************************************** 1.6 +* Copyright (C) 2005-2013, International Business Machines 1.7 +* Corporation and others. All Rights Reserved. 1.8 +******************************************************************************** 1.9 +* 1.10 +* File WINNMFMT.CPP 1.11 +* 1.12 +******************************************************************************** 1.13 +*/ 1.14 + 1.15 +#include "unicode/utypes.h" 1.16 + 1.17 +#if U_PLATFORM_USES_ONLY_WIN32_API 1.18 + 1.19 +#if !UCONFIG_NO_FORMATTING 1.20 + 1.21 +#include "winnmfmt.h" 1.22 + 1.23 +#include "unicode/format.h" 1.24 +#include "unicode/numfmt.h" 1.25 +#include "unicode/locid.h" 1.26 +#include "unicode/ustring.h" 1.27 + 1.28 +#include "cmemory.h" 1.29 +#include "uassert.h" 1.30 +#include "locmap.h" 1.31 + 1.32 +# define WIN32_LEAN_AND_MEAN 1.33 +# define VC_EXTRALEAN 1.34 +# define NOUSER 1.35 +# define NOSERVICE 1.36 +# define NOIME 1.37 +# define NOMCX 1.38 +#include <windows.h> 1.39 +#include <stdio.h> 1.40 + 1.41 +U_NAMESPACE_BEGIN 1.42 + 1.43 +union FormatInfo 1.44 +{ 1.45 + NUMBERFMTW number; 1.46 + CURRENCYFMTW currency; 1.47 +}; 1.48 + 1.49 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Win32NumberFormat) 1.50 + 1.51 +#define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type)) 1.52 +#define DELETE_ARRAY(array) uprv_free((void *) (array)) 1.53 + 1.54 +#define STACK_BUFFER_SIZE 32 1.55 + 1.56 +/* 1.57 + * Turns a string of the form "3;2;0" into the grouping UINT 1.58 + * needed for NUMBERFMT and CURRENCYFMT. If the string does not 1.59 + * end in ";0" then the return value should be multiplied by 10. 1.60 + * (e.g. "3" => 30, "3;2" => 320) 1.61 + */ 1.62 +static UINT getGrouping(const char *grouping) 1.63 +{ 1.64 + UINT g = 0; 1.65 + const char *s; 1.66 + 1.67 + for (s = grouping; *s != '\0'; s += 1) { 1.68 + if (*s > '0' && *s < '9') { 1.69 + g = g * 10 + (*s - '0'); 1.70 + } else if (*s != ';') { 1.71 + break; 1.72 + } 1.73 + } 1.74 + 1.75 + if (*s != '0') { 1.76 + g *= 10; 1.77 + } 1.78 + 1.79 + return g; 1.80 +} 1.81 + 1.82 +static void getNumberFormat(NUMBERFMTW *fmt, int32_t lcid) 1.83 +{ 1.84 + char buf[10]; 1.85 + 1.86 + GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_IDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT)); 1.87 + GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->LeadingZero, sizeof(UINT)); 1.88 + 1.89 + GetLocaleInfoA(lcid, LOCALE_SGROUPING, buf, 10); 1.90 + fmt->Grouping = getGrouping(buf); 1.91 + 1.92 + fmt->lpDecimalSep = NEW_ARRAY(UChar, 6); 1.93 + GetLocaleInfoW(lcid, LOCALE_SDECIMAL, fmt->lpDecimalSep, 6); 1.94 + 1.95 + fmt->lpThousandSep = NEW_ARRAY(UChar, 6); 1.96 + GetLocaleInfoW(lcid, LOCALE_STHOUSAND, fmt->lpThousandSep, 6); 1.97 + 1.98 + GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_INEGNUMBER, (LPWSTR) &fmt->NegativeOrder, sizeof(UINT)); 1.99 +} 1.100 + 1.101 +static void freeNumberFormat(NUMBERFMTW *fmt) 1.102 +{ 1.103 + if (fmt != NULL) { 1.104 + DELETE_ARRAY(fmt->lpThousandSep); 1.105 + DELETE_ARRAY(fmt->lpDecimalSep); 1.106 + } 1.107 +} 1.108 + 1.109 +static void getCurrencyFormat(CURRENCYFMTW *fmt, int32_t lcid) 1.110 +{ 1.111 + char buf[10]; 1.112 + 1.113 + GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ICURRDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT)); 1.114 + GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->LeadingZero, sizeof(UINT)); 1.115 + 1.116 + GetLocaleInfoA(lcid, LOCALE_SMONGROUPING, buf, sizeof(buf)); 1.117 + fmt->Grouping = getGrouping(buf); 1.118 + 1.119 + fmt->lpDecimalSep = NEW_ARRAY(UChar, 6); 1.120 + GetLocaleInfoW(lcid, LOCALE_SMONDECIMALSEP, fmt->lpDecimalSep, 6); 1.121 + 1.122 + fmt->lpThousandSep = NEW_ARRAY(UChar, 6); 1.123 + GetLocaleInfoW(lcid, LOCALE_SMONTHOUSANDSEP, fmt->lpThousandSep, 6); 1.124 + 1.125 + GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_INEGCURR, (LPWSTR) &fmt->NegativeOrder, sizeof(UINT)); 1.126 + GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ICURRENCY, (LPWSTR) &fmt->PositiveOrder, sizeof(UINT)); 1.127 + 1.128 + fmt->lpCurrencySymbol = NEW_ARRAY(UChar, 8); 1.129 + GetLocaleInfoW(lcid, LOCALE_SCURRENCY, (LPWSTR) fmt->lpCurrencySymbol, 8); 1.130 +} 1.131 + 1.132 +static void freeCurrencyFormat(CURRENCYFMTW *fmt) 1.133 +{ 1.134 + if (fmt != NULL) { 1.135 + DELETE_ARRAY(fmt->lpCurrencySymbol); 1.136 + DELETE_ARRAY(fmt->lpThousandSep); 1.137 + DELETE_ARRAY(fmt->lpDecimalSep); 1.138 + } 1.139 +} 1.140 + 1.141 +Win32NumberFormat::Win32NumberFormat(const Locale &locale, UBool currency, UErrorCode &status) 1.142 + : NumberFormat(), fCurrency(currency), fFractionDigitsSet(FALSE), fFormatInfo(NULL) 1.143 +{ 1.144 + if (!U_FAILURE(status)) { 1.145 + fLCID = locale.getLCID(); 1.146 + 1.147 + // Resolve actual locale to be used later 1.148 + UErrorCode tmpsts = U_ZERO_ERROR; 1.149 + char tmpLocID[ULOC_FULLNAME_CAPACITY]; 1.150 + int32_t len = uloc_getLocaleForLCID(fLCID, tmpLocID, sizeof(tmpLocID)/sizeof(tmpLocID[0]) - 1, &tmpsts); 1.151 + if (U_SUCCESS(tmpsts)) { 1.152 + tmpLocID[len] = 0; 1.153 + fLocale = Locale((const char*)tmpLocID); 1.154 + } 1.155 + 1.156 + fFormatInfo = (FormatInfo*)uprv_malloc(sizeof(FormatInfo)); 1.157 + 1.158 + if (fCurrency) { 1.159 + getCurrencyFormat(&fFormatInfo->currency, fLCID); 1.160 + } else { 1.161 + getNumberFormat(&fFormatInfo->number, fLCID); 1.162 + } 1.163 + } 1.164 +} 1.165 + 1.166 +Win32NumberFormat::Win32NumberFormat(const Win32NumberFormat &other) 1.167 + : NumberFormat(other), fFormatInfo((FormatInfo*)uprv_malloc(sizeof(FormatInfo))) 1.168 +{ 1.169 + if (fFormatInfo != NULL) { 1.170 + uprv_memset(fFormatInfo, 0, sizeof(*fFormatInfo)); 1.171 + } 1.172 + *this = other; 1.173 +} 1.174 + 1.175 +Win32NumberFormat::~Win32NumberFormat() 1.176 +{ 1.177 + if (fFormatInfo != NULL) { 1.178 + if (fCurrency) { 1.179 + freeCurrencyFormat(&fFormatInfo->currency); 1.180 + } else { 1.181 + freeNumberFormat(&fFormatInfo->number); 1.182 + } 1.183 + 1.184 + uprv_free(fFormatInfo); 1.185 + } 1.186 +} 1.187 + 1.188 +Win32NumberFormat &Win32NumberFormat::operator=(const Win32NumberFormat &other) 1.189 +{ 1.190 + NumberFormat::operator=(other); 1.191 + 1.192 + this->fCurrency = other.fCurrency; 1.193 + this->fLocale = other.fLocale; 1.194 + this->fLCID = other.fLCID; 1.195 + this->fFractionDigitsSet = other.fFractionDigitsSet; 1.196 + 1.197 + if (fCurrency) { 1.198 + freeCurrencyFormat(&fFormatInfo->currency); 1.199 + getCurrencyFormat(&fFormatInfo->currency, fLCID); 1.200 + } else { 1.201 + freeNumberFormat(&fFormatInfo->number); 1.202 + getNumberFormat(&fFormatInfo->number, fLCID); 1.203 + } 1.204 + 1.205 + return *this; 1.206 +} 1.207 + 1.208 +Format *Win32NumberFormat::clone(void) const 1.209 +{ 1.210 + return new Win32NumberFormat(*this); 1.211 +} 1.212 + 1.213 +UnicodeString& Win32NumberFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos) const 1.214 +{ 1.215 + return format(getMaximumFractionDigits(), appendTo, L"%.16f", number); 1.216 +} 1.217 + 1.218 +UnicodeString& Win32NumberFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos) const 1.219 +{ 1.220 + return format(getMinimumFractionDigits(), appendTo, L"%I32d", number); 1.221 +} 1.222 + 1.223 +UnicodeString& Win32NumberFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos) const 1.224 +{ 1.225 + return format(getMinimumFractionDigits(), appendTo, L"%I64d", number); 1.226 +} 1.227 + 1.228 +void Win32NumberFormat::parse(const UnicodeString& text, Formattable& result, ParsePosition& parsePosition) const 1.229 +{ 1.230 + UErrorCode status = U_ZERO_ERROR; 1.231 + NumberFormat *nf = fCurrency? NumberFormat::createCurrencyInstance(fLocale, status) : NumberFormat::createInstance(fLocale, status); 1.232 + 1.233 + nf->parse(text, result, parsePosition); 1.234 + delete nf; 1.235 +} 1.236 +void Win32NumberFormat::setMaximumFractionDigits(int32_t newValue) 1.237 +{ 1.238 + fFractionDigitsSet = TRUE; 1.239 + NumberFormat::setMaximumFractionDigits(newValue); 1.240 +} 1.241 + 1.242 +void Win32NumberFormat::setMinimumFractionDigits(int32_t newValue) 1.243 +{ 1.244 + fFractionDigitsSet = TRUE; 1.245 + NumberFormat::setMinimumFractionDigits(newValue); 1.246 +} 1.247 + 1.248 +UnicodeString &Win32NumberFormat::format(int32_t numDigits, UnicodeString &appendTo, wchar_t *fmt, ...) const 1.249 +{ 1.250 + wchar_t nStackBuffer[STACK_BUFFER_SIZE]; 1.251 + wchar_t *nBuffer = nStackBuffer; 1.252 + va_list args; 1.253 + int result; 1.254 + 1.255 + nBuffer[0] = 0x0000; 1.256 + 1.257 + /* Due to the arguments causing a result to be <= 23 characters (+2 for NULL and minus), 1.258 + we don't need to reallocate the buffer. */ 1.259 + va_start(args, fmt); 1.260 + result = _vsnwprintf(nBuffer, STACK_BUFFER_SIZE, fmt, args); 1.261 + va_end(args); 1.262 + 1.263 + /* Just to make sure of the above statement, we add this assert */ 1.264 + U_ASSERT(result >=0); 1.265 + // The following code is not used because _vscwprintf isn't available on MinGW at the moment. 1.266 + /*if (result < 0) { 1.267 + int newLength; 1.268 + 1.269 + va_start(args, fmt); 1.270 + newLength = _vscwprintf(fmt, args); 1.271 + va_end(args); 1.272 + 1.273 + nBuffer = NEW_ARRAY(UChar, newLength + 1); 1.274 + 1.275 + va_start(args, fmt); 1.276 + result = _vsnwprintf(nBuffer, newLength + 1, fmt, args); 1.277 + va_end(args); 1.278 + }*/ 1.279 + 1.280 + // vswprintf is sensitive to the locale set by setlocale. For some locales 1.281 + // it doesn't use "." as the decimal separator, which is what GetNumberFormatW 1.282 + // and GetCurrencyFormatW both expect to see. 1.283 + // 1.284 + // To fix this, we scan over the string and replace the first non-digits, except 1.285 + // for a leading "-", with a "." 1.286 + // 1.287 + // Note: (nBuffer[0] == L'-') will evaluate to 1 if there is a leading '-' in the 1.288 + // number, and 0 otherwise. 1.289 + for (wchar_t *p = &nBuffer[nBuffer[0] == L'-']; *p != L'\0'; p += 1) { 1.290 + if (*p < L'0' || *p > L'9') { 1.291 + *p = L'.'; 1.292 + break; 1.293 + } 1.294 + } 1.295 + 1.296 + UChar stackBuffer[STACK_BUFFER_SIZE]; 1.297 + UChar *buffer = stackBuffer; 1.298 + FormatInfo formatInfo; 1.299 + 1.300 + formatInfo = *fFormatInfo; 1.301 + buffer[0] = 0x0000; 1.302 + 1.303 + if (fCurrency) { 1.304 + if (fFractionDigitsSet) { 1.305 + formatInfo.currency.NumDigits = (UINT) numDigits; 1.306 + } 1.307 + 1.308 + if (!isGroupingUsed()) { 1.309 + formatInfo.currency.Grouping = 0; 1.310 + } 1.311 + 1.312 + result = GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, buffer, STACK_BUFFER_SIZE); 1.313 + 1.314 + if (result == 0) { 1.315 + DWORD lastError = GetLastError(); 1.316 + 1.317 + if (lastError == ERROR_INSUFFICIENT_BUFFER) { 1.318 + int newLength = GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, NULL, 0); 1.319 + 1.320 + buffer = NEW_ARRAY(UChar, newLength); 1.321 + buffer[0] = 0x0000; 1.322 + GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, buffer, newLength); 1.323 + } 1.324 + } 1.325 + } else { 1.326 + if (fFractionDigitsSet) { 1.327 + formatInfo.number.NumDigits = (UINT) numDigits; 1.328 + } 1.329 + 1.330 + if (!isGroupingUsed()) { 1.331 + formatInfo.number.Grouping = 0; 1.332 + } 1.333 + 1.334 + result = GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, buffer, STACK_BUFFER_SIZE); 1.335 + 1.336 + if (result == 0) { 1.337 + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 1.338 + int newLength = GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, NULL, 0); 1.339 + 1.340 + buffer = NEW_ARRAY(UChar, newLength); 1.341 + buffer[0] = 0x0000; 1.342 + GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, buffer, newLength); 1.343 + } 1.344 + } 1.345 + } 1.346 + 1.347 + appendTo.append(buffer, (int32_t) wcslen(buffer)); 1.348 + 1.349 + if (buffer != stackBuffer) { 1.350 + DELETE_ARRAY(buffer); 1.351 + } 1.352 + 1.353 + /*if (nBuffer != nStackBuffer) { 1.354 + DELETE_ARRAY(nBuffer); 1.355 + }*/ 1.356 + 1.357 + return appendTo; 1.358 +} 1.359 + 1.360 +U_NAMESPACE_END 1.361 + 1.362 +#endif /* #if !UCONFIG_NO_FORMATTING */ 1.363 + 1.364 +#endif // U_PLATFORM_USES_ONLY_WIN32_API