Wed, 31 Dec 2014 07:22:50 +0100
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 |