intl/icu/source/i18n/winnmfmt.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /*
michael@0 2 ********************************************************************************
michael@0 3 * Copyright (C) 2005-2013, International Business Machines
michael@0 4 * Corporation and others. All Rights Reserved.
michael@0 5 ********************************************************************************
michael@0 6 *
michael@0 7 * File WINNMFMT.CPP
michael@0 8 *
michael@0 9 ********************************************************************************
michael@0 10 */
michael@0 11
michael@0 12 #include "unicode/utypes.h"
michael@0 13
michael@0 14 #if U_PLATFORM_USES_ONLY_WIN32_API
michael@0 15
michael@0 16 #if !UCONFIG_NO_FORMATTING
michael@0 17
michael@0 18 #include "winnmfmt.h"
michael@0 19
michael@0 20 #include "unicode/format.h"
michael@0 21 #include "unicode/numfmt.h"
michael@0 22 #include "unicode/locid.h"
michael@0 23 #include "unicode/ustring.h"
michael@0 24
michael@0 25 #include "cmemory.h"
michael@0 26 #include "uassert.h"
michael@0 27 #include "locmap.h"
michael@0 28
michael@0 29 # define WIN32_LEAN_AND_MEAN
michael@0 30 # define VC_EXTRALEAN
michael@0 31 # define NOUSER
michael@0 32 # define NOSERVICE
michael@0 33 # define NOIME
michael@0 34 # define NOMCX
michael@0 35 #include <windows.h>
michael@0 36 #include <stdio.h>
michael@0 37
michael@0 38 U_NAMESPACE_BEGIN
michael@0 39
michael@0 40 union FormatInfo
michael@0 41 {
michael@0 42 NUMBERFMTW number;
michael@0 43 CURRENCYFMTW currency;
michael@0 44 };
michael@0 45
michael@0 46 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Win32NumberFormat)
michael@0 47
michael@0 48 #define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type))
michael@0 49 #define DELETE_ARRAY(array) uprv_free((void *) (array))
michael@0 50
michael@0 51 #define STACK_BUFFER_SIZE 32
michael@0 52
michael@0 53 /*
michael@0 54 * Turns a string of the form "3;2;0" into the grouping UINT
michael@0 55 * needed for NUMBERFMT and CURRENCYFMT. If the string does not
michael@0 56 * end in ";0" then the return value should be multiplied by 10.
michael@0 57 * (e.g. "3" => 30, "3;2" => 320)
michael@0 58 */
michael@0 59 static UINT getGrouping(const char *grouping)
michael@0 60 {
michael@0 61 UINT g = 0;
michael@0 62 const char *s;
michael@0 63
michael@0 64 for (s = grouping; *s != '\0'; s += 1) {
michael@0 65 if (*s > '0' && *s < '9') {
michael@0 66 g = g * 10 + (*s - '0');
michael@0 67 } else if (*s != ';') {
michael@0 68 break;
michael@0 69 }
michael@0 70 }
michael@0 71
michael@0 72 if (*s != '0') {
michael@0 73 g *= 10;
michael@0 74 }
michael@0 75
michael@0 76 return g;
michael@0 77 }
michael@0 78
michael@0 79 static void getNumberFormat(NUMBERFMTW *fmt, int32_t lcid)
michael@0 80 {
michael@0 81 char buf[10];
michael@0 82
michael@0 83 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_IDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT));
michael@0 84 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->LeadingZero, sizeof(UINT));
michael@0 85
michael@0 86 GetLocaleInfoA(lcid, LOCALE_SGROUPING, buf, 10);
michael@0 87 fmt->Grouping = getGrouping(buf);
michael@0 88
michael@0 89 fmt->lpDecimalSep = NEW_ARRAY(UChar, 6);
michael@0 90 GetLocaleInfoW(lcid, LOCALE_SDECIMAL, fmt->lpDecimalSep, 6);
michael@0 91
michael@0 92 fmt->lpThousandSep = NEW_ARRAY(UChar, 6);
michael@0 93 GetLocaleInfoW(lcid, LOCALE_STHOUSAND, fmt->lpThousandSep, 6);
michael@0 94
michael@0 95 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_INEGNUMBER, (LPWSTR) &fmt->NegativeOrder, sizeof(UINT));
michael@0 96 }
michael@0 97
michael@0 98 static void freeNumberFormat(NUMBERFMTW *fmt)
michael@0 99 {
michael@0 100 if (fmt != NULL) {
michael@0 101 DELETE_ARRAY(fmt->lpThousandSep);
michael@0 102 DELETE_ARRAY(fmt->lpDecimalSep);
michael@0 103 }
michael@0 104 }
michael@0 105
michael@0 106 static void getCurrencyFormat(CURRENCYFMTW *fmt, int32_t lcid)
michael@0 107 {
michael@0 108 char buf[10];
michael@0 109
michael@0 110 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ICURRDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT));
michael@0 111 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->LeadingZero, sizeof(UINT));
michael@0 112
michael@0 113 GetLocaleInfoA(lcid, LOCALE_SMONGROUPING, buf, sizeof(buf));
michael@0 114 fmt->Grouping = getGrouping(buf);
michael@0 115
michael@0 116 fmt->lpDecimalSep = NEW_ARRAY(UChar, 6);
michael@0 117 GetLocaleInfoW(lcid, LOCALE_SMONDECIMALSEP, fmt->lpDecimalSep, 6);
michael@0 118
michael@0 119 fmt->lpThousandSep = NEW_ARRAY(UChar, 6);
michael@0 120 GetLocaleInfoW(lcid, LOCALE_SMONTHOUSANDSEP, fmt->lpThousandSep, 6);
michael@0 121
michael@0 122 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_INEGCURR, (LPWSTR) &fmt->NegativeOrder, sizeof(UINT));
michael@0 123 GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ICURRENCY, (LPWSTR) &fmt->PositiveOrder, sizeof(UINT));
michael@0 124
michael@0 125 fmt->lpCurrencySymbol = NEW_ARRAY(UChar, 8);
michael@0 126 GetLocaleInfoW(lcid, LOCALE_SCURRENCY, (LPWSTR) fmt->lpCurrencySymbol, 8);
michael@0 127 }
michael@0 128
michael@0 129 static void freeCurrencyFormat(CURRENCYFMTW *fmt)
michael@0 130 {
michael@0 131 if (fmt != NULL) {
michael@0 132 DELETE_ARRAY(fmt->lpCurrencySymbol);
michael@0 133 DELETE_ARRAY(fmt->lpThousandSep);
michael@0 134 DELETE_ARRAY(fmt->lpDecimalSep);
michael@0 135 }
michael@0 136 }
michael@0 137
michael@0 138 Win32NumberFormat::Win32NumberFormat(const Locale &locale, UBool currency, UErrorCode &status)
michael@0 139 : NumberFormat(), fCurrency(currency), fFractionDigitsSet(FALSE), fFormatInfo(NULL)
michael@0 140 {
michael@0 141 if (!U_FAILURE(status)) {
michael@0 142 fLCID = locale.getLCID();
michael@0 143
michael@0 144 // Resolve actual locale to be used later
michael@0 145 UErrorCode tmpsts = U_ZERO_ERROR;
michael@0 146 char tmpLocID[ULOC_FULLNAME_CAPACITY];
michael@0 147 int32_t len = uloc_getLocaleForLCID(fLCID, tmpLocID, sizeof(tmpLocID)/sizeof(tmpLocID[0]) - 1, &tmpsts);
michael@0 148 if (U_SUCCESS(tmpsts)) {
michael@0 149 tmpLocID[len] = 0;
michael@0 150 fLocale = Locale((const char*)tmpLocID);
michael@0 151 }
michael@0 152
michael@0 153 fFormatInfo = (FormatInfo*)uprv_malloc(sizeof(FormatInfo));
michael@0 154
michael@0 155 if (fCurrency) {
michael@0 156 getCurrencyFormat(&fFormatInfo->currency, fLCID);
michael@0 157 } else {
michael@0 158 getNumberFormat(&fFormatInfo->number, fLCID);
michael@0 159 }
michael@0 160 }
michael@0 161 }
michael@0 162
michael@0 163 Win32NumberFormat::Win32NumberFormat(const Win32NumberFormat &other)
michael@0 164 : NumberFormat(other), fFormatInfo((FormatInfo*)uprv_malloc(sizeof(FormatInfo)))
michael@0 165 {
michael@0 166 if (fFormatInfo != NULL) {
michael@0 167 uprv_memset(fFormatInfo, 0, sizeof(*fFormatInfo));
michael@0 168 }
michael@0 169 *this = other;
michael@0 170 }
michael@0 171
michael@0 172 Win32NumberFormat::~Win32NumberFormat()
michael@0 173 {
michael@0 174 if (fFormatInfo != NULL) {
michael@0 175 if (fCurrency) {
michael@0 176 freeCurrencyFormat(&fFormatInfo->currency);
michael@0 177 } else {
michael@0 178 freeNumberFormat(&fFormatInfo->number);
michael@0 179 }
michael@0 180
michael@0 181 uprv_free(fFormatInfo);
michael@0 182 }
michael@0 183 }
michael@0 184
michael@0 185 Win32NumberFormat &Win32NumberFormat::operator=(const Win32NumberFormat &other)
michael@0 186 {
michael@0 187 NumberFormat::operator=(other);
michael@0 188
michael@0 189 this->fCurrency = other.fCurrency;
michael@0 190 this->fLocale = other.fLocale;
michael@0 191 this->fLCID = other.fLCID;
michael@0 192 this->fFractionDigitsSet = other.fFractionDigitsSet;
michael@0 193
michael@0 194 if (fCurrency) {
michael@0 195 freeCurrencyFormat(&fFormatInfo->currency);
michael@0 196 getCurrencyFormat(&fFormatInfo->currency, fLCID);
michael@0 197 } else {
michael@0 198 freeNumberFormat(&fFormatInfo->number);
michael@0 199 getNumberFormat(&fFormatInfo->number, fLCID);
michael@0 200 }
michael@0 201
michael@0 202 return *this;
michael@0 203 }
michael@0 204
michael@0 205 Format *Win32NumberFormat::clone(void) const
michael@0 206 {
michael@0 207 return new Win32NumberFormat(*this);
michael@0 208 }
michael@0 209
michael@0 210 UnicodeString& Win32NumberFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos) const
michael@0 211 {
michael@0 212 return format(getMaximumFractionDigits(), appendTo, L"%.16f", number);
michael@0 213 }
michael@0 214
michael@0 215 UnicodeString& Win32NumberFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos) const
michael@0 216 {
michael@0 217 return format(getMinimumFractionDigits(), appendTo, L"%I32d", number);
michael@0 218 }
michael@0 219
michael@0 220 UnicodeString& Win32NumberFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos) const
michael@0 221 {
michael@0 222 return format(getMinimumFractionDigits(), appendTo, L"%I64d", number);
michael@0 223 }
michael@0 224
michael@0 225 void Win32NumberFormat::parse(const UnicodeString& text, Formattable& result, ParsePosition& parsePosition) const
michael@0 226 {
michael@0 227 UErrorCode status = U_ZERO_ERROR;
michael@0 228 NumberFormat *nf = fCurrency? NumberFormat::createCurrencyInstance(fLocale, status) : NumberFormat::createInstance(fLocale, status);
michael@0 229
michael@0 230 nf->parse(text, result, parsePosition);
michael@0 231 delete nf;
michael@0 232 }
michael@0 233 void Win32NumberFormat::setMaximumFractionDigits(int32_t newValue)
michael@0 234 {
michael@0 235 fFractionDigitsSet = TRUE;
michael@0 236 NumberFormat::setMaximumFractionDigits(newValue);
michael@0 237 }
michael@0 238
michael@0 239 void Win32NumberFormat::setMinimumFractionDigits(int32_t newValue)
michael@0 240 {
michael@0 241 fFractionDigitsSet = TRUE;
michael@0 242 NumberFormat::setMinimumFractionDigits(newValue);
michael@0 243 }
michael@0 244
michael@0 245 UnicodeString &Win32NumberFormat::format(int32_t numDigits, UnicodeString &appendTo, wchar_t *fmt, ...) const
michael@0 246 {
michael@0 247 wchar_t nStackBuffer[STACK_BUFFER_SIZE];
michael@0 248 wchar_t *nBuffer = nStackBuffer;
michael@0 249 va_list args;
michael@0 250 int result;
michael@0 251
michael@0 252 nBuffer[0] = 0x0000;
michael@0 253
michael@0 254 /* Due to the arguments causing a result to be <= 23 characters (+2 for NULL and minus),
michael@0 255 we don't need to reallocate the buffer. */
michael@0 256 va_start(args, fmt);
michael@0 257 result = _vsnwprintf(nBuffer, STACK_BUFFER_SIZE, fmt, args);
michael@0 258 va_end(args);
michael@0 259
michael@0 260 /* Just to make sure of the above statement, we add this assert */
michael@0 261 U_ASSERT(result >=0);
michael@0 262 // The following code is not used because _vscwprintf isn't available on MinGW at the moment.
michael@0 263 /*if (result < 0) {
michael@0 264 int newLength;
michael@0 265
michael@0 266 va_start(args, fmt);
michael@0 267 newLength = _vscwprintf(fmt, args);
michael@0 268 va_end(args);
michael@0 269
michael@0 270 nBuffer = NEW_ARRAY(UChar, newLength + 1);
michael@0 271
michael@0 272 va_start(args, fmt);
michael@0 273 result = _vsnwprintf(nBuffer, newLength + 1, fmt, args);
michael@0 274 va_end(args);
michael@0 275 }*/
michael@0 276
michael@0 277 // vswprintf is sensitive to the locale set by setlocale. For some locales
michael@0 278 // it doesn't use "." as the decimal separator, which is what GetNumberFormatW
michael@0 279 // and GetCurrencyFormatW both expect to see.
michael@0 280 //
michael@0 281 // To fix this, we scan over the string and replace the first non-digits, except
michael@0 282 // for a leading "-", with a "."
michael@0 283 //
michael@0 284 // Note: (nBuffer[0] == L'-') will evaluate to 1 if there is a leading '-' in the
michael@0 285 // number, and 0 otherwise.
michael@0 286 for (wchar_t *p = &nBuffer[nBuffer[0] == L'-']; *p != L'\0'; p += 1) {
michael@0 287 if (*p < L'0' || *p > L'9') {
michael@0 288 *p = L'.';
michael@0 289 break;
michael@0 290 }
michael@0 291 }
michael@0 292
michael@0 293 UChar stackBuffer[STACK_BUFFER_SIZE];
michael@0 294 UChar *buffer = stackBuffer;
michael@0 295 FormatInfo formatInfo;
michael@0 296
michael@0 297 formatInfo = *fFormatInfo;
michael@0 298 buffer[0] = 0x0000;
michael@0 299
michael@0 300 if (fCurrency) {
michael@0 301 if (fFractionDigitsSet) {
michael@0 302 formatInfo.currency.NumDigits = (UINT) numDigits;
michael@0 303 }
michael@0 304
michael@0 305 if (!isGroupingUsed()) {
michael@0 306 formatInfo.currency.Grouping = 0;
michael@0 307 }
michael@0 308
michael@0 309 result = GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, buffer, STACK_BUFFER_SIZE);
michael@0 310
michael@0 311 if (result == 0) {
michael@0 312 DWORD lastError = GetLastError();
michael@0 313
michael@0 314 if (lastError == ERROR_INSUFFICIENT_BUFFER) {
michael@0 315 int newLength = GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, NULL, 0);
michael@0 316
michael@0 317 buffer = NEW_ARRAY(UChar, newLength);
michael@0 318 buffer[0] = 0x0000;
michael@0 319 GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, buffer, newLength);
michael@0 320 }
michael@0 321 }
michael@0 322 } else {
michael@0 323 if (fFractionDigitsSet) {
michael@0 324 formatInfo.number.NumDigits = (UINT) numDigits;
michael@0 325 }
michael@0 326
michael@0 327 if (!isGroupingUsed()) {
michael@0 328 formatInfo.number.Grouping = 0;
michael@0 329 }
michael@0 330
michael@0 331 result = GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, buffer, STACK_BUFFER_SIZE);
michael@0 332
michael@0 333 if (result == 0) {
michael@0 334 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
michael@0 335 int newLength = GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, NULL, 0);
michael@0 336
michael@0 337 buffer = NEW_ARRAY(UChar, newLength);
michael@0 338 buffer[0] = 0x0000;
michael@0 339 GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, buffer, newLength);
michael@0 340 }
michael@0 341 }
michael@0 342 }
michael@0 343
michael@0 344 appendTo.append(buffer, (int32_t) wcslen(buffer));
michael@0 345
michael@0 346 if (buffer != stackBuffer) {
michael@0 347 DELETE_ARRAY(buffer);
michael@0 348 }
michael@0 349
michael@0 350 /*if (nBuffer != nStackBuffer) {
michael@0 351 DELETE_ARRAY(nBuffer);
michael@0 352 }*/
michael@0 353
michael@0 354 return appendTo;
michael@0 355 }
michael@0 356
michael@0 357 U_NAMESPACE_END
michael@0 358
michael@0 359 #endif /* #if !UCONFIG_NO_FORMATTING */
michael@0 360
michael@0 361 #endif // U_PLATFORM_USES_ONLY_WIN32_API

mercurial