From 1b687d41ecf6b129982318632398526525f882b0 Mon Sep 17 00:00:00 2001 From: Anders Bakken Date: Mon, 31 May 2010 14:03:00 +0200 Subject: Add some convenience functions for QStringRef QStringRef was hard to use for a lot of real-world tasks since it lacked methods similar to the ones in QString for indexOf/lastIndexOf/contains/startsWith/endsWith This patch adds that functionality. I also copied the relevant test cases from QString for the new APIs and introduced a QStringRef test. Merge-request: 625 Reviewed-by: Olivier Goffart Reviewed-by: ossi --- src/corelib/tools/qstring.cpp | 869 ++++++++++++++++++++---- src/corelib/tools/qstring.h | 46 ++ tests/auto/corelib.pro | 1 + tests/auto/qstring/tst_qstring.cpp | 74 ++- tests/auto/qstringref/qstringref.pro | 4 + tests/auto/qstringref/tst_qstringref.cpp | 1058 ++++++++++++++++++++++++++++++ 6 files changed, 1931 insertions(+), 121 deletions(-) create mode 100644 tests/auto/qstringref/qstringref.pro create mode 100644 tests/auto/qstringref/tst_qstringref.cpp diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 6acbcec..e1167fa 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -107,7 +107,23 @@ int qFindString(const QChar *haystack, int haystackLen, int from, const QChar *needle, int needleLen, Qt::CaseSensitivity cs); int qFindStringBoyerMoore(const QChar *haystack, int haystackLen, int from, const QChar *needle, int needleLen, Qt::CaseSensitivity cs); - +static inline int qt_last_index_of(const QChar *haystack, int haystackLen, const QChar &needle, + int from, Qt::CaseSensitivity cs); +static inline int qt_string_count(const QChar *haystack, int haystackLen, + const QChar *needle, int needleLen, + Qt::CaseSensitivity cs); +static inline int qt_string_count(const QChar *haystack, int haystackLen, + const QChar &needle, Qt::CaseSensitivity cs); +static inline int qt_find_latin1_string(const QChar *hay, int size, const QLatin1String &needle, + int from, Qt::CaseSensitivity cs); +static inline bool qt_starts_with(const QChar *haystack, int haystackLen, + const QChar *needle, int needleLen, Qt::CaseSensitivity cs); +static inline bool qt_starts_with(const QChar *haystack, int haystackLen, + const QLatin1String &needle, Qt::CaseSensitivity cs); +static inline bool qt_ends_with(const QChar *haystack, int haystackLen, + const QChar *needle, int needleLen, Qt::CaseSensitivity cs); +static inline bool qt_ends_with(const QChar *haystack, int haystackLen, + const QLatin1String &needle, Qt::CaseSensitivity cs); // Unicode case-insensitive comparison static int ucstricmp(const ushort *a, const ushort *ae, const ushort *b, const ushort *be) @@ -2446,14 +2462,10 @@ int QString::indexOf(const QString &str, int from, Qt::CaseSensitivity cs) const \sa lastIndexOf(), contains(), count() */ + int QString::indexOf(const QLatin1String &str, int from, Qt::CaseSensitivity cs) const { - int len = qstrlen(str.latin1()); - QVarLengthArray s(len); - for (int i = 0; i < len; ++i) - s[i] = str.latin1()[i]; - - return qFindString(unicode(), length(), from, (const QChar *)s.data(), len, cs); + return qt_find_latin1_string(unicode(), size(), str, from, cs); } int qFindString( @@ -2543,6 +2555,23 @@ int QString::indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const return findChar(unicode(), length(), ch, from, cs); } +/*! + \since 4.8 + + \overload indexOf() + + Returns the index position of the first occurrence of the string + reference \a str in this string, searching forward from index + position \a from. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. +*/ +int QString::indexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const +{ + return qFindString(unicode(), length(), from, str.unicode(), str.length(), cs); +} + static int lastIndexOfHelper(const ushort *haystack, int from, const ushort *needle, int sl, Qt::CaseSensitivity cs) { /* @@ -2622,12 +2651,13 @@ int QString::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) c if (from > delta) from = delta; - return lastIndexOfHelper(d->data, from, str.d->data, str.d->size, cs); } /*! \since 4.5 + \overload lastIndexOf() + Returns the index position of the last occurrence of the string \a str in this string, searching backward from index position \a from. If \a from is -1 (default), the search starts at the last @@ -2675,26 +2705,43 @@ int QString::lastIndexOf(const QLatin1String &str, int from, Qt::CaseSensitivity */ int QString::lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const { - ushort c = ch.unicode(); - if (from < 0) - from += d->size; - if (from < 0 || from >= d->size) - return -1; - if (from >= 0) { - const ushort *n = d->data + from; - const ushort *b = d->data; - if (cs == Qt::CaseSensitive) { - for (; n >= b; --n) - if (*n == c) - return n - b; - } else { - c = foldCase(c); - for (; n >= b; --n) - if (foldCase(*n) == c) - return n - b; - } + return qt_last_index_of(unicode(), size(), ch, from, cs); } + +/*! + \since 4.8 + \overload lastIndexOf() + + Returns the index position of the last occurrence of the string + reference \a str in this string, searching backward from index + position \a from. If \a from is -1 (default), the search starts at + the last character; if \a from is -2, at the next to last character + and so on. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa indexOf(), contains(), count() +*/ +int QString::lastIndexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const +{ + const int sl = str.size(); + if (sl == 1) + return lastIndexOf(str.at(0), from, cs); + + const int l = d->size; + if (from < 0) + from += l; + int delta = l - sl; + if (from == l && sl == 0) + return from; + if (from < 0 || from >= l || delta < 0) return -1; + if (from > delta) + from = delta; + + return lastIndexOfHelper(d->data, from, reinterpret_cast(str.unicode()), + str.size(), cs); } #ifndef QT_NO_REGEXP @@ -2869,19 +2916,10 @@ QString& QString::replace(const QRegExp &rx, const QString &after) \sa contains(), indexOf() */ + int QString::count(const QString &str, Qt::CaseSensitivity cs) const { - int num = 0; - int i = -1; - if (d->size > 500 && str.d->size > 5) { - QStringMatcher matcher(str, cs); - while ((i = matcher.indexIn(*this, i + 1)) != -1) - ++num; - } else { - while ((i = indexOf(str, i + 1, cs)) != -1) - ++num; - } - return num; + return qt_string_count(unicode(), size(), str.unicode(), str.size(), cs); } /*! @@ -2889,25 +2927,29 @@ int QString::count(const QString &str, Qt::CaseSensitivity cs) const Returns the number of occurrences of character \a ch in the string. */ + int QString::count(QChar ch, Qt::CaseSensitivity cs) const { - ushort c = ch.unicode(); - int num = 0; - const ushort *i = d->data + d->size; - const ushort *b = d->data; - if (cs == Qt::CaseSensitive) { - while (i != b) - if (*--i == c) - ++num; - } else { - c = foldCase(c); - while (i != b) - if (foldCase(*(--i)) == c) - ++num; + return qt_string_count(unicode(), size(), ch, cs); } - return num; + +/*! + \since 4.8 + \overload count() + Returns the number of (potentially overlapping) occurrences of the + string reference \a str in this string. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa contains(), indexOf() +*/ +int QString::count(const QStringRef &str, Qt::CaseSensitivity cs) const +{ + return qt_string_count(unicode(), size(), str.unicode(), str.size(), cs); } + /*! \fn bool QString::contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const Returns true if this string contains an occurrence of the string @@ -2930,6 +2972,18 @@ int QString::count(QChar ch, Qt::CaseSensitivity cs) const character \a ch; otherwise returns false. */ +/*! \fn bool QString::contains(const QStringRef &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + \since 4.8 + + Returns true if this string contains an occurrence of the string + reference \a str; otherwise returns false. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa indexOf(), count() +*/ + /*! \fn bool QString::contains(const QRegExp &rx) const \overload contains() @@ -3331,22 +3385,8 @@ QString QString::mid(int position, int n) const */ bool QString::startsWith(const QString& s, Qt::CaseSensitivity cs) const { - if (d == &shared_null) - return (s.d == &shared_null); - if (d->size == 0) - return s.d->size == 0; - if (s.d->size > d->size) - return false; - if (cs == Qt::CaseSensitive) { - return qMemEquals(d->data, s.d->data, s.d->size); - } else { - uint last = 0; - uint olast = 0; - for (int i = 0; i < s.d->size; ++i) - if (foldCase(d->data[i], last) != foldCase(s.d->data[i], olast)) - return false; - } - return true; + return qt_starts_with(isNull() ? 0 : unicode(), size(), + s.isNull() ? 0 : s.unicode(), s.size(), cs); } /*! @@ -3354,24 +3394,7 @@ bool QString::startsWith(const QString& s, Qt::CaseSensitivity cs) const */ bool QString::startsWith(const QLatin1String& s, Qt::CaseSensitivity cs) const { - if (d == &shared_null) - return (s.latin1() == 0); - if (d->size == 0) - return !s.latin1() || *s.latin1() == 0; - int slen = qstrlen(s.latin1()); - if (slen > d->size) - return false; - const uchar *latin = (const uchar *)s.latin1(); - if (cs == Qt::CaseSensitive) { - for (int i = 0; i < slen; ++i) - if (d->data[i] != latin[i]) - return false; - } else { - for (int i = 0; i < slen; ++i) - if (foldCase(d->data[i]) != foldCase((ushort)latin[i])) - return false; - } - return true; + return qt_starts_with(isNull() ? 0 : unicode(), size(), s, cs); } /*! @@ -3389,6 +3412,23 @@ bool QString::startsWith(const QChar &c, Qt::CaseSensitivity cs) const } /*! + \since 4.8 + \overload + Returns true if the string starts with the string reference \a s; + otherwise returns false. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa endsWith() +*/ +bool QString::startsWith(const QStringRef &s, Qt::CaseSensitivity cs) const +{ + return qt_starts_with(isNull() ? 0 : unicode(), size(), + s.isNull() ? 0 : s.unicode(), s.size(), cs); +} + +/*! Returns true if the string ends with \a s; otherwise returns false. @@ -3401,49 +3441,34 @@ bool QString::startsWith(const QChar &c, Qt::CaseSensitivity cs) const */ bool QString::endsWith(const QString& s, Qt::CaseSensitivity cs) const { - if (d == &shared_null) - return (s.d == &shared_null); - if (d->size == 0) - return s.d->size == 0; - int pos = d->size - s.d->size; - if (pos < 0) - return false; - if (cs == Qt::CaseSensitive) { - return qMemEquals(d->data + pos, s.d->data, s.d->size); - } else { - uint last = 0; - uint olast = 0; - for (int i = 0; i < s.length(); i++) - if (foldCase(d->data[pos+i], last) != foldCase(s.d->data[i], olast)) - return false; + return qt_ends_with(isNull() ? 0 : unicode(), size(), + s.isNull() ? 0 : s.unicode(), s.size(), cs); } - return true; + +/*! + \since 4.8 + \overload endsWith() + Returns true if the string ends with the string reference \a s; + otherwise returns false. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa startsWith() +*/ +bool QString::endsWith(const QStringRef &s, Qt::CaseSensitivity cs) const +{ + return qt_ends_with(isNull() ? 0 : unicode(), size(), + s.isNull() ? 0 : s.unicode(), s.size(), cs); } + /*! \overload endsWith() */ bool QString::endsWith(const QLatin1String& s, Qt::CaseSensitivity cs) const { - if (d == &shared_null) - return (s.latin1() == 0); - if (d->size == 0) - return !s.latin1() || *s.latin1() == 0; - int slen = qstrlen(s.latin1()); - int pos = d->size - slen; - const uchar *latin = (const uchar *)s.latin1(); - if (pos < 0) - return false; - if (cs == Qt::CaseSensitive) { - for (int i = 0; i < slen; i++) - if (d->data[pos+i] != latin[i]) - return false; - } else { - for (int i = 0; i < slen; i++) - if (foldCase(d->data[pos+i]) != foldCase((ushort)latin[i])) - return false; - } - return true; + return qt_ends_with(isNull() ? 0 : unicode(), size(), s, cs); } /*! @@ -7613,6 +7638,7 @@ QDataStream &operator>>(QDataStream &in, QString &str) Use the startsWith(QString, Qt::CaseSensitive) overload instead. */ + /*! \fn bool QString::endsWith(const QString &s, bool cs) const @@ -8318,4 +8344,607 @@ QStringRef QString::midRef(int position, int n) const return QStringRef(this, position, n); } +/*! + \since 4.8 + + Returns the index position of the first occurrence of the string \a + str in this string reference, searching forward from index position + \a from. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + If \a from is -1, the search starts at the last character; if it is + -2, at the next to last character and so on. + + \sa QString::indexOf(), lastIndexOf(), contains(), count() +*/ +int QStringRef::indexOf(const QString &str, int from, Qt::CaseSensitivity cs) const +{ + return qFindString(unicode(), length(), from, str.unicode(), str.length(), cs); +} + +/*! + \since 4.8 + \overload indexOf() + + Returns the index position of the first occurrence of the + character \a ch in the string reference, searching forward from + index position \a from. Returns -1 if \a ch could not be found. + + \sa QString::indexOf(), lastIndexOf(), contains(), count() +*/ +int QStringRef::indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const +{ + return findChar(unicode(), length(), ch, from, cs); +} + +/*! + \since 4.8 + + Returns the index position of the first occurrence of the string \a + str in this string reference, searching forward from index position + \a from. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + If \a from is -1, the search starts at the last character; if it is + -2, at the next to last character and so on. + + \sa QString::indexOf(), lastIndexOf(), contains(), count() +*/ +int QStringRef::indexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) const +{ + return qt_find_latin1_string(unicode(), size(), str, from, cs); +} + +/*! + \since 4.8 + + \overload indexOf() + + Returns the index position of the first occurrence of the string + reference \a str in this string reference, searching forward from + index position \a from. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::indexOf(), lastIndexOf(), contains(), count() +*/ +int QStringRef::indexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const +{ + return qFindString(unicode(), size(), from, str.unicode(), str.size(), cs); +} + +/*! + \since 4.8 + + Returns the index position of the last occurrence of the string \a + str in this string reference, searching backward from index position + \a from. If \a from is -1 (default), the search starts at the last + character; if \a from is -2, at the next to last character and so + on. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::lastIndexOf(), indexOf(), contains(), count() +*/ +int QStringRef::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) const +{ + const int sl = str.size(); + if (sl == 1) + return lastIndexOf(str.at(0), from, cs); + + const int l = size();; + if (from < 0) + from += l; + int delta = l - sl; + if (from == l && sl == 0) + return from; + if (from < 0 || from >= l || delta < 0) + return -1; + if (from > delta) + from = delta; + + return lastIndexOfHelper(reinterpret_cast(unicode()), from, + reinterpret_cast(str.unicode()), str.size(), cs); +} + +/*! + \since 4.8 + \overload lastIndexOf() + + Returns the index position of the last occurrence of the character + \a ch, searching backward from position \a from. + + \sa QString::lastIndexOf(), indexOf(), contains(), count() +*/ +int QStringRef::lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const +{ + return qt_last_index_of(unicode(), size(), ch, from, cs); +} + +/*! + \since 4.8 + \overload lastIndexOf() + + Returns the index position of the last occurrence of the string \a + str in this string reference, searching backward from index position + \a from. If \a from is -1 (default), the search starts at the last + character; if \a from is -2, at the next to last character and so + on. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::lastIndexOf(), indexOf(), contains(), count() +*/ +int QStringRef::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) const +{ + const int sl = qstrlen(str.latin1()); + if (sl == 1) + return lastIndexOf(QLatin1Char(str.latin1()[0]), from, cs); + + const int l = size(); + if (from < 0) + from += l; + int delta = l - sl; + if (from == l && sl == 0) + return from; + if (from < 0 || from >= l || delta < 0) + return -1; + if (from > delta) + from = delta; + + QVarLengthArray s(sl); + for (int i = 0; i < sl; ++i) + s[i] = str.latin1()[i]; + + return lastIndexOfHelper(reinterpret_cast(unicode()), from, s.data(), sl, cs); +} + +/*! + \since 4.8 + \overload lastIndexOf() + + Returns the index position of the last occurrence of the string + reference \a str in this string reference, searching backward from + index position \a from. If \a from is -1 (default), the search + starts at the last character; if \a from is -2, at the next to last + character and so on. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::lastIndexOf(), indexOf(), contains(), count() +*/ +int QStringRef::lastIndexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const +{ + const int sl = str.size(); + if (sl == 1) + return lastIndexOf(str.at(0), from, cs); + + const int l = size(); + if (from < 0) + from += l; + int delta = l - sl; + if (from == l && sl == 0) + return from; + if (from < 0 || from >= l || delta < 0) + return -1; + if (from > delta) + from = delta; + + return lastIndexOfHelper(reinterpret_cast(unicode()), from, + reinterpret_cast(str.unicode()), + str.size(), cs); +} + +/*! + \since 4.8 + Returns the number of (potentially overlapping) occurrences of + the string \a str in this string reference. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa QString::count(), contains(), indexOf() +*/ +int QStringRef::count(const QString &str, Qt::CaseSensitivity cs) const +{ + return qt_string_count(unicode(), size(), str.unicode(), str.size(), cs); +} + +/*! + \since 4.8 + \overload count() + + Returns the number of occurrences of the character \a ch in the + string reference. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa QString::count(), contains(), indexOf() +*/ +int QStringRef::count(QChar ch, Qt::CaseSensitivity cs) const +{ + return qt_string_count(unicode(), size(), ch, cs); +} + +/*! + \since 4.8 + \overload count() + + Returns the number of (potentially overlapping) occurrences of the + string reference \a str in this string reference. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa QString::count(), contains(), indexOf() +*/ +int QStringRef::count(const QStringRef &str, Qt::CaseSensitivity cs) const +{ + return qt_string_count(unicode(), size(), str.unicode(), str.size(), cs); +} + +/*! + \since 4.8 + + Returns true if the string reference starts with \a str; otherwise + returns false. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa QString::startsWith(), endsWith() +*/ +bool QStringRef::startsWith(const QString &str, Qt::CaseSensitivity cs) const +{ + return qt_starts_with(isNull() ? 0 : unicode(), size(), + str.isNull() ? 0 : str.unicode(), str.size(), cs); +} + +/*! + \since 4.8 + \overload startsWith() + \sa QString::startsWith(), endsWith() +*/ +bool QStringRef::startsWith(QLatin1String str, Qt::CaseSensitivity cs) const +{ + return qt_starts_with(isNull() ? 0 : unicode(), size(), str, cs); +} + +/*! + \since 4.8 + \overload startsWith() + \sa QString::startsWith(), endsWith() +*/ +bool QStringRef::startsWith(const QStringRef &str, Qt::CaseSensitivity cs) const +{ + return qt_starts_with(isNull() ? 0 : unicode(), size(), + str.isNull() ? 0 : str.unicode(), str.size(), cs); +} + +/*! + \since 4.8 + \overload startsWith() + + Returns true if the string reference starts with \a ch; otherwise + returns false. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::startsWith(), endsWith() +*/ +bool QStringRef::startsWith(QChar ch, Qt::CaseSensitivity cs) const +{ + if (!isEmpty()) { + const ushort *data = reinterpret_cast(unicode()); + return (cs == Qt::CaseSensitive + ? data[0] == ch + : foldCase(data[0]) == foldCase(ch.unicode())); + } else { + return false; + } +} + +/*! + \since 4.8 + Returns true if the string reference ends with \a str; otherwise + returns false. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::endsWith(), startsWith() +*/ +bool QStringRef::endsWith(const QString &str, Qt::CaseSensitivity cs) const +{ + return qt_ends_with(isNull() ? 0 : unicode(), size(), + str.isNull() ? 0 : str.unicode(), str.size(), cs); +} + +/*! + \since 4.8 + \overload endsWith() + + Returns true if the string reference ends with \a ch; otherwise + returns false. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::endsWith(), endsWith() +*/ +bool QStringRef::endsWith(QChar ch, Qt::CaseSensitivity cs) const +{ + if (!isEmpty()) { + const ushort *data = reinterpret_cast(unicode()); + const int size = length(); + return (cs == Qt::CaseSensitive + ? data[size - 1] == ch + : foldCase(data[size - 1]) == foldCase(ch.unicode())); + } else { + return false; + } +} + +/*! + \since 4.8 + \overload endsWith() + \sa QString::endsWith(), endsWith() +*/ +bool QStringRef::endsWith(QLatin1String str, Qt::CaseSensitivity cs) const +{ + return qt_ends_with(isNull() ? 0 : unicode(), size(), str, cs); +} + +/*! + \since 4.8 + \overload endsWith() + \sa QString::endsWith(), endsWith() +*/ +bool QStringRef::endsWith(const QStringRef &str, Qt::CaseSensitivity cs) const +{ + return qt_ends_with(isNull() ? 0 : unicode(), size(), + str.isNull() ? 0 : str.unicode(), str.size(), cs); +} + + +/*! \fn bool QStringRef::contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + + \since 4.8 + Returns true if this string reference contains an occurrence of + the string \a str; otherwise returns false. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa indexOf(), count() +*/ + +/*! \fn bool QStringRef::contains(QChar ch, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + + \overload contains() + \since 4.8 + + Returns true if this string contains an occurrence of the + character \a ch; otherwise returns false. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + +*/ + +/*! \fn bool QStringRef::contains(const QStringRef &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + \overload contains() + \since 4.8 + + Returns true if this string reference contains an occurrence of + the string reference \a str; otherwise returns false. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa indexOf(), count() +*/ + +/*! \fn bool QStringRef::contains(QLatin1String str, Qt::CaseSensitivity cs) const + \since 4,8 + \overload contains() + + Returns true if this string reference contains an occurrence of + the string \a str; otherwise returns false. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa indexOf(), count() +*/ + +static inline int qt_last_index_of(const QChar *haystack, int haystackLen, const QChar &needle, + int from, Qt::CaseSensitivity cs) +{ + ushort c = needle.unicode(); + if (from < 0) + from += haystackLen; + if (from < 0 || from >= haystackLen) + return -1; + if (from >= 0) { + const ushort *b = reinterpret_cast(haystack); + const ushort *n = b + from; + if (cs == Qt::CaseSensitive) { + for (; n >= b; --n) + if (*n == c) + return n - b; + } else { + c = foldCase(c); + for (; n >= b; --n) + if (foldCase(*n) == c) + return n - b; + } + } + return -1; + + +} + +static inline int qt_string_count(const QChar *haystack, int haystackLen, + const QChar *needle, int needleLen, + Qt::CaseSensitivity cs) +{ + int num = 0; + int i = -1; + if (haystackLen > 500 && needleLen > 5) { + QStringMatcher matcher(needle, needleLen, cs); + while ((i = matcher.indexIn(haystack, haystackLen, i + 1)) != -1) + ++num; + } else { + while ((i = qFindString(haystack, haystackLen, i + 1, needle, needleLen, cs)) != -1) + ++num; + } + return num; +} + +static inline int qt_string_count(const QChar *unicode, int size, const QChar &ch, + Qt::CaseSensitivity cs) +{ + ushort c = ch.unicode(); + int num = 0; + const ushort *b = reinterpret_cast(unicode); + const ushort *i = b + size; + if (cs == Qt::CaseSensitive) { + while (i != b) + if (*--i == c) + ++num; + } else { + c = foldCase(c); + while (i != b) + if (foldCase(*(--i)) == c) + ++num; + } + return num; +} + +static inline int qt_find_latin1_string(const QChar *haystack, int size, + const QLatin1String &needle, + int from, Qt::CaseSensitivity cs) +{ + const char *latin1 = needle.latin1(); + int len = qstrlen(latin1); + QVarLengthArray s(len); + for (int i = 0; i < len; ++i) + s[i] = latin1[i]; + + return qFindString(haystack, size, from, + reinterpret_cast(s.constData()), len, cs); +} + +static inline bool qt_starts_with(const QChar *haystack, int haystackLen, + const QChar *needle, int needleLen, Qt::CaseSensitivity cs) +{ + if (!haystack) + return !needle; + if (haystackLen == 0) + return needleLen == 0; + if (needleLen > haystackLen) + return false; + + const ushort *h = reinterpret_cast(haystack); + const ushort *n = reinterpret_cast(needle); + + if (cs == Qt::CaseSensitive) { + return qMemEquals(h, n, needleLen); + } else { + uint last = 0; + uint olast = 0; + for (int i = 0; i < needleLen; ++i) + if (foldCase(h[i], last) != foldCase(n[i], olast)) + return false; + } + return true; +} + +static inline bool qt_starts_with(const QChar *haystack, int haystackLen, + const QLatin1String &needle, Qt::CaseSensitivity cs) +{ + if (!haystack) + return !needle.latin1(); + if (haystackLen == 0) + return !needle.latin1() || *needle.latin1() == 0; + const int slen = qstrlen(needle.latin1()); + if (slen > haystackLen) + return false; + const ushort *data = reinterpret_cast(haystack); + const uchar *latin = reinterpret_cast(needle.latin1()); + if (cs == Qt::CaseSensitive) { + for (int i = 0; i < slen; ++i) + if (data[i] != latin[i]) + return false; + } else { + for (int i = 0; i < slen; ++i) + if (foldCase(data[i]) != foldCase((ushort)latin[i])) + return false; + } + return true; +} + +static inline bool qt_ends_with(const QChar *haystack, int haystackLen, + const QChar *needle, int needleLen, Qt::CaseSensitivity cs) +{ + if (!haystack) + return !needle; + if (haystackLen == 0) + return needleLen == 0; + const int pos = haystackLen - needleLen; + if (pos < 0) + return false; + + const ushort *h = reinterpret_cast(haystack); + const ushort *n = reinterpret_cast(needle); + + if (cs == Qt::CaseSensitive) { + return qMemEquals(h + pos, n, needleLen); + } else { + uint last = 0; + uint olast = 0; + for (int i = 0; i < needleLen; i++) + if (foldCase(h[pos+i], last) != foldCase(n[i], olast)) + return false; + } + return true; +} + + +static inline bool qt_ends_with(const QChar *haystack, int haystackLen, + const QLatin1String &needle, Qt::CaseSensitivity cs) +{ + if (!haystack) + return !needle.latin1(); + if (haystackLen == 0) + return !needle.latin1() || *needle.latin1() == 0; + const int slen = qstrlen(needle.latin1()); + int pos = haystackLen - slen; + if (pos < 0) + return false; + const uchar *latin = reinterpret_cast(needle.latin1()); + const ushort *data = reinterpret_cast(haystack); + if (cs == Qt::CaseSensitive) { + for (int i = 0; i < slen; i++) + if (data[pos+i] != latin[i]) + return false; + } else { + for (int i = 0; i < slen; i++) + if (foldCase(data[pos+i]) != foldCase((ushort)latin[i])) + return false; + } + return true; +} + QT_END_NAMESPACE diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index a1c4e77..ed87acf 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -198,14 +198,18 @@ public: int indexOf(QChar c, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int indexOf(const QString &s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int indexOf(const QLatin1String &s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int indexOf(const QStringRef &s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int lastIndexOf(QChar c, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int lastIndexOf(const QString &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int lastIndexOf(const QLatin1String &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int lastIndexOf(const QStringRef &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; inline QBool contains(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; inline QBool contains(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + inline QBool contains(const QStringRef &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int count(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int count(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int count(const QStringRef &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; #ifndef QT_NO_REGEXP int indexOf(const QRegExp &, int from = 0) const; @@ -241,9 +245,11 @@ public: QStringRef midRef(int position, int n = -1) const Q_REQUIRED_RESULT; bool startsWith(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool startsWith(const QStringRef &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool startsWith(const QLatin1String &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool startsWith(const QChar &c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool endsWith(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool endsWith(const QStringRef &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool endsWith(const QLatin1String &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool endsWith(const QChar &c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; @@ -901,6 +907,8 @@ inline QString::const_iterator QString::constEnd() const { return reinterpret_cast(d->data + d->size); } inline QBool QString::contains(const QString &s, Qt::CaseSensitivity cs) const { return QBool(indexOf(s, 0, cs) != -1); } +inline QBool QString::contains(const QStringRef &s, Qt::CaseSensitivity cs) const +{ return QBool(indexOf(s, 0, cs) != -1); } inline QBool QString::contains(QChar c, Qt::CaseSensitivity cs) const { return QBool(indexOf(c, 0, cs) != -1); } @@ -1122,6 +1130,34 @@ public: m_size = other.m_size; return *this; } + int indexOf(const QString &str, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int indexOf(QChar ch, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int indexOf(QLatin1String str, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int indexOf(const QStringRef &str, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int lastIndexOf(const QString &str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int lastIndexOf(QChar ch, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int lastIndexOf(QLatin1String str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int lastIndexOf(const QStringRef &str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + + inline QBool contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + inline QBool contains(QChar ch, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + inline QBool contains(QLatin1String str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + inline QBool contains(const QStringRef &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + + int count(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int count(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int count(const QStringRef &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + + bool startsWith(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool startsWith(QLatin1String s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool startsWith(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool startsWith(const QStringRef &c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + + bool endsWith(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool endsWith(QLatin1String s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool endsWith(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool endsWith(const QStringRef &c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + inline QStringRef &operator=(const QString *string); inline const QChar *unicode() const { @@ -1240,6 +1276,16 @@ inline int QStringRef::localeAwareCompare(const QStringRef &s1, const QString &s inline int QStringRef::localeAwareCompare(const QStringRef &s1, const QStringRef &s2) { return QString::localeAwareCompare_helper(s1.constData(), s1.length(), s2.constData(), s2.length()); } +inline QBool QStringRef::contains(const QString &s, Qt::CaseSensitivity cs) const +{ return QBool(indexOf(s, 0, cs) != -1); } +inline QBool QStringRef::contains(QLatin1String s, Qt::CaseSensitivity cs) const +{ return QBool(indexOf(s, 0, cs) != -1); } +inline QBool QStringRef::contains(QChar c, Qt::CaseSensitivity cs) const +{ return QBool(indexOf(c, 0, cs) != -1); } +inline QBool QStringRef::contains(const QStringRef &s, Qt::CaseSensitivity cs) const +{ return QBool(indexOf(s, 0, cs) != -1); } + + QT_END_NAMESPACE diff --git a/tests/auto/corelib.pro b/tests/auto/corelib.pro index 259be4c..a7ab54d 100644 --- a/tests/auto/corelib.pro +++ b/tests/auto/corelib.pro @@ -75,6 +75,7 @@ SUBDIRS=\ qstringbuilder4 \ qstringlist \ qstringmatcher \ + qstringref \ qtconcurrentfilter \ qtconcurrentiteratekernel \ qtconcurrentmap \ diff --git a/tests/auto/qstring/tst_qstring.cpp b/tests/auto/qstring/tst_qstring.cpp index 1bea4b7..eb2d5f1 100644 --- a/tests/auto/qstring/tst_qstring.cpp +++ b/tests/auto/qstring/tst_qstring.cpp @@ -57,6 +57,10 @@ Q_DECLARE_METATYPE(qlonglong) //TESTED_CLASS= //TESTED_FILES= +#define CREATE_REF(string) \ + const QString padded = QString::fromLatin1(" %1 ").arg(string); \ + QStringRef ref = padded.midRef(1, padded.size() - 2); + class tst_QString : public QObject { Q_OBJECT @@ -135,6 +139,7 @@ private slots: void leftRef(); void stringRef(); void contains(); + void count(); void lastIndexOf_data(); void lastIndexOf(); void indexOf_data(); @@ -1063,12 +1068,14 @@ void tst_QString::indexOf() QFETCH( int, startpos ); QFETCH( bool, bcs ); QFETCH( int, resultpos ); + CREATE_REF(needle); Qt::CaseSensitivity cs = bcs ? Qt::CaseSensitive : Qt::CaseInsensitive; bool needleIsLatin = (QString::fromLatin1(needle.toLatin1()) == needle); QCOMPARE( haystack.indexOf(needle, startpos, cs), resultpos ); + QCOMPARE( haystack.indexOf(ref, startpos, cs), resultpos ); if (needleIsLatin) { QCOMPARE( haystack.indexOf(needle.toLatin1(), startpos, cs), resultpos ); QCOMPARE( haystack.indexOf(needle.toLatin1().data(), startpos, cs), resultpos ); @@ -1096,12 +1103,14 @@ void tst_QString::indexOf() if (cs == Qt::CaseSensitive) { QCOMPARE( haystack.indexOf(needle, startpos), resultpos ); + QCOMPARE( haystack.indexOf(ref, startpos), resultpos ); if (needleIsLatin) { QCOMPARE( haystack.indexOf(needle.toLatin1(), startpos), resultpos ); QCOMPARE( haystack.indexOf(needle.toLatin1().data(), startpos), resultpos ); } if (startpos == 0) { QCOMPARE( haystack.indexOf(needle), resultpos ); + QCOMPARE( haystack.indexOf(ref), resultpos ); if (needleIsLatin) { QCOMPARE( haystack.indexOf(needle.toLatin1()), resultpos ); QCOMPARE( haystack.indexOf(needle.toLatin1().data()), resultpos ); @@ -1110,6 +1119,7 @@ void tst_QString::indexOf() } if (needle.size() == 1) { QCOMPARE(haystack.indexOf(needle.at(0), startpos, cs), resultpos); + QCOMPARE(haystack.indexOf(ref.at(0), startpos, cs), resultpos); } } @@ -1170,14 +1180,17 @@ void tst_QString::indexOf2() QFETCH( QString, haystack ); QFETCH( QString, needle ); QFETCH( int, resultpos ); + CREATE_REF(needle); QByteArray chaystack = haystack.toLatin1(); QByteArray cneedle = needle.toLatin1(); int got; QCOMPARE( haystack.indexOf(needle, 0, Qt::CaseSensitive), resultpos ); + QCOMPARE( haystack.indexOf(ref, 0, Qt::CaseSensitive), resultpos ); QCOMPARE( QStringMatcher(needle, Qt::CaseSensitive).indexIn(haystack, 0), resultpos ); QCOMPARE( haystack.indexOf(needle, 0, Qt::CaseInsensitive), resultpos ); + QCOMPARE( haystack.indexOf(ref, 0, Qt::CaseInsensitive), resultpos ); QCOMPARE( QStringMatcher(needle, Qt::CaseInsensitive).indexIn(haystack, 0), resultpos ); if ( needle.length() > 0 ) { got = haystack.lastIndexOf( needle, -1, Qt::CaseSensitive ); @@ -1244,10 +1257,12 @@ void tst_QString::lastIndexOf() QFETCH(int, from); QFETCH(int, expected); QFETCH(bool, caseSensitive); + CREATE_REF(needle); Qt::CaseSensitivity cs = (caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); QCOMPARE(haystack.lastIndexOf(needle, from, cs), expected); + QCOMPARE(haystack.lastIndexOf(ref, from, cs), expected); QCOMPARE(haystack.lastIndexOf(needle.toLatin1(), from, cs), expected); QCOMPARE(haystack.lastIndexOf(needle.toLatin1().data(), from, cs), expected); @@ -1277,20 +1292,23 @@ void tst_QString::lastIndexOf() if (cs == Qt::CaseSensitive) { QCOMPARE(haystack.lastIndexOf(needle, from), expected); + QCOMPARE(haystack.lastIndexOf(ref, from), expected); QCOMPARE(haystack.lastIndexOf(needle.toLatin1(), from), expected); QCOMPARE(haystack.lastIndexOf(needle.toLatin1().data(), from), expected); if (from == -1) { QCOMPARE(haystack.lastIndexOf(needle), expected); + QCOMPARE(haystack.lastIndexOf(ref), expected); QCOMPARE(haystack.lastIndexOf(needle.toLatin1()), expected); QCOMPARE(haystack.lastIndexOf(needle.toLatin1().data()), expected); } } if (needle.size() == 1) { QCOMPARE(haystack.lastIndexOf(needle.at(0), from), expected); + QCOMPARE(haystack.lastIndexOf(ref.at(0), from), expected); } } -void tst_QString::contains() +void tst_QString::count() { QString a; a="ABCDEFGHIEfGEFG"; // 15 chars @@ -1305,8 +1323,42 @@ void tst_QString::contains() QCOMPARE(a.count( "", Qt::CaseInsensitive), 16); QCOMPARE(a.count(QRegExp("[FG][HI]")),1); QCOMPARE(a.count(QRegExp("[G][HE]")),2); + + CREATE_REF(QLatin1String("FG")); + QCOMPARE(a.count(ref),2); + QCOMPARE(a.count(ref,Qt::CaseInsensitive),3); + QCOMPARE(a.count( QStringRef(), Qt::CaseInsensitive), 16); + QStringRef emptyRef(&a, 0, 0); + QCOMPARE(a.count( emptyRef, Qt::CaseInsensitive), 16); + } +void tst_QString::contains() +{ + QString a; + a="ABCDEFGHIEfGEFG"; // 15 chars + QVERIFY(a.contains('A')); + QVERIFY(!a.contains('Z')); + QVERIFY(a.contains('E')); + QVERIFY(a.contains('F')); + QVERIFY(a.contains('F',Qt::CaseInsensitive)); + QVERIFY(a.contains("FG")); + QVERIFY(a.contains("FG",Qt::CaseInsensitive)); + QVERIFY(a.contains( QString(), Qt::CaseInsensitive)); + QVERIFY(a.contains( "", Qt::CaseInsensitive)); + QVERIFY(a.contains(QRegExp("[FG][HI]"))); + QVERIFY(a.contains(QRegExp("[G][HE]"))); + + CREATE_REF(QLatin1String("FG")); + QVERIFY(a.contains(ref)); + QVERIFY(a.contains(ref, Qt::CaseInsensitive)); + QVERIFY(a.contains( QStringRef(), Qt::CaseInsensitive)); + QStringRef emptyRef(&a, 0, 0); + QVERIFY(a.contains(emptyRef, Qt::CaseInsensitive)); + +} + + void tst_QString::left() { QString a; @@ -2827,6 +2879,14 @@ void tst_QString::startsWith() QVERIFY( !a.startsWith(QChar(), Qt::CaseSensitive) ); QVERIFY( !a.startsWith(QLatin1Char(0), Qt::CaseSensitive) ); +#define TEST_REF_STARTS_WITH(string, yes) { CREATE_REF(string); QCOMPARE(a.startsWith(ref), yes); } + + TEST_REF_STARTS_WITH("A", true); + TEST_REF_STARTS_WITH("AB", true); + TEST_REF_STARTS_WITH("C", false); + TEST_REF_STARTS_WITH("ABCDEF", false); +#undef TEST_REF_STARTS_WITH + a = ""; QVERIFY( a.startsWith("") ); QVERIFY( a.startsWith(QString::null) ); @@ -2852,6 +2912,7 @@ void tst_QString::startsWith() QVERIFY( !a.startsWith(QLatin1Char(0)) ); QVERIFY( !a.startsWith(QLatin1Char('x')) ); QVERIFY( !a.startsWith(QChar()) ); + } void tst_QString::endsWith() @@ -2919,6 +2980,17 @@ void tst_QString::endsWith() QVERIFY( !a.endsWith(QChar(), Qt::CaseSensitive) ); QVERIFY( !a.endsWith(QLatin1Char(0), Qt::CaseSensitive) ); + +#define TEST_REF_ENDS_WITH(string, yes) { CREATE_REF(string); QCOMPARE(a.endsWith(ref), yes); } + TEST_REF_ENDS_WITH(QLatin1String("B"), true); + TEST_REF_ENDS_WITH(QLatin1String("AB"), true); + TEST_REF_ENDS_WITH(QLatin1String("C"), false); + TEST_REF_ENDS_WITH(QLatin1String("ABCDEF"), false); + TEST_REF_ENDS_WITH(QLatin1String(""), true); + TEST_REF_ENDS_WITH(QLatin1String(0), true); + +#undef TEST_REF_STARTS_WITH + a = ""; QVERIFY( a.endsWith("") ); QVERIFY( a.endsWith(QString::null) ); diff --git a/tests/auto/qstringref/qstringref.pro b/tests/auto/qstringref/qstringref.pro new file mode 100644 index 0000000..48e7ddf --- /dev/null +++ b/tests/auto/qstringref/qstringref.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +SOURCES += tst_qstringref.cpp + +QT = core diff --git a/tests/auto/qstringref/tst_qstringref.cpp b/tests/auto/qstringref/tst_qstringref.cpp new file mode 100644 index 0000000..1980901 --- /dev/null +++ b/tests/auto/qstringref/tst_qstringref.cpp @@ -0,0 +1,1058 @@ +/**************************************************************************** +** +** 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 test suite 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 +#include +#include + +#include +#include + +Q_DECLARE_METATYPE(qlonglong) + +//TESTED_CLASS= +//TESTED_FILES= + + +class tst_QStringRef : public QObject +{ + Q_OBJECT + +public: + tst_QStringRef(); + virtual ~tst_QStringRef(); + + +public slots: + void init(); + void cleanup(); +private slots: + void endsWith(); + void startsWith(); + void contains(); + void count(); + void lastIndexOf_data(); + void lastIndexOf(); + void indexOf_data(); + void indexOf(); + void indexOf2_data(); + void indexOf2(); + void length_data(); + void length(); + void isEmpty(); + void compare_data(); + void compare(); + void operator_eqeq_nullstring(); + void localeAwareCompare_data(); + void localeAwareCompare(); +}; + +static QStringRef emptyRef() +{ + static const QString empty(""); + return empty.midRef(0); +} + +#define CREATE_REF(string) \ + const QString padded = QString::fromLatin1(" %1 ").arg(string); \ + QStringRef ref = padded.midRef(1, padded.size() - 2); + +typedef QList IntList; + +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(IntList) + +// This next bit is needed for the NAN and INF in string -> number conversion tests +#include +#include +#include +#if defined(Q_WS_WIN) +# include +// mingw defines NAN and INFINITY to 0/0 and x/0 +# if defined(Q_CC_GNU) +# undef NAN +# undef INFINITY +# else +# define isnan(d) _isnan(d) +# endif +#endif +#if defined(Q_OS_MAC) && !defined isnan +#define isnan(d) __isnand(d) +#endif +#if defined(Q_OS_SOLARIS) +# include +#endif +#if defined(Q_OS_OSF) && (defined(__DECC) || defined(__DECCXX)) +# define INFINITY DBL_INFINITY +# define NAN DBL_QNAN +#endif +#if defined(Q_OS_IRIX) && defined(Q_CC_GNU) +# include +# define isnan(d) isnand(d) +#endif + +enum { + LittleEndian, + BigEndian +#ifdef Q_BYTE_ORDER +# if Q_BYTE_ORDER == Q_BIG_ENDIAN + , ByteOrder = BigEndian +# elif Q_BYTE_ORDER == Q_LITTLE_ENDIAN + , ByteOrder = LittleEndian +# else +# error "undefined byte order" +# endif +}; +#else +}; +static const unsigned int one = 1; +static const bool ByteOrder = ((*((unsigned char *) &one) == 0) ? BigEndian : LittleEndian); +#endif +#if !defined(INFINITY) +static const unsigned char be_inf_bytes[] = { 0x7f, 0xf0, 0, 0, 0, 0, 0,0 }; +static const unsigned char le_inf_bytes[] = { 0, 0, 0, 0, 0, 0, 0xf0, 0x7f }; +static inline double inf() +{ + if (ByteOrder == BigEndian) + return *reinterpret_cast(be_inf_bytes); + return *reinterpret_cast(le_inf_bytes); +} +# define INFINITY (::inf()) +#endif +#if !defined(NAN) +static const unsigned char be_nan_bytes[] = { 0x7f, 0xf8, 0, 0, 0, 0, 0,0 }; +static const unsigned char le_nan_bytes[] = { 0, 0, 0, 0, 0, 0, 0xf8, 0x7f }; +static inline double nan() +{ + if (ByteOrder == BigEndian) + return *reinterpret_cast(be_nan_bytes); + return *reinterpret_cast(le_nan_bytes); +} +# define NAN(::nan()) +#endif + +tst_QStringRef::tst_QStringRef() +{ +} + +tst_QStringRef::~tst_QStringRef() +{ +} + +void tst_QStringRef::init() +{ +} + +void tst_QStringRef::cleanup() +{ + QLocale::setDefault(QString(QLatin1Char('C'))); +} + +void tst_QStringRef::length_data() +{ + QTest::addColumn("s1"); + QTest::addColumn("res"); + + QTest::newRow("data0") << QString("Test") << 4; + QTest::newRow("data1") << QString("The quick brown fox jumps over the lazy dog") << 43; + QTest::newRow("data2") << QString() << 0; + QTest::newRow("data3") << QString("A") << 1; + QTest::newRow("data4") << QString("AB") << 2; + QTest::newRow("data5") << QString("AB\n") << 3; + QTest::newRow("data6") << QString("AB\nC") << 4; + QTest::newRow("data7") << QString("\n") << 1; + QTest::newRow("data8") << QString("\nA") << 2; + QTest::newRow("data9") << QString("\nAB") << 3; + QTest::newRow("data10") << QString("\nAB\nCDE") << 7; + QTest::newRow("data11") << QString("shdnftrheid fhgnt gjvnfmd chfugkh bnfhg thgjf vnghturkf chfnguh bjgnfhvygh hnbhgutjfv dhdnjds dcjs d") << 100; + QTest::newRow("data12") << QString("") << 0; +} + + +void tst_QStringRef::length() +{ + QFETCH(QString, s1); + CREATE_REF(s1); + QTEST(ref.length(), "res"); +} + + +void tst_QStringRef::isEmpty() +{ + QStringRef a; + QVERIFY(a.isEmpty()); + QVERIFY(emptyRef().isEmpty()); + CREATE_REF("Not empty"); + QVERIFY(!ref.isEmpty()); +} + +void tst_QStringRef::indexOf_data() +{ + QTest::addColumn("haystack"); + QTest::addColumn("needle"); + QTest::addColumn("startpos"); + QTest::addColumn("bcs"); + QTest::addColumn("resultpos"); + + QTest::newRow("data0") << QString("abc") << QString("a") << 0 << true << 0; + QTest::newRow("data1") << QString("abc") << QString("a") << 0 << false << 0; + QTest::newRow("data2") << QString("abc") << QString("A") << 0 << true << -1; + QTest::newRow("data3") << QString("abc") << QString("A") << 0 << false << 0; + QTest::newRow("data4") << QString("abc") << QString("a") << 1 << true << -1; + QTest::newRow("data5") << QString("abc") << QString("a") << 1 << false << -1; + QTest::newRow("data6") << QString("abc") << QString("A") << 1 << true << -1; + QTest::newRow("data7") << QString("abc") << QString("A") << 1 << false << -1; + QTest::newRow("data8") << QString("abc") << QString("b") << 0 << true << 1; + QTest::newRow("data9") << QString("abc") << QString("b") << 0 << false << 1; + QTest::newRow("data10") << QString("abc") << QString("B") << 0 << true << -1; + QTest::newRow("data11") << QString("abc") << QString("B") << 0 << false << 1; + QTest::newRow("data12") << QString("abc") << QString("b") << 1 << true << 1; + QTest::newRow("data13") << QString("abc") << QString("b") << 1 << false << 1; + QTest::newRow("data14") << QString("abc") << QString("B") << 1 << true << -1; + QTest::newRow("data15") << QString("abc") << QString("B") << 1 << false << 1; + QTest::newRow("data16") << QString("abc") << QString("b") << 2 << true << -1; + QTest::newRow("data17") << QString("abc") << QString("b") << 2 << false << -1; + + QTest::newRow("data20") << QString("ABC") << QString("A") << 0 << true << 0; + QTest::newRow("data21") << QString("ABC") << QString("A") << 0 << false << 0; + QTest::newRow("data22") << QString("ABC") << QString("a") << 0 << true << -1; + QTest::newRow("data23") << QString("ABC") << QString("a") << 0 << false << 0; + QTest::newRow("data24") << QString("ABC") << QString("A") << 1 << true << -1; + QTest::newRow("data25") << QString("ABC") << QString("A") << 1 << false << -1; + QTest::newRow("data26") << QString("ABC") << QString("a") << 1 << true << -1; + QTest::newRow("data27") << QString("ABC") << QString("a") << 1 << false << -1; + QTest::newRow("data28") << QString("ABC") << QString("B") << 0 << true << 1; + QTest::newRow("data29") << QString("ABC") << QString("B") << 0 << false << 1; + QTest::newRow("data30") << QString("ABC") << QString("b") << 0 << true << -1; + QTest::newRow("data31") << QString("ABC") << QString("b") << 0 << false << 1; + QTest::newRow("data32") << QString("ABC") << QString("B") << 1 << true << 1; + QTest::newRow("data33") << QString("ABC") << QString("B") << 1 << false << 1; + QTest::newRow("data34") << QString("ABC") << QString("b") << 1 << true << -1; + QTest::newRow("data35") << QString("ABC") << QString("b") << 1 << false << 1; + QTest::newRow("data36") << QString("ABC") << QString("B") << 2 << true << -1; + QTest::newRow("data37") << QString("ABC") << QString("B") << 2 << false << -1; + + QTest::newRow("data40") << QString("aBc") << QString("bc") << 0 << true << -1; + QTest::newRow("data41") << QString("aBc") << QString("Bc") << 0 << true << 1; + QTest::newRow("data42") << QString("aBc") << QString("bC") << 0 << true << -1; + QTest::newRow("data43") << QString("aBc") << QString("BC") << 0 << true << -1; + QTest::newRow("data44") << QString("aBc") << QString("bc") << 0 << false << 1; + QTest::newRow("data45") << QString("aBc") << QString("Bc") << 0 << false << 1; + QTest::newRow("data46") << QString("aBc") << QString("bC") << 0 << false << 1; + QTest::newRow("data47") << QString("aBc") << QString("BC") << 0 << false << 1; + QTest::newRow("data48") << QString("AbC") << QString("bc") << 0 << true << -1; + QTest::newRow("data49") << QString("AbC") << QString("Bc") << 0 << true << -1; + QTest::newRow("data50") << QString("AbC") << QString("bC") << 0 << true << 1; + QTest::newRow("data51") << QString("AbC") << QString("BC") << 0 << true << -1; + QTest::newRow("data52") << QString("AbC") << QString("bc") << 0 << false << 1; + QTest::newRow("data53") << QString("AbC") << QString("Bc") << 0 << false << 1; + + QTest::newRow("data54") << QString("AbC") << QString("bC") << 0 << false << 1; + QTest::newRow("data55") << QString("AbC") << QString("BC") << 0 << false << 1; + QTest::newRow("data56") << QString("AbC") << QString("BC") << 1 << false << 1; + QTest::newRow("data57") << QString("AbC") << QString("BC") << 2 << false << -1; +#if 0 + QTest::newRow("null-in-null") << QString() << QString() << 0 << false << 0; + QTest::newRow("empty-in-null") << QString() << QString("") << 0 << false << 0; + QTest::newRow("null-in-empty") << QString("") << QString() << 0 << false << 0; + QTest::newRow("empty-in-empty") << QString("") << QString("") << 0 << false << 0; +#endif + + + QString s1 = "abc"; + s1 += QChar(0xb5); + QString s2; + s2 += QChar(0x3bc); + QTest::newRow("data58") << QString(s1) << QString(s2) << 0 << false << 3; + s2.prepend("C"); + QTest::newRow("data59") << QString(s1) << QString(s2) << 0 << false << 2; + + QString veryBigHaystack(500, 'a'); + veryBigHaystack += 'B'; + QTest::newRow("BoyerMooreStressTest") << veryBigHaystack << veryBigHaystack << 0 << true << 0; + QTest::newRow("BoyerMooreStressTest2") << veryBigHaystack + 'c' << veryBigHaystack << 0 << true << 0; + QTest::newRow("BoyerMooreStressTest3") << 'c' + veryBigHaystack << veryBigHaystack << 0 << true << 1; + QTest::newRow("BoyerMooreStressTest4") << veryBigHaystack << veryBigHaystack + 'c' << 0 << true << -1; + QTest::newRow("BoyerMooreStressTest5") << veryBigHaystack << 'c' + veryBigHaystack << 0 << true << -1; + QTest::newRow("BoyerMooreStressTest6") << 'd' + veryBigHaystack << 'c' + veryBigHaystack << 0 << true << -1; + QTest::newRow("BoyerMooreStressTest6") << veryBigHaystack + 'c' << 'c' + veryBigHaystack << 0 << true << -1; + + QTest::newRow("BoyerMooreInsensitiveStressTest") << veryBigHaystack << veryBigHaystack << 0 << false << 0; + +} + +void tst_QStringRef::indexOf() +{ + QFETCH(QString, haystack); + QFETCH(QString, needle); + QFETCH(int, startpos); + QFETCH(bool, bcs); + QFETCH(int, resultpos); + + const QString haystackPadded = QString::fromLatin1(" %1 ").arg(haystack); + const QString needlePadded = QString::fromLatin1(" %1 ").arg(needle); + const QStringRef haystackRef(&haystackPadded, 1, haystack.size()); + const QStringRef needleRef(&needlePadded, 1, needle.size()); + + Qt::CaseSensitivity cs = bcs ? Qt::CaseSensitive : Qt::CaseInsensitive; + + QCOMPARE(haystack.indexOf(needle, startpos, cs), resultpos); + QCOMPARE(haystackRef.indexOf(needle, startpos, cs), resultpos); + QCOMPARE(haystackRef.indexOf(needleRef, startpos, cs), resultpos); + QCOMPARE(haystack.indexOf(needleRef, startpos, cs), resultpos); + + if (cs == Qt::CaseSensitive) { + QCOMPARE(haystack.indexOf(needle, startpos), resultpos); + QCOMPARE(haystackRef.indexOf(needle, startpos), resultpos); + QCOMPARE(haystackRef.indexOf(needleRef, startpos), resultpos); + QCOMPARE(haystack.indexOf(needleRef, startpos), resultpos); + if (startpos == 0) { + QCOMPARE(haystack.indexOf(needle), resultpos); + QCOMPARE(haystackRef.indexOf(needle), resultpos); + QCOMPARE(haystackRef.indexOf(needleRef), resultpos); + QCOMPARE(haystack.indexOf(needleRef), resultpos); + } + } + if (needle.size() == 1) { + QCOMPARE(needle.at(0), needleRef.at(0)); + QCOMPARE(haystack.indexOf(needleRef.at(0), startpos, cs), resultpos); + QCOMPARE(haystackRef.indexOf(needle.at(0), startpos, cs), resultpos); + QCOMPARE(haystackRef.indexOf(needleRef.at(0), startpos, cs), resultpos); + QCOMPARE(haystack.indexOf(needleRef.at(0), startpos ,cs), resultpos); + } +} + +void tst_QStringRef::indexOf2_data() +{ + QTest::addColumn("haystack"); + QTest::addColumn("needle"); + QTest::addColumn("resultpos"); + + QTest::newRow("data0") << QString() << QString() << 0; + QTest::newRow("data1") << QString() << QString("") << 0; + QTest::newRow("data2") << QString("") << QString() << 0; + QTest::newRow("data3") << QString("") << QString("") << 0; + QTest::newRow("data4") << QString() << QString("a") << -1; + QTest::newRow("data5") << QString() << QString("abcdefg") << -1; + QTest::newRow("data6") << QString("") << QString("a") << -1; + QTest::newRow("data7") << QString("") << QString("abcdefg") << -1; + + QTest::newRow("data8") << QString("a") << QString() << 0; + QTest::newRow("data9") << QString("a") << QString("") << 0; + QTest::newRow("data10") << QString("a") << QString("a") << 0; + QTest::newRow("data11") << QString("a") << QString("b") << -1; + QTest::newRow("data12") << QString("a") << QString("abcdefg") << -1; + QTest::newRow("data13") << QString("ab") << QString() << 0; + QTest::newRow("data14") << QString("ab") << QString("") << 0; + QTest::newRow("data15") << QString("ab") << QString("a") << 0; + QTest::newRow("data16") << QString("ab") << QString("b") << 1; + QTest::newRow("data17") << QString("ab") << QString("ab") << 0; + QTest::newRow("data18") << QString("ab") << QString("bc") << -1; + QTest::newRow("data19") << QString("ab") << QString("abcdefg") << -1; + + QTest::newRow("data30") << QString("abc") << QString("a") << 0; + QTest::newRow("data31") << QString("abc") << QString("b") << 1; + QTest::newRow("data32") << QString("abc") << QString("c") << 2; + QTest::newRow("data33") << QString("abc") << QString("d") << -1; + QTest::newRow("data34") << QString("abc") << QString("ab") << 0; + QTest::newRow("data35") << QString("abc") << QString("bc") << 1; + QTest::newRow("data36") << QString("abc") << QString("cd") << -1; + QTest::newRow("data37") << QString("abc") << QString("ac") << -1; + + // sizeof(whale) > 32 + QString whale = "a5zby6cx7dw8evf9ug0th1si2rj3qkp4lomn"; + QString minnow = "zby"; + QTest::newRow("data40") << whale << minnow << 2; + QTest::newRow("data41") << (whale + whale) << minnow << 2; + QTest::newRow("data42") << (minnow + whale) << minnow << 0; + QTest::newRow("data43") << whale << whale << 0; + QTest::newRow("data44") << (whale + whale) << whale << 0; + QTest::newRow("data45") << whale << (whale + whale) << -1; + QTest::newRow("data46") << (whale + whale) << (whale + whale) << 0; + QTest::newRow("data47") << (whale + whale) << (whale + minnow) << -1; + QTest::newRow("data48") << (minnow + whale) << whale << (int)minnow.length(); +} + +void tst_QStringRef::indexOf2() +{ + QFETCH(QString, haystack); + QFETCH(QString, needle); + QFETCH(int, resultpos); + + const QString haystackPadded = QString::fromLatin1(" %1 ").arg(haystack); + const QString needlePadded = QString::fromLatin1(" %1 ").arg(needle); + const QStringRef haystackRef(&haystackPadded, 1, haystack.size()); + const QStringRef needleRef(&needlePadded, 1, needle.size()); + + + int got; + + QCOMPARE(haystack.indexOf(needleRef, 0, Qt::CaseSensitive), resultpos); + QCOMPARE(haystackRef.indexOf(needle, 0, Qt::CaseSensitive), resultpos); + QCOMPARE(haystackRef.indexOf(needleRef, 0, Qt::CaseSensitive), resultpos); + QCOMPARE(haystack.indexOf(needleRef, 0, Qt::CaseInsensitive), resultpos); + QCOMPARE(haystackRef.indexOf(needle, 0, Qt::CaseInsensitive), resultpos); + QCOMPARE(haystackRef.indexOf(needleRef, 0, Qt::CaseInsensitive), resultpos); + if (needle.length() > 0) { + got = haystackRef.lastIndexOf(needle, -1, Qt::CaseSensitive); + QVERIFY(got == resultpos || (resultpos >= 0 && got >= resultpos)); + got = haystackRef.lastIndexOf(needle, -1, Qt::CaseInsensitive); + QVERIFY(got == resultpos || (resultpos >= 0 && got >= resultpos)); + + got = haystack.lastIndexOf(needleRef, -1, Qt::CaseSensitive); + QVERIFY(got == resultpos || (resultpos >= 0 && got >= resultpos)); + got = haystack.lastIndexOf(needleRef, -1, Qt::CaseInsensitive); + QVERIFY(got == resultpos || (resultpos >= 0 && got >= resultpos)); + + got = haystackRef.lastIndexOf(needleRef, -1, Qt::CaseSensitive); + QVERIFY(got == resultpos || (resultpos >= 0 && got >= resultpos)); + got = haystackRef.lastIndexOf(needleRef, -1, Qt::CaseInsensitive); + QVERIFY(got == resultpos || (resultpos >= 0 && got >= resultpos)); + } +} + +void tst_QStringRef::lastIndexOf_data() +{ + QTest::addColumn("haystack"); + QTest::addColumn("needle"); + QTest::addColumn("from"); + QTest::addColumn("expected"); + QTest::addColumn("caseSensitive"); + + QString a = "ABCDEFGHIEfGEFG"; + + QTest::newRow("-1") << a << "G" << a.size() - 1 << 14 << true; + QTest::newRow("1") << a << "G" << - 1 << 14 << true; + QTest::newRow("2") << a << "G" << -3 << 11 << true; + QTest::newRow("3") << a << "G" << -5 << 6 << true; + QTest::newRow("4") << a << "G" << 14 << 14 << true; + QTest::newRow("5") << a << "G" << 13 << 11 << true; + QTest::newRow("6") << a << "B" << a.size() - 1 << 1 << true; + QTest::newRow("6") << a << "B" << - 1 << 1 << true; + QTest::newRow("7") << a << "B" << 1 << 1 << true; + QTest::newRow("8") << a << "B" << 0 << -1 << true; + + QTest::newRow("9") << a << "G" << -1 << a.size()-1 << true; + QTest::newRow("10") << a << "G" << a.size()-1 << a.size()-1 << true; + QTest::newRow("11") << a << "G" << a.size() << -1 << true; + QTest::newRow("12") << a << "A" << 0 << 0 << true; + QTest::newRow("13") << a << "A" << -1*a.size() << 0 << true; + + QTest::newRow("15") << a << "efg" << 0 << -1 << false; + QTest::newRow("16") << a << "efg" << a.size() << -1 << false; + QTest::newRow("17") << a << "efg" << -1 * a.size() << -1 << false; + QTest::newRow("19") << a << "efg" << a.size() - 1 << 12 << false; + QTest::newRow("20") << a << "efg" << 12 << 12 << false; + QTest::newRow("21") << a << "efg" << -12 << -1 << false; + QTest::newRow("22") << a << "efg" << 11 << 9 << false; + + QTest::newRow("24") << "" << "asdf" << -1 << -1 << false; + QTest::newRow("25") << "asd" << "asdf" << -1 << -1 << false; + QTest::newRow("26") << "" << QString() << -1 << -1 << false; + + QTest::newRow("27") << a << "" << a.size() << a.size() << false; + QTest::newRow("28") << a << "" << a.size() + 10 << -1 << false; +} + +void tst_QStringRef::lastIndexOf() +{ + QFETCH(QString, haystack); + QFETCH(QString, needle); + QFETCH(int, from); + QFETCH(int, expected); + QFETCH(bool, caseSensitive); + + const QString haystackPadded = QString::fromLatin1(" %1 ").arg(haystack); + const QString needlePadded = QString::fromLatin1(" %1 ").arg(needle); + const QStringRef haystackRef(&haystackPadded, 1, haystack.size()); + const QStringRef needleRef(&needlePadded, 1, needle.size()); + + Qt::CaseSensitivity cs = (caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); + + QCOMPARE(haystack.lastIndexOf(needleRef, from, cs), expected); + QCOMPARE(haystackRef.lastIndexOf(needle, from, cs), expected); + QCOMPARE(haystackRef.lastIndexOf(needleRef, from, cs), expected); + + + if (cs == Qt::CaseSensitive) { + QCOMPARE(haystack.lastIndexOf(needleRef, from), expected); + QCOMPARE(haystackRef.lastIndexOf(needle, from), expected); + QCOMPARE(haystackRef.lastIndexOf(needleRef, from), expected); + + if (from == -1) { + QCOMPARE(haystack.lastIndexOf(needleRef), expected); + QCOMPARE(haystackRef.lastIndexOf(needle), expected); + QCOMPARE(haystackRef.lastIndexOf(needleRef), expected); + + } + } + if (needle.size() == 1) { + QCOMPARE(haystack.lastIndexOf(needleRef.at(0), from), expected); + QCOMPARE(haystackRef.lastIndexOf(needle.at(0), from), expected); + QCOMPARE(haystackRef.lastIndexOf(needleRef.at(0), from), expected); + } +} + +void tst_QStringRef::count() +{ + const QString a = QString::fromLatin1("ABCDEFGHIEfGEFG"); // 15 chars + CREATE_REF(a); + QCOMPARE(ref.count('A'),1); + QCOMPARE(ref.count('Z'),0); + QCOMPARE(ref.count('E'),3); + QCOMPARE(ref.count('F'),2); + QCOMPARE(ref.count('F',Qt::CaseInsensitive),3); + QCOMPARE(ref.count("FG"),2); + QCOMPARE(ref.count("FG",Qt::CaseInsensitive),3); + QCOMPARE(ref.count(QString(), Qt::CaseInsensitive), 16); + QCOMPARE(ref.count("", Qt::CaseInsensitive), 16); +} + +void tst_QStringRef::contains() +{ + const QString a = QString::fromLatin1("ABCDEFGHIEfGEFG"); // 15 chars + CREATE_REF(a); + QVERIFY(ref.contains('A')); + QVERIFY(!ref.contains('Z')); + QVERIFY(ref.contains('E')); + QVERIFY(ref.contains('F')); + QVERIFY(ref.contains('F',Qt::CaseInsensitive)); + QVERIFY(ref.contains("FG")); + QVERIFY(ref.contains(QString("FG").midRef(0))); + const QString ref2 = QString::fromLatin1(" FG "); + QVERIFY(ref.contains(ref2.midRef(1, 2),Qt::CaseInsensitive)); + QVERIFY(ref.contains(QString(), Qt::CaseInsensitive)); + QVERIFY(ref.contains("", Qt::CaseInsensitive)); // apparently +} + +void tst_QStringRef::startsWith() +{ + { + const QString a = QString::fromLatin1("AB"); + CREATE_REF(a); + QVERIFY(ref.startsWith("A")); + QVERIFY(ref.startsWith("AB")); + QVERIFY(!ref.startsWith("C")); + QVERIFY(!ref.startsWith("ABCDEF")); + QVERIFY(ref.startsWith("")); + QVERIFY(ref.startsWith(QString::null)); + QVERIFY(ref.startsWith('A')); + QVERIFY(ref.startsWith(QLatin1Char('A'))); + QVERIFY(ref.startsWith(QChar('A'))); + QVERIFY(!ref.startsWith('C')); + QVERIFY(!ref.startsWith(QChar())); + QVERIFY(!ref.startsWith(QLatin1Char(0))); + + QVERIFY(ref.startsWith(QLatin1String("A"))); + QVERIFY(ref.startsWith(QLatin1String("AB"))); + QVERIFY(!ref.startsWith(QLatin1String("C"))); + QVERIFY(!ref.startsWith(QLatin1String("ABCDEF"))); + QVERIFY(ref.startsWith(QLatin1String(""))); + QVERIFY(ref.startsWith(QLatin1String(0))); + + QVERIFY(ref.startsWith("A", Qt::CaseSensitive)); + QVERIFY(ref.startsWith("A", Qt::CaseInsensitive)); + QVERIFY(!ref.startsWith("a", Qt::CaseSensitive)); + QVERIFY(ref.startsWith("a", Qt::CaseInsensitive)); + QVERIFY(!ref.startsWith("aB", Qt::CaseSensitive)); + QVERIFY(ref.startsWith("aB", Qt::CaseInsensitive)); + QVERIFY(!ref.startsWith("C", Qt::CaseSensitive)); + QVERIFY(!ref.startsWith("C", Qt::CaseInsensitive)); + QVERIFY(!ref.startsWith("c", Qt::CaseSensitive)); + QVERIFY(!ref.startsWith("c", Qt::CaseInsensitive)); + QVERIFY(!ref.startsWith("abcdef", Qt::CaseInsensitive)); + QVERIFY(ref.startsWith("", Qt::CaseInsensitive)); + QVERIFY(ref.startsWith(QString::null, Qt::CaseInsensitive)); + QVERIFY(ref.startsWith('a', Qt::CaseInsensitive)); + QVERIFY(ref.startsWith('A', Qt::CaseInsensitive)); + QVERIFY(ref.startsWith(QLatin1Char('a'), Qt::CaseInsensitive)); + QVERIFY(ref.startsWith(QChar('a'), Qt::CaseInsensitive)); + QVERIFY(!ref.startsWith('c', Qt::CaseInsensitive)); + QVERIFY(!ref.startsWith(QChar(), Qt::CaseInsensitive)); + QVERIFY(!ref.startsWith(QLatin1Char(0), Qt::CaseInsensitive)); + + QVERIFY(ref.startsWith(QLatin1String("A"), Qt::CaseSensitive)); + QVERIFY(ref.startsWith(QLatin1String("A"), Qt::CaseInsensitive)); + QVERIFY(!ref.startsWith(QLatin1String("a"), Qt::CaseSensitive)); + QVERIFY(ref.startsWith(QLatin1String("a"), Qt::CaseInsensitive)); + QVERIFY(!ref.startsWith(QLatin1String("aB"), Qt::CaseSensitive)); + QVERIFY(ref.startsWith(QLatin1String("aB"), Qt::CaseInsensitive)); + QVERIFY(!ref.startsWith(QLatin1String("C"), Qt::CaseSensitive)); + QVERIFY(!ref.startsWith(QLatin1String("C"), Qt::CaseInsensitive)); + QVERIFY(!ref.startsWith(QLatin1String("c"), Qt::CaseSensitive)); + QVERIFY(!ref.startsWith(QLatin1String("c"), Qt::CaseInsensitive)); + QVERIFY(!ref.startsWith(QLatin1String("abcdef"), Qt::CaseInsensitive)); + QVERIFY(ref.startsWith(QLatin1String(""), Qt::CaseInsensitive)); + QVERIFY(ref.startsWith(QLatin1String(0), Qt::CaseInsensitive)); + QVERIFY(ref.startsWith('A', Qt::CaseSensitive)); + QVERIFY(ref.startsWith(QLatin1Char('A'), Qt::CaseSensitive)); + QVERIFY(ref.startsWith(QChar('A'), Qt::CaseSensitive)); + QVERIFY(!ref.startsWith('a', Qt::CaseSensitive)); + QVERIFY(!ref.startsWith(QChar(), Qt::CaseSensitive)); + QVERIFY(!ref.startsWith(QLatin1Char(0), Qt::CaseSensitive)); + } + { + const QString a = QString::fromLatin1(""); + CREATE_REF(a); + QVERIFY(ref.startsWith("")); + QVERIFY(ref.startsWith(QString::null)); + QVERIFY(!ref.startsWith("ABC")); + + QVERIFY(ref.startsWith(QLatin1String(""))); + QVERIFY(ref.startsWith(QLatin1String(0))); + QVERIFY(!ref.startsWith(QLatin1String("ABC"))); + + QVERIFY(!ref.startsWith(QLatin1Char(0))); + QVERIFY(!ref.startsWith(QLatin1Char('x'))); + QVERIFY(!ref.startsWith(QChar())); + } + { + const QStringRef ref; + QVERIFY(!ref.startsWith("")); + QVERIFY(ref.startsWith(QString::null)); + QVERIFY(!ref.startsWith("ABC")); + + QVERIFY(!ref.startsWith(QLatin1String(""))); + QVERIFY(ref.startsWith(QLatin1String(0))); + QVERIFY(!ref.startsWith(QLatin1String("ABC"))); + + QVERIFY(!ref.startsWith(QLatin1Char(0))); + QVERIFY(!ref.startsWith(QLatin1Char('x'))); + QVERIFY(!ref.startsWith(QChar())); + } +} + +void tst_QStringRef::endsWith() +{ + { + const QString a = QString::fromLatin1("AB"); + CREATE_REF(a); + QVERIFY(ref.endsWith("B")); + QVERIFY(ref.endsWith("AB")); + QVERIFY(!ref.endsWith("C")); + QVERIFY(!ref.endsWith("ABCDEF")); + QVERIFY(ref.endsWith("")); + QVERIFY(ref.endsWith(QString::null)); + QVERIFY(ref.endsWith('B')); + QVERIFY(ref.endsWith(QLatin1Char('B'))); + QVERIFY(ref.endsWith(QChar('B'))); + QVERIFY(!ref.endsWith('C')); + QVERIFY(!ref.endsWith(QChar())); + QVERIFY(!ref.endsWith(QLatin1Char(0))); + + QVERIFY(ref.endsWith(QLatin1String("B"))); + QVERIFY(ref.endsWith(QLatin1String("AB"))); + QVERIFY(!ref.endsWith(QLatin1String("C"))); + QVERIFY(!ref.endsWith(QLatin1String("ABCDEF"))); + QVERIFY(ref.endsWith(QLatin1String(""))); + QVERIFY(ref.endsWith(QLatin1String(0))); + + QVERIFY(ref.endsWith("B", Qt::CaseSensitive)); + QVERIFY(ref.endsWith("B", Qt::CaseInsensitive)); + QVERIFY(!ref.endsWith("b", Qt::CaseSensitive)); + QVERIFY(ref.endsWith("b", Qt::CaseInsensitive)); + QVERIFY(!ref.endsWith("aB", Qt::CaseSensitive)); + QVERIFY(ref.endsWith("aB", Qt::CaseInsensitive)); + QVERIFY(!ref.endsWith("C", Qt::CaseSensitive)); + QVERIFY(!ref.endsWith("C", Qt::CaseInsensitive)); + QVERIFY(!ref.endsWith("c", Qt::CaseSensitive)); + QVERIFY(!ref.endsWith("c", Qt::CaseInsensitive)); + QVERIFY(!ref.endsWith("abcdef", Qt::CaseInsensitive)); + QVERIFY(ref.endsWith("", Qt::CaseInsensitive)); + QVERIFY(ref.endsWith(QString::null, Qt::CaseInsensitive)); + QVERIFY(ref.endsWith('b', Qt::CaseInsensitive)); + QVERIFY(ref.endsWith('B', Qt::CaseInsensitive)); + QVERIFY(ref.endsWith(QLatin1Char('b'), Qt::CaseInsensitive)); + QVERIFY(ref.endsWith(QChar('b'), Qt::CaseInsensitive)); + QVERIFY(!ref.endsWith('c', Qt::CaseInsensitive)); + QVERIFY(!ref.endsWith(QChar(), Qt::CaseInsensitive)); + QVERIFY(!ref.endsWith(QLatin1Char(0), Qt::CaseInsensitive)); + + QVERIFY(ref.endsWith(QLatin1String("B"), Qt::CaseSensitive)); + QVERIFY(ref.endsWith(QLatin1String("B"), Qt::CaseInsensitive)); + QVERIFY(!ref.endsWith(QLatin1String("b"), Qt::CaseSensitive)); + QVERIFY(ref.endsWith(QLatin1String("b"), Qt::CaseInsensitive)); + QVERIFY(!ref.endsWith(QLatin1String("aB"), Qt::CaseSensitive)); + QVERIFY(ref.endsWith(QLatin1String("aB"), Qt::CaseInsensitive)); + QVERIFY(!ref.endsWith(QLatin1String("C"), Qt::CaseSensitive)); + QVERIFY(!ref.endsWith(QLatin1String("C"), Qt::CaseInsensitive)); + QVERIFY(!ref.endsWith(QLatin1String("c"), Qt::CaseSensitive)); + QVERIFY(!ref.endsWith(QLatin1String("c"), Qt::CaseInsensitive)); + QVERIFY(!ref.endsWith(QLatin1String("abcdef"), Qt::CaseInsensitive)); + QVERIFY(ref.endsWith(QLatin1String(""), Qt::CaseInsensitive)); + QVERIFY(ref.endsWith(QLatin1String(0), Qt::CaseInsensitive)); + QVERIFY(ref.endsWith('B', Qt::CaseSensitive)); + QVERIFY(ref.endsWith(QLatin1Char('B'), Qt::CaseSensitive)); + QVERIFY(ref.endsWith(QChar('B'), Qt::CaseSensitive)); + QVERIFY(!ref.endsWith('b', Qt::CaseSensitive)); + QVERIFY(!ref.endsWith(QChar(), Qt::CaseSensitive)); + QVERIFY(!ref.endsWith(QLatin1Char(0), Qt::CaseSensitive)); + + } + { + const QString a = QString::fromLatin1(""); + CREATE_REF(a); + QVERIFY(ref.endsWith("")); + QVERIFY(ref.endsWith(QString::null)); + QVERIFY(!ref.endsWith("ABC")); + QVERIFY(!ref.endsWith(QLatin1Char(0))); + QVERIFY(!ref.endsWith(QLatin1Char('x'))); + QVERIFY(!ref.endsWith(QChar())); + + QVERIFY(ref.endsWith(QLatin1String(""))); + QVERIFY(ref.endsWith(QLatin1String(0))); + QVERIFY(!ref.endsWith(QLatin1String("ABC"))); + } + + { + QStringRef ref; + QVERIFY(!ref.endsWith("")); + QVERIFY(ref.endsWith(QString::null)); + QVERIFY(!ref.endsWith("ABC")); + + QVERIFY(!ref.endsWith(QLatin1String(""))); + QVERIFY(ref.endsWith(QLatin1String(0))); + QVERIFY(!ref.endsWith(QLatin1String("ABC"))); + + QVERIFY(!ref.endsWith(QLatin1Char(0))); + QVERIFY(!ref.endsWith(QLatin1Char('x'))); + QVERIFY(!ref.endsWith(QChar())); + } +} + +void tst_QStringRef::operator_eqeq_nullstring() +{ + /* Some of these might not be all that logical but it's the behaviour we've had since 3.0.0 + so we should probably stick with it. */ + + QVERIFY(QStringRef() == ""); + QVERIFY("" == QStringRef()); + + QVERIFY(QString("") == ""); + QVERIFY("" == QString("")); + + QVERIFY(QStringRef().size() == 0); + + QVERIFY(QString("").size() == 0); + + QVERIFY(QStringRef() == QString("")); + QVERIFY(QString("") == QString()); +} + +void tst_QStringRef::localeAwareCompare_data() +{ +#ifdef Q_OS_WIN + QTest::addColumn("locale"); +#else + QTest::addColumn("locale"); +#endif + QTest::addColumn("s1"); + QTest::addColumn("s2"); + QTest::addColumn("result"); + + /* + The C locale performs pure byte comparisons for + Latin-1-specific characters(I think). Compare with Swedish + below. + */ +#ifdef Q_OS_WIN // assume c locale to be english + QTest::newRow("c1") << MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) << QString("\xe5") << QString("\xe4") << 1; + QTest::newRow("c2") << MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) << QString("\xe4") << QString("\xf6") << -1; + QTest::newRow("c3") << MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) << QString("\xe5") << QString("\xf6") << -1; +#else + QTest::newRow("c1") << QString("C") << QString("\xe5") << QString("\xe4") << 1; + QTest::newRow("c2") << QString("C") << QString("\xe4") << QString("\xf6") << -1; + QTest::newRow("c3") << QString("C") << QString("\xe5") << QString("\xf6") << -1; +#endif + + /* + It's hard to test English, because it's treated differently + on different platforms. For example, on Linux, it uses the + iso14651_t1 template file, which happens to provide good + defaults for Swedish. Mac OS X seems to do a pure bytewise + comparison of Latin-1 values, although I'm not sure. So I + just test digits to make sure that it's not totally broken. + */ +#ifdef Q_OS_WIN + QTest::newRow("english1") << MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) << QString("5") << QString("4") << 1; + QTest::newRow("english2") << MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) << QString("4") << QString("6") << -1; + QTest::newRow("english3") << MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) << QString("5") << QString("6") << -1; +#else + QTest::newRow("english1") << QString("en_US") << QString("5") << QString("4") << 1; + QTest::newRow("english2") << QString("en_US") << QString("4") << QString("6") << -1; + QTest::newRow("english3") << QString("en_US") << QString("5") << QString("6") << -1; +#endif + /* + In Swedish, a with ring above(E5) comes before a with + diaresis(E4), which comes before o diaresis(F6), which + all come after z. + */ +#ifdef Q_OS_MAC + QTest::newRow("swedish1") << QString("sv_SE.ISO8859-1") << QString("\xe5") << QString("\xe4") << -1; + QTest::newRow("swedish2") << QString("sv_SE.ISO8859-1") << QString("\xe4") << QString("\xf6") << -1; + QTest::newRow("swedish3") << QString("sv_SE.ISO8859-1") << QString("\xe5") << QString("\xf6") << -1; + QTest::newRow("swedish4") << QString("sv_SE.ISO8859-1") << QString("z") << QString("\xe5") << -1; +#elif defined(Q_OS_WIN) + QTest::newRow("swedish1") << MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) << QString("\xe5") << QString("\xe4") << -1; + QTest::newRow("swedish2") << MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) << QString("\xe4") << QString("\xf6") << -1; + QTest::newRow("swedish3") << MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) << QString("\xe5") << QString("\xf6") << -1; + QTest::newRow("swedish4") << MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) << QString("z") << QString("\xe5") << -1; +#else + QTest::newRow("swedish1") << QString("sv_SE") << QString("\xe5") << QString("\xe4") << -1; + QTest::newRow("swedish2") << QString("sv_SE") << QString("\xe4") << QString("\xf6") << -1; + QTest::newRow("swedish3") << QString("sv_SE") << QString("\xe5") << QString("\xf6") << -1; + QTest::newRow("swedish4") << QString("sv_SE") << QString("z") << QString("\xe5") << -1; +#endif + +#if 0 + /* + In Norwegian, ae(E6) comes before o with stroke(D8), which + comes before a with ring above(E5). + */ + QTest::newRow("norwegian1") << QString("no_NO") << QString("\xe6") << QString("\xd8") << -1; + QTest::newRow("norwegian2") << QString("no_NO") << QString("\xd8") << QString("\xe5") << -1; + QTest::newRow("norwegian3") << QString("no_NO") << QString("\xe6") << QString("\xe5") << -1; +#endif + + /* + In German, z comes *after* a with diaresis(E4), + which comes before o diaresis(F6). + */ +#ifdef Q_OS_MAC + QTest::newRow("german1") << QString("de_DE.ISO8859-1") << QString("z") << QString("\xe4") << 1; + QTest::newRow("german2") << QString("de_DE.ISO8859-1") << QString("\xe4") << QString("\xf6") << -1; + QTest::newRow("german3") << QString("de_DE.ISO8859-1") << QString("z") << QString("\xf6") << 1; +#elif defined(Q_OS_WIN) + QTest::newRow("german1") << MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) << QString("z") << QString("\xe4") << 1; + QTest::newRow("german2") << MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) << QString("\xe4") << QString("\xf6") << -1; + QTest::newRow("german3") << MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) << QString("z") << QString("\xf6") << 1; +#else + QTest::newRow("german1") << QString("de_DE") << QString("z") << QString("\xe4") << 1; + QTest::newRow("german2") << QString("de_DE") << QString("\xe4") << QString("\xf6") << -1; + QTest::newRow("german3") << QString("de_DE") << QString("z") << QString("\xf6") << 1; +#endif +} + +void tst_QStringRef::localeAwareCompare() +{ +#ifdef Q_OS_WIN +# ifndef Q_OS_WINCE + QSKIP("On others than Win CE, we cannot set the system or user locale.", SkipAll); +# endif + QFETCH(ulong, locale); +#else + QFETCH(QString, locale); +#endif + QFETCH(QString, s1); + QFETCH(QString, s2); + + const QString padded1 = QString::fromLatin1(" %1 ").arg(s1); + const QString padded2 = QString::fromLatin1(" %2 ").arg(s2); + const QStringRef ref1 = padded1.midRef(1, s1.size()); + const QStringRef ref2 = padded2.midRef(1, s2.size()); + + QFETCH(int, result); + + +#ifdef Q_OS_WIN +# if defined(Q_OS_WINCE) + DWORD oldLcid = GetUserDefaultLCID(); + SetUserDefaultLCID(locale); + + QCOMPARE(locale, GetUserDefaultLCID()); +# else + DWORD oldLcid = GetThreadLocale(); + SetThreadLocale(locale); + + QCOMPARE(locale, GetThreadLocale()); +# endif + +#elif defined(Q_WS_MAC) + QSKIP("Setting the locale is not supported on OS X(you can set the C locale, but that won't affect CFStringCompare which is used to compare strings)", SkipAll); +#else + if (!locale.isEmpty()) { + const char *newLocale = setlocale(LC_ALL, locale.toLatin1()); + if (!newLocale) { + setlocale(LC_ALL, ""); + QSKIP("Please install the proper locale on this machine to test properly", SkipSingle); + return; + } + } +#endif + + int testres = QStringRef::localeAwareCompare(ref1, ref2); + if (result < 0) { + QVERIFY(testres < 0); + } else if (result > 0) { + QVERIFY(testres > 0); + } else { + QVERIFY(testres == 0); + } + + + testres = QStringRef::localeAwareCompare(ref2, ref1); + if (result > 0) { + QVERIFY(testres < 0); + } else if (result < 0) { + QVERIFY(testres > 0); + } else { + QVERIFY(testres == 0); + } + +#ifdef Q_OS_WIN +# if defined(Q_OS_WINCE) + SetUserDefaultLCID(oldLcid); +# else + SetThreadLocale(oldLcid); +# endif + +#else + if (!locale.isEmpty()) + setlocale(LC_ALL, ""); +#endif +} + + +static inline int sign(int x) +{ + return x == 0 ? 0 : (x < 0 ? -1 : 1); +} + +void tst_QStringRef::compare_data() +{ + QTest::addColumn("s1"); + QTest::addColumn("s2"); + QTest::addColumn("csr"); // case sensitive result + QTest::addColumn("cir"); // case insensitive result + + + // null strings + QTest::newRow("data0") << QString("") << QString("") << 0 << 0; + QTest::newRow("data1") << QString("a") << QString("") << 1 << 1; + QTest::newRow("data2") << QString("") << QString("a") << -1 << -1; + + // equal length + QTest::newRow("data3") << QString("abc") << QString("abc") << 0 << 0; + QTest::newRow("data4") << QString("abC") << QString("abc") << -1 << 0; + QTest::newRow("data5") << QString("abc") << QString("abC") << 1 << 0; + + // different length + QTest::newRow("data6") << QString("abcdef") << QString("abc") << 1 << 1; + QTest::newRow("data6") << QString("abCdef") << QString("abc") << -1 << 1; + QTest::newRow("data7") << QString("abc") << QString("abcdef") << -1 << -1; + + QString upper; + upper += QChar(QChar::highSurrogate(0x10400)); + upper += QChar(QChar::lowSurrogate(0x10400)); + QString lower; + lower += QChar(QChar::highSurrogate(0x10428)); + lower += QChar(QChar::lowSurrogate(0x10428)); + QTest::newRow("data8") << upper << lower << -1 << 0; + + // embedded nulls + // These dont work as of now. Its OK that these dont work since \0 is not a valid unicode + /*QTest::newRow("data9") << QString(QByteArray("\0", 1)) << QString(QByteArray("\0", 1)) << 0 << 0; + QTest::newRow("data10") << QString(QByteArray("\0", 1)) << QString("") << 1 << 1; + QTest::newRow("data11") << QString("") << QString(QByteArray("\0", 1)) << -1 << -1; + QTest::newRow("data12") << QString("ab\0c") << QString(QByteArray("ab\0c", 4)) << 0 << 0; + QTest::newRow("data13") << QString(QByteArray("ab\0c", 4)) << QString("abc") << -1 << -1; + QTest::newRow("data14") << QString("abc") << QString(QByteArray("ab\0c", 4)) << 1 << 1;*/ +} + +static bool isLatin(const QString &s) +{ + for (int i = 0; i < s.length(); ++i) + if (s.at(i).unicode() > 0xff) + return false; + return true; +} + +void tst_QStringRef::compare() +{ + QFETCH(QString, s1); + QFETCH(QString, s2); + QFETCH(int, csr); + QFETCH(int, cir); + + QStringRef r1(&s1, 0, s1.length()); + QStringRef r2(&s2, 0, s2.length()); + + QCOMPARE(sign(QString::compare(s1, s2)), csr); + QCOMPARE(sign(QStringRef::compare(r1, r2)), csr); + QCOMPARE(sign(s1.compare(s2)), csr); + QCOMPARE(sign(s1.compare(r2)), csr); + QCOMPARE(sign(r1.compare(r2)), csr); + + QCOMPARE(sign(s1.compare(s2, Qt::CaseSensitive)), csr); + QCOMPARE(sign(s1.compare(s2, Qt::CaseInsensitive)), cir); + QCOMPARE(sign(s1.compare(r2, Qt::CaseSensitive)), csr); + QCOMPARE(sign(s1.compare(r2, Qt::CaseInsensitive)), cir); + QCOMPARE(sign(r1.compare(r2, Qt::CaseSensitive)), csr); + QCOMPARE(sign(r1.compare(r2, Qt::CaseInsensitive)), cir); + + QCOMPARE(sign(QString::compare(s1, s2, Qt::CaseSensitive)), csr); + QCOMPARE(sign(QString::compare(s1, s2, Qt::CaseInsensitive)), cir); + QCOMPARE(sign(QString::compare(s1, r2, Qt::CaseSensitive)), csr); + QCOMPARE(sign(QString::compare(s1, r2, Qt::CaseInsensitive)), cir); + QCOMPARE(sign(QStringRef::compare(r1, r2, Qt::CaseSensitive)), csr); + QCOMPARE(sign(QStringRef::compare(r1, r2, Qt::CaseInsensitive)), cir); + + if (!cir) { + QCOMPARE(s1.toCaseFolded(), s2.toCaseFolded()); + } + + if (isLatin(s2)) { + QCOMPARE(sign(QString::compare(s1, QLatin1String(s2.toLatin1()))), csr); + QCOMPARE(sign(QString::compare(s1, QLatin1String(s2.toLatin1()), Qt::CaseInsensitive)), cir); + QCOMPARE(sign(QStringRef::compare(r1, QLatin1String(s2.toLatin1()))), csr); + QCOMPARE(sign(QStringRef::compare(r1, QLatin1String(s2.toLatin1()), Qt::CaseInsensitive)), cir); + } + + if (isLatin(s1)) { + QCOMPARE(sign(QString::compare(QLatin1String(s1.toLatin1()), s2)), csr); + QCOMPARE(sign(QString::compare(QLatin1String(s1.toLatin1()), s2, Qt::CaseInsensitive)), cir); + } +} + +QTEST_APPLESS_MAIN(tst_QStringRef) + +#include "tst_qstringref.moc" -- cgit v0.12 From 453b2578b5bdb67553dec1c4e8f480c94f1153cd Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 1 Jun 2010 10:10:32 +0200 Subject: tst_qstringref.cpp: Fix licence header and compilation on windows Somehow the emacs macro for style did a too good job --- tests/auto/qstringref/tst_qstringref.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/auto/qstringref/tst_qstringref.cpp b/tests/auto/qstringref/tst_qstringref.cpp index 1980901..2d25efd 100644 --- a/tests/auto/qstringref/tst_qstringref.cpp +++ b/tests/auto/qstringref/tst_qstringref.cpp @@ -1,8 +1,8 @@ /**************************************************************************** ** -** Copyright(C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. -** Contact: Nokia Corporation(qt-info@nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the test suite of the Qt Toolkit. ** @@ -39,7 +39,6 @@ ** ****************************************************************************/ - #include #include #include @@ -167,7 +166,7 @@ static inline double nan() return *reinterpret_cast(be_nan_bytes); return *reinterpret_cast(le_nan_bytes); } -# define NAN(::nan()) +# define NAN (::nan()) #endif tst_QStringRef::tst_QStringRef() -- cgit v0.12 From bf6de2f79991bb66161960343f18fc1cae0aca41 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Wed, 2 Jun 2010 10:04:56 +0200 Subject: Itemviews: use localeAwareCompare for comparing in the widget classes Task-number: QTBUG-10839 Reviewed-by: Thierry --- src/corelib/kernel/qabstractitemmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/kernel/qabstractitemmodel.cpp b/src/corelib/kernel/qabstractitemmodel.cpp index 3660a3c..83aed8c 100644 --- a/src/corelib/kernel/qabstractitemmodel.cpp +++ b/src/corelib/kernel/qabstractitemmodel.cpp @@ -532,7 +532,7 @@ bool QAbstractItemModelPrivate::variantLessThan(const QVariant &v1, const QVaria case 1: //floating point return v1.toReal() < v2.toReal(); default: - return v1.toString() < v2.toString(); + return v1.toString().localeAwareCompare(v2.toString()) < 0; } } -- cgit v0.12 From d20d5611aec3eb2c4be1808250f8eed47e0ebf19 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Wed, 2 Jun 2010 14:43:53 +0200 Subject: Remove tst_QStringRef::localeAwareCompare This new test breaks on embedded. --- tests/auto/qstringref/tst_qstringref.cpp | 176 ------------------------------- 1 file changed, 176 deletions(-) diff --git a/tests/auto/qstringref/tst_qstringref.cpp b/tests/auto/qstringref/tst_qstringref.cpp index 2d25efd..585e14e 100644 --- a/tests/auto/qstringref/tst_qstringref.cpp +++ b/tests/auto/qstringref/tst_qstringref.cpp @@ -81,8 +81,6 @@ private slots: void compare_data(); void compare(); void operator_eqeq_nullstring(); - void localeAwareCompare_data(); - void localeAwareCompare(); }; static QStringRef emptyRef() @@ -777,180 +775,6 @@ void tst_QStringRef::operator_eqeq_nullstring() QVERIFY(QString("") == QString()); } -void tst_QStringRef::localeAwareCompare_data() -{ -#ifdef Q_OS_WIN - QTest::addColumn("locale"); -#else - QTest::addColumn("locale"); -#endif - QTest::addColumn("s1"); - QTest::addColumn("s2"); - QTest::addColumn("result"); - - /* - The C locale performs pure byte comparisons for - Latin-1-specific characters(I think). Compare with Swedish - below. - */ -#ifdef Q_OS_WIN // assume c locale to be english - QTest::newRow("c1") << MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) << QString("\xe5") << QString("\xe4") << 1; - QTest::newRow("c2") << MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) << QString("\xe4") << QString("\xf6") << -1; - QTest::newRow("c3") << MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) << QString("\xe5") << QString("\xf6") << -1; -#else - QTest::newRow("c1") << QString("C") << QString("\xe5") << QString("\xe4") << 1; - QTest::newRow("c2") << QString("C") << QString("\xe4") << QString("\xf6") << -1; - QTest::newRow("c3") << QString("C") << QString("\xe5") << QString("\xf6") << -1; -#endif - - /* - It's hard to test English, because it's treated differently - on different platforms. For example, on Linux, it uses the - iso14651_t1 template file, which happens to provide good - defaults for Swedish. Mac OS X seems to do a pure bytewise - comparison of Latin-1 values, although I'm not sure. So I - just test digits to make sure that it's not totally broken. - */ -#ifdef Q_OS_WIN - QTest::newRow("english1") << MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) << QString("5") << QString("4") << 1; - QTest::newRow("english2") << MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) << QString("4") << QString("6") << -1; - QTest::newRow("english3") << MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) << QString("5") << QString("6") << -1; -#else - QTest::newRow("english1") << QString("en_US") << QString("5") << QString("4") << 1; - QTest::newRow("english2") << QString("en_US") << QString("4") << QString("6") << -1; - QTest::newRow("english3") << QString("en_US") << QString("5") << QString("6") << -1; -#endif - /* - In Swedish, a with ring above(E5) comes before a with - diaresis(E4), which comes before o diaresis(F6), which - all come after z. - */ -#ifdef Q_OS_MAC - QTest::newRow("swedish1") << QString("sv_SE.ISO8859-1") << QString("\xe5") << QString("\xe4") << -1; - QTest::newRow("swedish2") << QString("sv_SE.ISO8859-1") << QString("\xe4") << QString("\xf6") << -1; - QTest::newRow("swedish3") << QString("sv_SE.ISO8859-1") << QString("\xe5") << QString("\xf6") << -1; - QTest::newRow("swedish4") << QString("sv_SE.ISO8859-1") << QString("z") << QString("\xe5") << -1; -#elif defined(Q_OS_WIN) - QTest::newRow("swedish1") << MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) << QString("\xe5") << QString("\xe4") << -1; - QTest::newRow("swedish2") << MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) << QString("\xe4") << QString("\xf6") << -1; - QTest::newRow("swedish3") << MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) << QString("\xe5") << QString("\xf6") << -1; - QTest::newRow("swedish4") << MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) << QString("z") << QString("\xe5") << -1; -#else - QTest::newRow("swedish1") << QString("sv_SE") << QString("\xe5") << QString("\xe4") << -1; - QTest::newRow("swedish2") << QString("sv_SE") << QString("\xe4") << QString("\xf6") << -1; - QTest::newRow("swedish3") << QString("sv_SE") << QString("\xe5") << QString("\xf6") << -1; - QTest::newRow("swedish4") << QString("sv_SE") << QString("z") << QString("\xe5") << -1; -#endif - -#if 0 - /* - In Norwegian, ae(E6) comes before o with stroke(D8), which - comes before a with ring above(E5). - */ - QTest::newRow("norwegian1") << QString("no_NO") << QString("\xe6") << QString("\xd8") << -1; - QTest::newRow("norwegian2") << QString("no_NO") << QString("\xd8") << QString("\xe5") << -1; - QTest::newRow("norwegian3") << QString("no_NO") << QString("\xe6") << QString("\xe5") << -1; -#endif - - /* - In German, z comes *after* a with diaresis(E4), - which comes before o diaresis(F6). - */ -#ifdef Q_OS_MAC - QTest::newRow("german1") << QString("de_DE.ISO8859-1") << QString("z") << QString("\xe4") << 1; - QTest::newRow("german2") << QString("de_DE.ISO8859-1") << QString("\xe4") << QString("\xf6") << -1; - QTest::newRow("german3") << QString("de_DE.ISO8859-1") << QString("z") << QString("\xf6") << 1; -#elif defined(Q_OS_WIN) - QTest::newRow("german1") << MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) << QString("z") << QString("\xe4") << 1; - QTest::newRow("german2") << MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) << QString("\xe4") << QString("\xf6") << -1; - QTest::newRow("german3") << MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) << QString("z") << QString("\xf6") << 1; -#else - QTest::newRow("german1") << QString("de_DE") << QString("z") << QString("\xe4") << 1; - QTest::newRow("german2") << QString("de_DE") << QString("\xe4") << QString("\xf6") << -1; - QTest::newRow("german3") << QString("de_DE") << QString("z") << QString("\xf6") << 1; -#endif -} - -void tst_QStringRef::localeAwareCompare() -{ -#ifdef Q_OS_WIN -# ifndef Q_OS_WINCE - QSKIP("On others than Win CE, we cannot set the system or user locale.", SkipAll); -# endif - QFETCH(ulong, locale); -#else - QFETCH(QString, locale); -#endif - QFETCH(QString, s1); - QFETCH(QString, s2); - - const QString padded1 = QString::fromLatin1(" %1 ").arg(s1); - const QString padded2 = QString::fromLatin1(" %2 ").arg(s2); - const QStringRef ref1 = padded1.midRef(1, s1.size()); - const QStringRef ref2 = padded2.midRef(1, s2.size()); - - QFETCH(int, result); - - -#ifdef Q_OS_WIN -# if defined(Q_OS_WINCE) - DWORD oldLcid = GetUserDefaultLCID(); - SetUserDefaultLCID(locale); - - QCOMPARE(locale, GetUserDefaultLCID()); -# else - DWORD oldLcid = GetThreadLocale(); - SetThreadLocale(locale); - - QCOMPARE(locale, GetThreadLocale()); -# endif - -#elif defined(Q_WS_MAC) - QSKIP("Setting the locale is not supported on OS X(you can set the C locale, but that won't affect CFStringCompare which is used to compare strings)", SkipAll); -#else - if (!locale.isEmpty()) { - const char *newLocale = setlocale(LC_ALL, locale.toLatin1()); - if (!newLocale) { - setlocale(LC_ALL, ""); - QSKIP("Please install the proper locale on this machine to test properly", SkipSingle); - return; - } - } -#endif - - int testres = QStringRef::localeAwareCompare(ref1, ref2); - if (result < 0) { - QVERIFY(testres < 0); - } else if (result > 0) { - QVERIFY(testres > 0); - } else { - QVERIFY(testres == 0); - } - - - testres = QStringRef::localeAwareCompare(ref2, ref1); - if (result > 0) { - QVERIFY(testres < 0); - } else if (result < 0) { - QVERIFY(testres > 0); - } else { - QVERIFY(testres == 0); - } - -#ifdef Q_OS_WIN -# if defined(Q_OS_WINCE) - SetUserDefaultLCID(oldLcid); -# else - SetThreadLocale(oldLcid); -# endif - -#else - if (!locale.isEmpty()) - setlocale(LC_ALL, ""); -#endif -} - - static inline int sign(int x) { return x == 0 ? 0 : (x < 0 ? -1 : 1); -- cgit v0.12 From ad3d90572e2a5ed9ab63553ee3e5be3db5c957a3 Mon Sep 17 00:00:00 2001 From: Ian Walters Date: Wed, 28 Apr 2010 10:59:54 +1000 Subject: Fix LeftToRight + setSpacing + setWrapping when drag-and-dropping Quite a number of assumptions in qlistview assume either wrapping or spacing, but not both. This fixes the Flow left to right case. The TopToBottom case is not affected. TopToBottom + wrapping still works TopToBottom + spacing still works, TopToBottom + spacing + wrapping is still as broken as ever. Drag move prior to this patch was correct, but also need the drop to use the spacing when determining what index to apply to, not just the drag move. Reviewed-by: gabi Merge-Request: 586 Task-number: QTBUG-8938 --- src/gui/itemviews/qabstractitemview_p.h | 2 +- src/gui/itemviews/qlistview.cpp | 95 ++++++++++++++++++++++++++++++--- src/gui/itemviews/qlistview_p.h | 2 + 3 files changed, 90 insertions(+), 9 deletions(-) diff --git a/src/gui/itemviews/qabstractitemview_p.h b/src/gui/itemviews/qabstractitemview_p.h index fce74f3..b9408a5 100644 --- a/src/gui/itemviews/qabstractitemview_p.h +++ b/src/gui/itemviews/qabstractitemview_p.h @@ -136,7 +136,7 @@ public: void stopAutoScroll() { autoScrollTimer.stop(); autoScrollCount = 0;} - bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index); + virtual bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index); bool droppingOnItself(QDropEvent *event, const QModelIndex &index); QWidget *editor(const QModelIndex &index, const QStyleOptionViewItem &options); diff --git a/src/gui/itemviews/qlistview.cpp b/src/gui/itemviews/qlistview.cpp index 1869093..846d911 100644 --- a/src/gui/itemviews/qlistview.cpp +++ b/src/gui/itemviews/qlistview.cpp @@ -1833,6 +1833,14 @@ QAbstractItemView::DropIndicatorPosition QListViewPrivate::position(const QPoint else return QAbstractItemViewPrivate::position(pos, rect, idx); } + +bool QListViewPrivate::dropOn(QDropEvent *event, int *dropRow, int *dropCol, QModelIndex *dropIndex) +{ + if (viewMode == QListView::ListMode && flow == QListView::LeftToRight) + return static_cast(commonListView)->dropOn(event, dropRow, dropCol, dropIndex); + else + return QAbstractItemViewPrivate::dropOn(event, dropRow, dropCol, dropIndex); +} #endif /* @@ -1960,7 +1968,13 @@ void QListModeViewBase::dragMoveEvent(QDragMoveEvent *event) // ignore by default event->ignore(); - QModelIndex index = qq->indexAt(event->pos()); + // can't use indexAt, doesn't account for spacing. + QPoint p = event->pos(); + QRect rect(p.x() + horizontalOffset(), p.y() + verticalOffset(), 1, 1); + rect.adjust(-dd->spacing(), -dd->spacing(), dd->spacing(), dd->spacing()); + const QVector intersectVector = dd->intersectingSet(rect); + QModelIndex index = intersectVector.count() > 0 + ? intersectVector.last() : QModelIndex(); dd->hover = index; if (!dd->droppingOnItself(event, index) && dd->canDecode(event)) { @@ -1968,10 +1982,11 @@ void QListModeViewBase::dragMoveEvent(QDragMoveEvent *event) if (index.isValid() && dd->showDropIndicator) { QRect rect = qq->visualRect(index); dd->dropIndicatorPosition = position(event->pos(), rect, index); + // if spacing, should try to draw between items, not just next to item. switch (dd->dropIndicatorPosition) { case QAbstractItemView::AboveItem: if (dd->isIndexDropEnabled(index.parent())) { - dd->dropIndicatorRect = QRect(rect.left(), rect.top(), 0, rect.height()); + dd->dropIndicatorRect = QRect(rect.left()-dd->spacing(), rect.top(), 0, rect.height()); event->accept(); } else { dd->dropIndicatorRect = QRect(); @@ -1979,7 +1994,7 @@ void QListModeViewBase::dragMoveEvent(QDragMoveEvent *event) break; case QAbstractItemView::BelowItem: if (dd->isIndexDropEnabled(index.parent())) { - dd->dropIndicatorRect = QRect(rect.right(), rect.top(), 0, rect.height()); + dd->dropIndicatorRect = QRect(rect.right()+dd->spacing(), rect.top(), 0, rect.height()); event->accept(); } else { dd->dropIndicatorRect = QRect(); @@ -2014,6 +2029,68 @@ void QListModeViewBase::dragMoveEvent(QDragMoveEvent *event) qq->startAutoScroll(); } +/*! + If the event hasn't already been accepted, determines the index to drop on. + + if (row == -1 && col == -1) + // append to this drop index + else + // place at row, col in drop index + + If it returns true a drop can be done, and dropRow, dropCol and dropIndex reflects the position of the drop. + \internal + */ +bool QListModeViewBase::dropOn(QDropEvent *event, int *dropRow, int *dropCol, QModelIndex *dropIndex) +{ + if (event->isAccepted()) + return false; + + QModelIndex index; + if (dd->viewport->rect().contains(event->pos())) { + // can't use indexAt, doesn't account for spacing. + QPoint p = event->pos(); + QRect rect(p.x() + horizontalOffset(), p.y() + verticalOffset(), 1, 1); + rect.adjust(-dd->spacing(), -dd->spacing(), dd->spacing(), dd->spacing()); + const QVector intersectVector = dd->intersectingSet(rect); + index = intersectVector.count() > 0 + ? intersectVector.last() : QModelIndex(); + if (!index.isValid()) + index = dd->root; + } + + // If we are allowed to do the drop + if (dd->model->supportedDropActions() & event->dropAction()) { + int row = -1; + int col = -1; + if (index != dd->root) { + dd->dropIndicatorPosition = position(event->pos(), qq->visualRect(index), index); + switch (dd->dropIndicatorPosition) { + case QAbstractItemView::AboveItem: + row = index.row(); + col = index.column(); + index = index.parent(); + break; + case QAbstractItemView::BelowItem: + row = index.row() + 1; + col = index.column(); + index = index.parent(); + break; + case QAbstractItemView::OnItem: + case QAbstractItemView::OnViewport: + break; + } + } else { + dd->dropIndicatorPosition = QAbstractItemView::OnViewport; + } + *dropIndex = index; + *dropRow = row; + *dropCol = col; + if (!dd->droppingOnItself(event, index)) + return true; + } + return false; +} + #endif //QT_NO_DRAGANDDROP void QListModeViewBase::updateVerticalScrollBar(const QSize &step) @@ -2107,7 +2184,7 @@ int QListModeViewBase::verticalOffset() const int value = verticalScrollBar()->value(); if (value >= segmentPositions.count()) return 0; - return segmentPositions.at(value); + return segmentPositions.at(value) - spacing(); } } else if (flow() == QListView::TopToBottom && !flowPositions.isEmpty()) { int value = verticalScrollBar()->value(); @@ -2155,14 +2232,14 @@ void QListModeViewBase::scrollContentsBy(int dx, int dy, bool scrollElasticBand) if (horizontal && flow() == QListView::TopToBottom && dx != 0) { int currentValue = qBound(0, horizontalValue, max); int previousValue = qBound(0, currentValue + dx, max); - int currentCoordinate = segmentPositions.at(currentValue); - int previousCoordinate = segmentPositions.at(previousValue); + int currentCoordinate = segmentPositions.at(currentValue) - spacing(); + int previousCoordinate = segmentPositions.at(previousValue) - spacing(); dx = previousCoordinate - currentCoordinate; } else if (vertical && flow() == QListView::LeftToRight && dy != 0) { int currentValue = qBound(0, verticalValue, max); int previousValue = qBound(0, currentValue + dy, max); - int currentCoordinate = segmentPositions.at(currentValue); - int previousCoordinate = segmentPositions.at(previousValue); + int currentCoordinate = segmentPositions.at(currentValue) - spacing(); + int previousCoordinate = segmentPositions.at(previousValue) - spacing(); dy = previousCoordinate - currentCoordinate; } } else { @@ -2330,6 +2407,8 @@ void QListModeViewBase::doStaticLayout(const QListViewLayoutInfo &info) segmentExtents.append(flowPosition); flowPosition = info.spacing + segStartPosition; segPosition += deltaSegPosition; + if (info.wrap) + segPosition += info.spacing; segmentPositions.append(segPosition); segmentStartRows.append(row); deltaSegPosition = 0; diff --git a/src/gui/itemviews/qlistview_p.h b/src/gui/itemviews/qlistview_p.h index bf7657b..9ae1f50 100644 --- a/src/gui/itemviews/qlistview_p.h +++ b/src/gui/itemviews/qlistview_p.h @@ -237,6 +237,7 @@ public: // WARNING: Plenty of duplicated code from QAbstractItemView{,Private}. QAbstractItemView::DropIndicatorPosition position(const QPoint &pos, const QRect &rect, const QModelIndex &idx) const; void dragMoveEvent(QDragMoveEvent *e); + bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index); #endif private: @@ -364,6 +365,7 @@ public: #ifndef QT_NO_DRAGANDDROP virtual QAbstractItemView::DropIndicatorPosition position(const QPoint &pos, const QRect &rect, const QModelIndex &idx) const; + bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index); #endif inline void setGridSize(const QSize &size) { grid = size; } -- cgit v0.12