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