intl/icu/source/i18n/winnmfmt.cpp

changeset 0
6474c204b198
     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

mercurial