diff options
Diffstat (limited to 'src/corelib/tools/qlocale_icu.cpp')
-rw-r--r-- | src/corelib/tools/qlocale_icu.cpp | 205 |
1 files changed, 35 insertions, 170 deletions
diff --git a/src/corelib/tools/qlocale_icu.cpp b/src/corelib/tools/qlocale_icu.cpp index 42651aa745..97ce82108b 100644 --- a/src/corelib/tools/qlocale_icu.cpp +++ b/src/corelib/tools/qlocale_icu.cpp @@ -42,199 +42,56 @@ #include "qglobal.h" #include "qlibrary.h" #include "qdebug.h" +#include "qlocale_p.h" +#include "qmutex.h" #include "unicode/uversion.h" #include "unicode/ucol.h" +#include "unicode/uloc.h" +#include "unicode/ustring.h" QT_BEGIN_NAMESPACE -typedef UCollator *(*Ptr_ucol_open)(const char *loc, UErrorCode *status); -typedef void (*Ptr_ucol_close)(UCollator *coll); -typedef UCollationResult (*Ptr_ucol_strcoll)(const UCollator *coll, const UChar *source, int32_t sourceLength, const UChar *target, int32_t targetLength); typedef int32_t (*Ptr_u_strToCase)(UChar *dest, int32_t destCapacity, const UChar *src, int32_t srcLength, const char *locale, UErrorCode *pErrorCode); -static Ptr_ucol_open ptr_ucol_open = 0; -static Ptr_ucol_strcoll ptr_ucol_strcoll = 0; -static Ptr_ucol_close ptr_ucol_close = 0; -static Ptr_u_strToCase ptr_u_strToUpper = 0; -static Ptr_u_strToCase ptr_u_strToLower = 0; - -enum LibLoadStatus -{ - ErrorLoading = -1, - NotLoaded = 0, - Loaded = 1 -}; - -static LibLoadStatus status = NotLoaded; - -static UCollator *icuCollator = 0; - -namespace { -struct Libraries { - QLibrary libicui18n; - QLibrary libicuuc; - ~Libraries() - { - if (icuCollator) { - ptr_ucol_close(icuCollator); - icuCollator = 0; - } - - libicui18n.unload(); - libicuuc.unload(); - } -}; -} -Q_GLOBAL_STATIC(Libraries, icuLibraries) - -static bool loadIcuLibrary(QLibrary &lib, const QString &name) -{ -#ifdef Q_OS_WIN - // QLibrary on Windows does not use the version number, the libraries - // are named "icuin<version>.dll", though. - lib.setFileName(name); -#else - // on Unix, we can use the version number - lib.setFileNameAndVersion(name, QStringLiteral(U_ICU_VERSION_SHORT)); -#endif - - // the ICU libraries appear to allocate global statics and not free them - // set the PreventUnloadHint so that we can unload the QLibrary object and - // delete it, but the libraries themselves remain in memory - lib.setLoadHints(QLibrary::PreventUnloadHint); - return lib.load(); -} - -// this function is NOT THREAD-SAFE! -bool qt_initIcu(const QString &localeString) -{ - if (status == ErrorLoading || !icuLibraries()) - return false; - - if (status == NotLoaded) { - - // resolve libicui18n -#ifdef Q_OS_WIN - // QLibrary on Windows does not use the version number, the libraries - // are named "icuin<version>.dll", though. - // QStringLiteral should work here and will work when MSVC fully supports C++11 - // unfortunately, current versions have do not support proper string concatenation - QString libicui18nName = QLatin1String("icuin" U_ICU_VERSION_SHORT); - QString libicuucName = QLatin1String("icuuc" U_ICU_VERSION_SHORT); -#else - QString libicui18nName = QStringLiteral("icui18n"); - QString libicuucName = QStringLiteral("icuuc"); -#endif - QLibrary &lib = icuLibraries()->libicui18n; - if (!loadIcuLibrary(lib, libicui18nName)) { - qWarning("Unable to load library '%s' version " U_ICU_VERSION_SHORT ": %s", - qPrintable(libicui18nName), - qPrintable(lib.errorString())); - status = ErrorLoading; - return false; - } - - ptr_ucol_open = (Ptr_ucol_open)lib.resolve("ucol_open"); - ptr_ucol_close = (Ptr_ucol_close)lib.resolve("ucol_close"); - ptr_ucol_strcoll = (Ptr_ucol_strcoll)lib.resolve("ucol_strcoll"); - - if (!ptr_ucol_open || !ptr_ucol_close || !ptr_ucol_strcoll) { - // try again with decorated symbol names - ptr_ucol_open = (Ptr_ucol_open)lib.resolve("ucol_open" QT_STRINGIFY(U_ICU_VERSION_SUFFIX)); - ptr_ucol_close = (Ptr_ucol_close)lib.resolve("ucol_close" QT_STRINGIFY(U_ICU_VERSION_SUFFIX)); - ptr_ucol_strcoll = (Ptr_ucol_strcoll)lib.resolve("ucol_strcoll" QT_STRINGIFY(U_ICU_VERSION_SUFFIX)); - } - - if (!ptr_ucol_open || !ptr_ucol_close || !ptr_ucol_strcoll) { - ptr_ucol_open = 0; - ptr_ucol_close = 0; - ptr_ucol_strcoll = 0; - - qWarning("Unable to find symbols in '%s'.", qPrintable(libicui18nName)); - status = ErrorLoading; - return false; - } - - // resolve libicuuc - QLibrary &ucLib = icuLibraries()->libicuuc; - if (!loadIcuLibrary(ucLib, libicuucName)) { - qWarning("Unable to load library '%s' version " U_ICU_VERSION_SHORT ": %s", - qPrintable(libicuucName), - qPrintable(ucLib.errorString())); - status = ErrorLoading; - return false; - } - - ptr_u_strToUpper = (Ptr_u_strToCase)ucLib.resolve("u_strToUpper"); - ptr_u_strToLower = (Ptr_u_strToCase)ucLib.resolve("u_strToLower"); - - if (!ptr_u_strToUpper || !ptr_u_strToLower) { - ptr_u_strToUpper = (Ptr_u_strToCase)ucLib.resolve("u_strToUpper" QT_STRINGIFY(U_ICU_VERSION_SUFFIX)); - ptr_u_strToLower = (Ptr_u_strToCase)ucLib.resolve("u_strToLower" QT_STRINGIFY(U_ICU_VERSION_SUFFIX)); - } - - if (!ptr_u_strToUpper || !ptr_u_strToLower) { - ptr_u_strToUpper = 0; - ptr_u_strToLower = 0; - - qWarning("Unable to find symbols in '%s'", qPrintable(libicuucName)); - status = ErrorLoading; - return false; - } - - // success :) - status = Loaded; - } - - if (icuCollator) { - ptr_ucol_close(icuCollator); - icuCollator = 0; - } - - UErrorCode icuStatus = U_ZERO_ERROR; - icuCollator = ptr_ucol_open(localeString.toLatin1().constData(), &icuStatus); - - if (!icuCollator) { - qWarning("Unable to open locale %s in ICU, error code %d", qPrintable(localeString), icuStatus); - return false; - } - - return true; -} - -bool qt_ucol_strcoll(const QChar *source, int sourceLength, const QChar *target, int targetLength, int *result) +bool QIcu::strcoll(const QByteArray &localeID, + const QChar *source, int sourceLength, const QChar *target, int targetLength, int *result) { Q_ASSERT(result); Q_ASSERT(source); Q_ASSERT(target); - if (!icuCollator) + UErrorCode icuStatus = U_ZERO_ERROR; + UCollator *collator = ucol_open(localeID, &icuStatus); + + if (U_FAILURE((icuStatus))) return false; - *result = ptr_ucol_strcoll(icuCollator, reinterpret_cast<const UChar *>(source), int32_t(sourceLength), - reinterpret_cast<const UChar *>(target), int32_t(targetLength)); + *result = ucol_strcoll(collator, + reinterpret_cast<const UChar *>(source), int32_t(sourceLength), + reinterpret_cast<const UChar *>(target), int32_t(targetLength)); + + ucol_close(collator); return true; } // caseFunc can either be u_strToUpper or u_strToLower -static bool qt_u_strToCase(const QString &str, QString *out, const QLocale &locale, Ptr_u_strToCase caseFunc) +static bool qt_u_strToCase(const QString &str, QString *out, const char *localeID, Ptr_u_strToCase caseFunc) { Q_ASSERT(out); - if (!icuCollator) - return false; - - QString result(str.size(), Qt::Uninitialized); + int32_t size = str.size(); + size += size >> 2; // add 25% for possible expansions + QString result(size, Qt::Uninitialized); UErrorCode status = U_ZERO_ERROR; - int32_t size = caseFunc(reinterpret_cast<UChar *>(result.data()), result.size(), + size = caseFunc(reinterpret_cast<UChar *>(result.data()), result.size(), reinterpret_cast<const UChar *>(str.constData()), str.size(), - locale.bcp47Name().toLatin1().constData(), &status); + localeID, &status); - if (U_FAILURE(status)) + if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) return false; if (size < result.size()) { @@ -246,7 +103,7 @@ static bool qt_u_strToCase(const QString &str, QString *out, const QLocale &loca status = U_ZERO_ERROR; size = caseFunc(reinterpret_cast<UChar *>(result.data()), result.size(), reinterpret_cast<const UChar *>(str.constData()), str.size(), - locale.bcp47Name().toLatin1().constData(), &status); + localeID, &status); if (U_FAILURE(status)) return false; @@ -260,14 +117,22 @@ static bool qt_u_strToCase(const QString &str, QString *out, const QLocale &loca return true; } -bool qt_u_strToUpper(const QString &str, QString *out, const QLocale &locale) +QString QIcu::toUpper(const QByteArray &localeID, const QString &str, bool *ok) { - return qt_u_strToCase(str, out, locale, ptr_u_strToUpper); + QString out; + bool err = qt_u_strToCase(str, &out, localeID, u_strToUpper); + if (ok) + *ok = err; + return out; } -bool qt_u_strToLower(const QString &str, QString *out, const QLocale &locale) +QString QIcu::toLower(const QByteArray &localeID, const QString &str, bool *ok) { - return qt_u_strToCase(str, out, locale, ptr_u_strToLower); + QString out; + bool err = qt_u_strToCase(str, &out, localeID, u_strToLower); + if (ok) + *ok = err; + return out; } QT_END_NAMESPACE |