summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
authorDenis Dzyubenko <denis.dzyubenko@nokia.com>2011-04-12 12:48:13 (GMT)
committerDenis Dzyubenko <denis.dzyubenko@nokia.com>2011-04-12 13:39:00 (GMT)
commit46163663e956b988719563eae18773a2dedd424e (patch)
tree1064cae633e56f66f7a1cc49de4720f3807284e0 /src/corelib
parent04d1cbce75e77392077ce4ae014fe8774c32a8fc (diff)
downloadQt-46163663e956b988719563eae18773a2dedd424e.zip
Qt-46163663e956b988719563eae18773a2dedd424e.tar.gz
Qt-46163663e956b988719563eae18773a2dedd424e.tar.bz2
Added support for libICU for collation and toLower/toUpper.
This patch uses icu to do string collation via QString::localeAwareCompare function and for QString::toLower/toUpper - which is important e.g. for turkish locales where uppercased 'i' is not a latin 'I'. Based on the patch by Harald Fernengel Reviewed-by: Harald Fernengel Reviewed-by: Zeno Albisser
Diffstat (limited to 'src/corelib')
-rw-r--r--src/corelib/tools/qlocale.cpp14
-rw-r--r--src/corelib/tools/qlocale_icu.cpp224
-rw-r--r--src/corelib/tools/qstring.cpp35
-rw-r--r--src/corelib/tools/tools.pri5
4 files changed, 277 insertions, 1 deletions
diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp
index d986b9b..5c4085a 100644
--- a/src/corelib/tools/qlocale.cpp
+++ b/src/corelib/tools/qlocale.cpp
@@ -86,6 +86,10 @@ static QLocalePrivate *system_lp = 0;
Q_GLOBAL_STATIC(QLocalePrivate, globalLocalePrivate)
#endif
+#ifdef QT_USE_ICU
+extern bool qt_initIcu(const QString &localeName);
+#endif
+
/******************************************************************************
** Helpers for accessing Qt locale database
*/
@@ -520,6 +524,12 @@ void QLocalePrivate::updateSystemPrivate()
res = sys_locale->query(QSystemLocale::PositiveSign, QVariant());
if (!res.isNull())
system_lp->m_plus = res.toString().at(0).unicode();
+
+#ifdef QT_USE_ICU
+ if (!default_lp)
+ qt_initIcu(system_lp->bcp47Name());
+#endif
+
}
#endif
@@ -879,6 +889,10 @@ void QLocale::setDefault(const QLocale &locale)
{
default_lp = locale.d();
default_number_options = locale.numberOptions();
+
+#ifdef QT_USE_ICU
+ qt_initIcu(locale.bcp47Name());
+#endif
}
/*!
diff --git a/src/corelib/tools/qlocale_icu.cpp b/src/corelib/tools/qlocale_icu.cpp
new file mode 100644
index 0000000..0e283dd
--- /dev/null
+++ b/src/corelib/tools/qlocale_icu.cpp
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+#include "qlibrary.h"
+#include "qdebug.h"
+
+#include "unicode/uversion.h"
+#include "unicode/ucol.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;
+
+#define STRINGIFY2(x) #x
+#define STRINGIFY(x) STRINGIFY2(x)
+
+bool qt_initIcu(const QString &localeString)
+{
+ if (status == ErrorLoading)
+ return false;
+
+ if (status == NotLoaded) {
+
+ // resolve libicui18n
+ QLibrary lib(QLatin1String("icui18n"), QLatin1String(U_ICU_VERSION_SHORT));
+ if (!lib.load()) {
+ qWarning() << "Unable to load library icui18n" << 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" STRINGIFY(U_ICU_VERSION_SUFFIX));
+ ptr_ucol_close = (Ptr_ucol_close)lib.resolve("ucol_close" STRINGIFY(U_ICU_VERSION_SUFFIX));
+ ptr_ucol_strcoll = (Ptr_ucol_strcoll)lib.resolve("ucol_strcoll" 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 icui18n");
+ status = ErrorLoading;
+ return false;
+ }
+
+ // resolve libicuuc
+ QLibrary ucLib(QLatin1String("icuuc"), QLatin1String(U_ICU_VERSION_SHORT));
+ if (!ucLib.load()) {
+ qWarning() << "Unable to load library icuuc" << 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" STRINGIFY(U_ICU_VERSION_SUFFIX));
+ ptr_u_strToLower = (Ptr_u_strToCase)ucLib.resolve("u_strToLower" 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 icuuc");
+ 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)
+{
+ Q_ASSERT(result);
+ Q_ASSERT(source);
+ Q_ASSERT(target);
+
+ if (!icuCollator)
+ return false;
+
+ *result = ptr_ucol_strcoll(icuCollator, reinterpret_cast<const UChar *>(source), int32_t(sourceLength),
+ reinterpret_cast<const UChar *>(target), int32_t(targetLength));
+
+ 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)
+{
+ Q_ASSERT(out);
+
+ if (!icuCollator)
+ return false;
+
+ QString result(str.size(), Qt::Uninitialized);
+
+ UErrorCode status = U_ZERO_ERROR;
+
+ int32_t size = caseFunc(reinterpret_cast<UChar *>(result.data()), result.size(),
+ reinterpret_cast<const UChar *>(str.constData()), str.size(),
+ locale.bcp47Name().toLatin1().constData(), &status);
+
+ if (U_FAILURE(status))
+ return false;
+
+ if (size < result.size()) {
+ result.resize(size);
+ } else if (size > result.size()) {
+ // the resulting string is larger than our source string
+ result.resize(size);
+
+ 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);
+
+ if (U_FAILURE(status))
+ return false;
+
+ // if the sizes don't match now, we give up.
+ if (size != result.size())
+ return false;
+ }
+
+ *out = result;
+ return true;
+}
+
+bool qt_u_strToUpper(const QString &str, QString *out, const QLocale &locale)
+{
+ return qt_u_strToCase(str, out, locale, ptr_u_strToUpper);
+}
+
+bool qt_u_strToLower(const QString &str, QString *out, const QLocale &locale)
+{
+ return qt_u_strToCase(str, out, locale, ptr_u_strToLower);
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp
index b7272ec..5493ba9 100644
--- a/src/corelib/tools/qstring.cpp
+++ b/src/corelib/tools/qstring.cpp
@@ -106,6 +106,14 @@ QTextCodec *QString::codecForCStrings;
static QHash<void *, QByteArray> *asciiCache = 0;
#endif
+#ifdef QT_USE_ICU
+// qlocale_icu.cpp
+extern bool qt_ucol_strcoll(const QChar *source, int sourceLength, const QChar *target, int targetLength, int *result);
+extern bool qt_u_strToUpper(const QString &str, QString *out, const QLocale &locale);
+extern bool qt_u_strToLower(const QString &str, QString *out, const QLocale &locale);
+#endif
+
+
// internal
int qFindString(const QChar *haystack, int haystackLen, int from,
const QChar *needle, int needleLen, Qt::CaseSensitivity cs);
@@ -431,7 +439,6 @@ const QString::Null QString::null = { };
\ingroup shared
\ingroup string-processing
-
QString stores a string of 16-bit \l{QChar}s, where each QChar
corresponds one Unicode 4.0 character. (Unicode characters
with code values above 65535 are stored using surrogate pairs,
@@ -4829,6 +4836,14 @@ int QString::localeAwareCompare_helper(const QChar *data1, int length1,
TPtrC p2 = TPtrC16(reinterpret_cast<const TUint16 *>(data2), length2);
return p1.CompareC(p2);
#elif defined(Q_OS_UNIX)
+# if defined(QT_USE_ICU)
+ int res;
+ if (qt_ucol_strcoll(data1, length1, data2, length2, &res)) {
+ if (res == 0)
+ res = ucstrcmp(data1, length1, data2, length2);
+ return res;
+ } // else fall through
+# endif
// declared in <string.h>
int delta = strcoll(toLocal8Bit_helper(data1, length1), toLocal8Bit_helper(data2, length2));
if (delta == 0)
@@ -4964,6 +4979,15 @@ QString QString::toLower() const
if (!d->size)
return *this;
+#ifdef QT_USE_ICU
+ {
+ QString result;
+ if (qt_u_strToLower(*this, &result, QLocale()))
+ return result;
+ // else fall through and use Qt's toUpper
+ }
+#endif
+
const ushort *e = d->data + d->size;
// this avoids one out of bounds check in the loop
@@ -5055,6 +5079,15 @@ QString QString::toUpper() const
if (!d->size)
return *this;
+#ifdef QT_USE_ICU
+ {
+ QString result;
+ if (qt_u_strToUpper(*this, &result, QLocale()))
+ return result;
+ // else fall through and use Qt's toUpper
+ }
+#endif
+
const ushort *e = d->data + d->size;
// this avoids one out of bounds check in the loop
diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri
index 849dc63..0c2cf16 100644
--- a/src/corelib/tools/tools.pri
+++ b/src/corelib/tools/tools.pri
@@ -97,6 +97,11 @@ else:SOURCES += tools/qelapsedtimer_generic.cpp
contains(QT_CONFIG, zlib):include($$PWD/../../3rdparty/zlib.pri)
else:include($$PWD/../../3rdparty/zlib_dependency.pri)
+contains(QT_CONFIG,icu) {
+ SOURCES += tools/qlocale_icu.cpp
+ DEFINES += QT_USE_ICU
+}
+
DEFINES += HB_EXPORT=Q_CORE_EXPORT
INCLUDEPATH += ../3rdparty/harfbuzz/src
HEADERS += ../3rdparty/harfbuzz/src/harfbuzz.h