winnmfmt.cpp 14 KB

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