From dad013ac0177be398b26236d5aa606a32d0a3356 Mon Sep 17 00:00:00 2001 From: Francesco Montorsi Date: Thu, 19 Mar 2009 18:05:49 +0000 Subject: [PATCH] extend wxXLocale with wxStrto[d,l,ul] functions; make wxXLocale::Init() a little bit smarter on Unix systems; make XLocaleTestCase not fail on systems where french/italian support is not installed git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@59627 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/xlocale.h | 43 +++++++++++++-- interface/wx/xlocale.h | 55 +++++++++++++++---- src/common/xlocale.cpp | 37 +++++++++++++ tests/xlocale/xlocale.cpp | 133 ++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 250 insertions(+), 18 deletions(-) diff --git a/include/wx/xlocale.h b/include/wx/xlocale.h index 64b6e53..d2ca8d2 100644 --- a/include/wx/xlocale.h +++ b/include/wx/xlocale.h @@ -18,8 +18,8 @@ using decimal point &c. TODO: Currently only the character classification and transformation - functions are implemented, we also need at least - - numbers: atof_l(), strtod_l() &c + functions and number <-> string functions, are implemented, + we also need at least - formatted IO: scanf_l(), printf_l() &c - time: strftime_l(), strptime_l() */ @@ -44,6 +44,7 @@ #include #include #include + #include #if wxUSE_UNICODE #include @@ -96,6 +97,9 @@ public: // Get the type wxXLocale_t Get() const { return m_locale; } + bool operator== (const wxXLocale& loc) const + { return m_locale == loc.m_locale; } + private: // Special ctor for the "C" locale, it's only used internally as the user // code is supposed to use GetCLocale() @@ -177,7 +181,7 @@ private: // A shorter synonym for the most commonly used locale object #define wxCLocale (wxXLocale::GetCLocale()) - +extern WXDLLIMPEXP_DATA_BASE(wxXLocale) wxNullXLocale; // Wrappers for various functions: #ifdef wxHAS_XLOCALE_SUPPORT @@ -224,7 +228,26 @@ private: inline int wxToupper_l(char c, const wxXLocale& loc) { return wxCRT_Toupper_lA(static_cast(c), loc.Get()); } + + // stdlib functions for numeric <-> string conversion + // NOTE: GNU libc does not have ato[fil]_l functions; + // MSVC++8 does not have _strto[u]ll_l functions; + // thus we take the minimal set of functions provided in both environments: + + #define wxCRT_Strtod_lA wxXLOCALE_IDENT(strtod_l) + #define wxCRT_Strtol_lA wxXLOCALE_IDENT(strtol_l) + #define wxCRT_Strtoul_lA wxXLOCALE_IDENT(strtoul_l) + + inline double wxStrtod_lA(const char *c, char **endptr, const wxXLocale& loc) + { return wxCRT_Strtod_lA(c, endptr, loc.Get()); } + inline long wxStrtol_lA(const char *c, char **endptr, int base, const wxXLocale& loc) + { return wxCRT_Strtol_lA(c, endptr, base, loc.Get()); } + inline unsigned long wxStrtoul_lA(const char *c, char **endptr, int base, const wxXLocale& loc) + { return wxCRT_Strtoul_lA(c, endptr, base, loc.Get()); } + #if wxUSE_UNICODE + + // ctype functions #define wxCRT_Isalnum_lW wxXLOCALE_IDENT(iswalnum_l) #define wxCRT_Isalpha_lW wxXLOCALE_IDENT(iswalpha_l) #define wxCRT_Iscntrl_lW wxXLOCALE_IDENT(iswcntrl_l) @@ -266,6 +289,20 @@ private: inline wchar_t wxToupper_l(wchar_t c, const wxXLocale& loc) { return wxCRT_Toupper_lW(c, loc.Get()); } + + // stdlib functions for numeric <-> string conversion + // (see notes above about missing functions) + #define wxCRT_Strtod_lW wxXLOCALE_IDENT(wcstod_l) + #define wxCRT_Strtol_lW wxXLOCALE_IDENT(wcstol_l) + #define wxCRT_Strtoul_lW wxXLOCALE_IDENT(wcstoul_l) + + inline double wxStrtod_l(const wchar_t *c, wchar_t **endptr, const wxXLocale& loc) + { return wxCRT_Strtod_lW(c, endptr, loc.Get()); } + inline long wxStrtol_l(const wchar_t *c, wchar_t **endptr, int base, const wxXLocale& loc) + { return wxCRT_Strtol_lW(c, endptr, base, loc.Get()); } + inline unsigned long wxStrtoul_l(const wchar_t *c, wchar_t **endptr, int base, const wxXLocale& loc) + { return wxCRT_Strtoul_lW(c, endptr, base, loc.Get()); } + #endif // wxUSE_UNICDE (ctype functions) #else // !wxHAS_XLOCALE_SUPPORT // ctype functions diff --git a/interface/wx/xlocale.h b/interface/wx/xlocale.h index 4d36bea..a4246a8 100644 --- a/interface/wx/xlocale.h +++ b/interface/wx/xlocale.h @@ -27,6 +27,8 @@ number in a standard format it can use: @code wxPrintf_l(wxXLocale::GetCLocale(), "%g", number) @endcode to do it. + + See @ref group_funcmacro_locale for a list of wxXLocale-enabled functions. Conversely, if a program wanted to output the number in French locale, even if the current locale is different, it could use wxXLocale(wxLANGUAGE_FRENCH). @@ -50,19 +52,12 @@ @c wxUSE_XLOCALE was set to 0 during the library compilation. - @section xlocale_func Locale-dependent functions - - Currently the following @c _l-functions are available: - - Character classification functions: - @c wxIsxxx_l(), e.g. @c wxIsalpha_l(), @c wxIslower_l() and all the others. - - Character transformation functions: - @c wxTolower_l() and @c wxToupper_l() - We hope to provide many more functions (covering numbers, time and formatted - IO) in the near future. - @library{wxbase} @category{cfg} + @stdobjects + @li ::wxNullXLocale + @see wxLocale */ class wxXLocale @@ -98,4 +93,44 @@ public: or @false otherwise. */ bool IsOk() const; + + /** + Comparison operator. + */ + bool operator==(const wxXLocale& loc) const; }; + +/** + An empty and invalid wxXLocale object. +*/ +wxXLocale wxNullXLocale; + + + +// ============================================================================ +// Global functions/macros +// ============================================================================ + +/** @addtogroup group_funcmacro_locale */ +//@{ + +int wxIsalnum_l(wchar_t c, const wxXLocale& loc); +int wxIsalpha_l(wchar_t c, const wxXLocale& loc); +int wxIscntrl_l(wchar_t c, const wxXLocale& loc); +int wxIsdigit_l(wchar_t c, const wxXLocale& loc); +int wxIsgraph_l(wchar_t c, const wxXLocale& loc); +int wxIslower_l(wchar_t c, const wxXLocale& loc); +int wxIsprint_l(wchar_t c, const wxXLocale& loc); +int wxIspunct_l(wchar_t c, const wxXLocale& loc); +int wxIsspace_l(wchar_t c, const wxXLocale& loc); +int wxIsupper_l(wchar_t c, const wxXLocale& loc); +int wxIsxdigit_l(wchar_t c, const wxXLocale& loc); +wchar_t wxTolower_l(wchar_t c, const wxXLocale& loc); +wchar_t wxToupper_l(wchar_t c, const wxXLocale& loc); + +double wxStrtod_l(const wchar_t *c, wchar_t **endptr, const wxXLocale& loc); +long wxStrtol_l(const wchar_t *c, wchar_t **endptr, int base, const wxXLocale& loc); +unsigned long wxStrtoul_l(const wchar_t *c, wchar_t **endptr, int base, const wxXLocale& loc); + +//@} + diff --git a/src/common/xlocale.cpp b/src/common/xlocale.cpp index b04288c..b2b035a 100644 --- a/src/common/xlocale.cpp +++ b/src/common/xlocale.cpp @@ -38,6 +38,9 @@ // This is the C locale object, it is created on demand static wxXLocale *gs_cLocale = NULL; +wxXLocale wxNullXLocale; + + // ============================================================================ // implementation // ============================================================================ @@ -100,6 +103,9 @@ wxXLocale::wxXLocale(wxLanguage lang) void wxXLocale::Init(const char *loc) { + if (!loc || *loc == '\0') + return; + m_locale = _create_locale(LC_ALL, loc); } @@ -117,7 +123,38 @@ void wxXLocale::Free() void wxXLocale::Init(const char *loc) { + if (!loc || *loc == '\0') + return; + m_locale = newlocale(LC_ALL_MASK, loc, NULL); + if (!m_locale) + { + // NOTE: here we do something similar to what wxSetLocaleTryUTF8() does + // in wxLocale code (but with newlocale() calls instead of wxSetlocale()) + wxString buf(loc); + wxString buf2; + buf2 = buf + wxS(".UTF-8"); + m_locale = newlocale(LC_ALL_MASK, buf2, NULL); + if ( !m_locale ) + { + buf2 = buf + wxS(".utf-8"); + m_locale = newlocale(LC_ALL_MASK, buf2, NULL); + } + if ( !m_locale ) + { + buf2 = buf + wxS(".UTF8"); + m_locale = newlocale(LC_ALL_MASK, buf2, NULL); + } + if ( !m_locale ) + { + buf2 = buf + wxS(".utf8"); + m_locale = newlocale(LC_ALL_MASK, buf2, NULL); + } + } + + // TODO: wxLocale performs many more manipulations of the given locale + // string in the attempt to set a valid locale; reusing that code + // (changing it to take a generic wxTryLocale callback) would be nice } void wxXLocale::Free() diff --git a/tests/xlocale/xlocale.cpp b/tests/xlocale/xlocale.cpp index 07172bb..d5cf800 100644 --- a/tests/xlocale/xlocale.cpp +++ b/tests/xlocale/xlocale.cpp @@ -40,12 +40,15 @@ private: CPPUNIT_TEST_SUITE( XLocaleTestCase ); CPPUNIT_TEST( TestCtor ); CPPUNIT_TEST( TestCtypeFunctions ); + CPPUNIT_TEST( TestStdlibFunctions ); CPPUNIT_TEST_SUITE_END(); void TestCtor(); void TestCtypeFunctions(); + void TestStdlibFunctions(); void TestCtypeFunctionsWith(const wxXLocale& loc); + void TestStdlibFunctionsWith(const wxXLocale& loc); DECLARE_NO_COPY_CLASS(XLocaleTestCase) }; @@ -63,6 +66,11 @@ void XLocaleTestCase::TestCtor() CPPUNIT_ASSERT( !wxXLocale().IsOk() ); CPPUNIT_ASSERT( wxCLocale.IsOk() ); CPPUNIT_ASSERT( wxXLocale("C").IsOk() ); + CPPUNIT_ASSERT( !wxXLocale("bloordyblop").IsOk() ); + + if (!wxLocale::IsAvailable(wxLANGUAGE_FRENCH)) + return; // you should have french support installed to continue this test! + #ifdef wxHAS_XLOCALE_SUPPORT CPPUNIT_ASSERT( wxXLocale(wxLANGUAGE_FRENCH).IsOk() ); #ifdef __WXMSW__ @@ -71,12 +79,15 @@ void XLocaleTestCase::TestCtor() CPPUNIT_ASSERT( wxXLocale("fr_FR").IsOk() ); #endif #endif - CPPUNIT_ASSERT( !wxXLocale("bloordyblop").IsOk() ); } // test the ctype functions with the given locale void XLocaleTestCase::TestCtypeFunctionsWith(const wxXLocale& loc) { + // NOTE: here go the checks which must pass under _any_ locale "loc"; + // checks for specific locales are in TestCtypeFunctions() + + // isalnum CPPUNIT_ASSERT( wxIsalnum_l('0', loc) ); CPPUNIT_ASSERT( wxIsalnum_l('9', loc) ); @@ -167,20 +178,132 @@ void XLocaleTestCase::TestCtypeFunctionsWith(const wxXLocale& loc) CPPUNIT_ASSERT_EQUAL( '9', (char)wxToupper_l('9', loc) ); } +// test the stdlib functions with the given locale +void XLocaleTestCase::TestStdlibFunctionsWith(const wxXLocale& loc) +{ + // NOTE: here go the checks which must pass under _any_ locale "loc"; + // checks for specific locales are in TestStdlibFunctions() + +#if wxUSE_UNICODE + wchar_t* endptr; +#else + char* endptr; +#endif + + // strtod (don't use decimal separator as it's locale-specific) + CPPUNIT_ASSERT_EQUAL( 0.0, wxStrtod_l(wxT("0"), NULL, loc) ); + CPPUNIT_ASSERT_EQUAL( 1234.0, wxStrtod_l(wxT("1234"), NULL, loc) ); + + // strtol + endptr = NULL; + CPPUNIT_ASSERT_EQUAL( 100, wxStrtol_l(wxT("100"), NULL, 0, loc) ); + CPPUNIT_ASSERT_EQUAL( 0xFF, wxStrtol_l(wxT("0xFF"), NULL, 0, loc) ); + CPPUNIT_ASSERT_EQUAL( 2001, wxStrtol_l(wxT("2001 60c0c0 -1101110100110100100000 0x6fffff"), &endptr, 10, loc) ); + CPPUNIT_ASSERT_EQUAL( 0x60c0c0, wxStrtol_l(endptr, &endptr, 16, loc) ); + CPPUNIT_ASSERT_EQUAL( -0x374D20, wxStrtol_l(endptr, &endptr, 2, loc) ); + CPPUNIT_ASSERT_EQUAL( 0x6fffff, wxStrtol_l(endptr, NULL, 0, loc) ); + + // strtoul + // NOTE: 3147483647 and 0x12A05F200 are greater than LONG_MAX (on 32bit machines) but + // smaller than ULONG_MAX + CPPUNIT_ASSERT_EQUAL( (unsigned long)3147483647, wxStrtoul_l(wxT("3147483647"), NULL, 0, loc) ); + CPPUNIT_ASSERT_EQUAL( (unsigned long)0x12A05F200, wxStrtoul_l(wxT("0x12A05F200"), NULL, 0, loc) ); + + // TODO: test for "failure" behaviour of the functions above +} + void XLocaleTestCase::TestCtypeFunctions() { TestCtypeFunctionsWith(wxCLocale); #ifdef wxHAS_XLOCALE_SUPPORT + + // french + + if (!wxLocale::IsAvailable(wxLANGUAGE_FRENCH)) + return; // you should have french support installed to continue this test! + wxXLocale locFR(wxLANGUAGE_FRENCH); CPPUNIT_ASSERT( locFR.IsOk() ); // doesn't make sense to continue otherwise TestCtypeFunctionsWith(locFR); - CPPUNIT_ASSERT( wxIsalpha_l('\xe9', locFR) ); - CPPUNIT_ASSERT( wxIslower_l('\xe9', locFR) ); - CPPUNIT_ASSERT( !wxIslower_l('\xc9', locFR) ); - CPPUNIT_ASSERT( wxIsupper_l('\xc9', locFR) ); + CPPUNIT_ASSERT( wxIsalpha_l(wxT('\xe9'), locFR) ); + CPPUNIT_ASSERT( wxIslower_l(wxT('\xe9'), locFR) ); + CPPUNIT_ASSERT( !wxIslower_l(wxT('\xc9'), locFR) ); + CPPUNIT_ASSERT( wxIsupper_l(wxT('\xc9'), locFR) ); + CPPUNIT_ASSERT( wxIsalpha_l(wxT('\xe7'), locFR) ); + CPPUNIT_ASSERT( wxIslower_l(wxT('\xe7'), locFR) ); + CPPUNIT_ASSERT( wxIsupper_l(wxT('\xc7'), locFR) ); + + + // italian + + if (!wxLocale::IsAvailable(wxLANGUAGE_ITALIAN)) + return; // you should have italian support installed to continue this test! + + wxXLocale locIT(wxLANGUAGE_ITALIAN); + CPPUNIT_ASSERT( locIT.IsOk() ); // doesn't make sense to continue otherwise + + TestCtypeFunctionsWith(locIT); + + CPPUNIT_ASSERT( wxIsalpha_l(wxT('\xe1'), locIT) ); + CPPUNIT_ASSERT( wxIslower_l(wxT('\xe1'), locIT) ); +#endif +} + +void XLocaleTestCase::TestStdlibFunctions() +{ + TestStdlibFunctionsWith(wxCLocale); + +#if wxUSE_UNICODE + wchar_t* endptr; +#else + char* endptr; +#endif + + // strtod checks specific for C locale + endptr = NULL; + CPPUNIT_ASSERT_EQUAL( 0.0, wxStrtod_l(wxT("0.000"), NULL, wxCLocale) ); + CPPUNIT_ASSERT_EQUAL( 1.234, wxStrtod_l(wxT("1.234"), NULL, wxCLocale) ); + CPPUNIT_ASSERT_EQUAL( -1.234E-5, wxStrtod_l(wxT("-1.234E-5"), NULL, wxCLocale) ); + CPPUNIT_ASSERT_EQUAL( 365.24, wxStrtod_l(wxT("365.24 29.53"), &endptr, wxCLocale) ); + CPPUNIT_ASSERT_EQUAL( 29.53, wxStrtod_l(endptr, NULL, wxCLocale) ); + +#ifdef wxHAS_XLOCALE_SUPPORT + + // french + + if (!wxLocale::IsAvailable(wxLANGUAGE_FRENCH)) + return; // you should have french support installed to continue this test! + + wxXLocale locFR(wxLANGUAGE_FRENCH); + CPPUNIT_ASSERT( locFR.IsOk() ); // doesn't make sense to continue otherwise + + TestCtypeFunctionsWith(locFR); + + // comma as decimal point: + CPPUNIT_ASSERT_EQUAL( 1.234, wxStrtod_l(wxT("1,234"), NULL, locFR) ); + + // space as thousands separator is not recognized by wxStrtod_l(): + CPPUNIT_ASSERT( 1234.5 != wxStrtod_l(wxT("1 234,5"), NULL, locFR) ); + + + // italian + + if (!wxLocale::IsAvailable(wxLANGUAGE_ITALIAN)) + return; // you should have italian support installed to continue this test! + + wxXLocale locIT(wxLANGUAGE_ITALIAN); + CPPUNIT_ASSERT( locIT.IsOk() ); // doesn't make sense to continue otherwise + + TestStdlibFunctionsWith(locIT); + + // comma as decimal point: + CPPUNIT_ASSERT_EQUAL( 1.234, wxStrtod_l(wxT("1,234"), NULL, locIT) ); + + // dot as thousands separator is not recognized by wxStrtod_l(): + CPPUNIT_ASSERT( 1234.5 != wxStrtod_l(wxT("1.234,5"), NULL, locIT) ); #endif } -- 2.7.4