Long live Qt::SplitBehavior!
The is a copy of the QString::SplitBehavior enum, but scoped in the Qt namespace instead of inside QString, where it creates problems using it elsewhere (QStringView, in particular). Overload all QString{,Ref} functions taking QString::SplitBehavior with Qt::SplitBehavior. Make Qt::SplitBehavior a QFlags for easier future extensions (e.g. a hint to use Boyer-Moore searching). Added tests in QStringApiSymmetry. [ChangeLog][QtCore] Added new Qt::SplitBehavior. [ChangeLog][QtCore][QString/QStringRef] The split functions now optionally take Qt::SplitBehavior. Change-Id: I43a1f8d6b22f09af3709a0b4fb46fca61f9d1d1f Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
cea46aa362
commit
26a0db4b44
@ -194,6 +194,13 @@ public:
|
||||
DescendingOrder
|
||||
};
|
||||
|
||||
enum SplitBehaviorFlags {
|
||||
KeepEmptyParts = 0,
|
||||
SkipEmptyParts = 0x1,
|
||||
};
|
||||
Q_DECLARE_FLAGS(SplitBehavior, SplitBehaviorFlags)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(SplitBehavior)
|
||||
|
||||
enum TileRule {
|
||||
StretchTile,
|
||||
RepeatTile,
|
||||
@ -1772,6 +1779,7 @@ public:
|
||||
QT_Q_FLAG(Alignment)
|
||||
QT_Q_ENUM(TextFlag)
|
||||
QT_Q_FLAG(Orientations)
|
||||
QT_Q_FLAG(SplitBehavior)
|
||||
QT_Q_FLAG(DropActions)
|
||||
QT_Q_FLAG(Edges)
|
||||
QT_Q_FLAG(DockWidgetAreas)
|
||||
|
@ -2361,6 +2361,19 @@
|
||||
'ZZZ' ends with 'AAA' in Latin-1 locales
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum Qt::SplitBehavior
|
||||
\since 5.14
|
||||
|
||||
This enum specifies how the split() functions should behave with
|
||||
respect to empty strings.
|
||||
|
||||
\value KeepEmptyParts If a field is empty, keep it in the result.
|
||||
\value SkipEmptyParts If a field is empty, don't include it in the result.
|
||||
|
||||
\sa QString::split()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum Qt::ClipOperation
|
||||
|
||||
|
@ -545,6 +545,30 @@ public:
|
||||
Q_REQUIRED_RESULT QStringList split(const QRegularExpression &sep, SplitBehavior behavior = KeepEmptyParts) const;
|
||||
Q_REQUIRED_RESULT QVector<QStringRef> splitRef(const QRegularExpression &sep, SplitBehavior behavior = KeepEmptyParts) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
static Q_DECL_CONSTEXPR SplitBehavior _sb(Qt::SplitBehavior sb) Q_DECL_NOTHROW
|
||||
{ return sb & Qt::SkipEmptyParts ? SkipEmptyParts : KeepEmptyParts; }
|
||||
public:
|
||||
|
||||
Q_REQUIRED_RESULT inline QStringList split(const QString &sep, Qt::SplitBehavior behavior,
|
||||
Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
|
||||
Q_REQUIRED_RESULT inline QVector<QStringRef> splitRef(const QString &sep, Qt::SplitBehavior behavior,
|
||||
Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
|
||||
Q_REQUIRED_RESULT inline QStringList split(QChar sep, Qt::SplitBehavior behavior,
|
||||
Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
|
||||
Q_REQUIRED_RESULT inline QVector<QStringRef> splitRef(QChar sep, Qt::SplitBehavior behavior,
|
||||
Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
|
||||
#ifndef QT_NO_REGEXP
|
||||
Q_REQUIRED_RESULT inline QStringList split(const QRegExp &sep, Qt::SplitBehavior behavior) const;
|
||||
Q_REQUIRED_RESULT inline QVector<QStringRef> splitRef(const QRegExp &sep, Qt::SplitBehavior behavior) const;
|
||||
#endif
|
||||
#ifndef QT_NO_REGULAREXPRESSION
|
||||
Q_REQUIRED_RESULT inline QStringList split(const QRegularExpression &sep, Qt::SplitBehavior behavior) const;
|
||||
Q_REQUIRED_RESULT inline QVector<QStringRef> splitRef(const QRegularExpression &sep, Qt::SplitBehavior behavior) const;
|
||||
#endif
|
||||
|
||||
|
||||
enum NormalizationForm {
|
||||
NormalizationForm_D,
|
||||
NormalizationForm_C,
|
||||
@ -1514,6 +1538,11 @@ public:
|
||||
Q_REQUIRED_RESULT QVector<QStringRef> split(QChar sep, QString::SplitBehavior behavior = QString::KeepEmptyParts,
|
||||
Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
|
||||
|
||||
Q_REQUIRED_RESULT inline QVector<QStringRef> split(const QString &sep, Qt::SplitBehavior behavior,
|
||||
Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
|
||||
Q_REQUIRED_RESULT inline QVector<QStringRef> split(QChar sep, Qt::SplitBehavior behavior,
|
||||
Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
|
||||
|
||||
Q_REQUIRED_RESULT QStringRef left(int n) const;
|
||||
Q_REQUIRED_RESULT QStringRef right(int n) const;
|
||||
Q_REQUIRED_RESULT QStringRef mid(int pos, int n = -1) const;
|
||||
|
@ -330,6 +330,23 @@ inline int QStringList::lastIndexOf(const QRegularExpression &rx, int from) cons
|
||||
#endif // QT_CONFIG(regularexpression)
|
||||
#endif // Q_QDOC
|
||||
|
||||
//
|
||||
// QString inline functions:
|
||||
//
|
||||
|
||||
QStringList QString::split(const QString &sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const
|
||||
{ return split(sep, _sb(behavior), cs); }
|
||||
QStringList QString::split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const
|
||||
{ return split(sep, _sb(behavior), cs); }
|
||||
#ifndef QT_NO_REGEXP
|
||||
QStringList QString::split(const QRegExp &sep, Qt::SplitBehavior behavior) const
|
||||
{ return split(sep, _sb(behavior)); }
|
||||
#endif
|
||||
#if QT_CONFIG(regularexpression)
|
||||
QStringList QString::split(const QRegularExpression &sep, Qt::SplitBehavior behavior) const
|
||||
{ return split(sep, _sb(behavior)); }
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QSTRINGLIST_H
|
||||
|
@ -1125,6 +1125,24 @@ extern template class Q_CORE_EXPORT QVector<QPoint>;
|
||||
|
||||
QVector<uint> QStringView::toUcs4() const { return QtPrivate::convertToUcs4(*this); }
|
||||
|
||||
QVector<QStringRef> QString::splitRef(const QString &sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const
|
||||
{ return splitRef(sep, _sb(behavior), cs); }
|
||||
QVector<QStringRef> QString::splitRef(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const
|
||||
{ return splitRef(sep, _sb(behavior), cs); }
|
||||
#ifndef QT_NO_REGEXP
|
||||
QVector<QStringRef> QString::splitRef(const QRegExp &sep, Qt::SplitBehavior behavior) const
|
||||
{ return splitRef(sep, _sb(behavior)); }
|
||||
#endif
|
||||
#if QT_CONFIG(regularexpression)
|
||||
QVector<QStringRef> QString::splitRef(const QRegularExpression &sep, Qt::SplitBehavior behavior) const
|
||||
{ return splitRef(sep, _sb(behavior)); }
|
||||
#endif
|
||||
QVector<QStringRef> QStringRef::split(const QString &sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const
|
||||
{ return split(sep, QString::_sb(behavior), cs); }
|
||||
QVector<QStringRef> QStringRef::split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const
|
||||
{ return split(sep, QString::_sb(behavior), cs); }
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QVECTOR_H
|
||||
|
@ -48,6 +48,14 @@ QString toQString(const T &t) { return QString(t); }
|
||||
QString toQString(const QStringRef &ref) { return ref.toString(); }
|
||||
QString toQString(QStringView view) { return view.toString(); }
|
||||
|
||||
template <typename Iterable>
|
||||
QStringList toQStringList(const Iterable &i) {
|
||||
QStringList result;
|
||||
for (auto &e : i)
|
||||
result.push_back(toQString(e));
|
||||
return result;
|
||||
}
|
||||
|
||||
// FIXME: these are missing at the time of writing, add them, then remove the dummies here:
|
||||
#define MAKE_RELOP(op, A1, A2) \
|
||||
static bool operator op (A1 lhs, A2 rhs) \
|
||||
@ -292,6 +300,26 @@ private Q_SLOTS:
|
||||
void endsWith_QLatin1String_QChar_data() { endsWith_data(false); }
|
||||
void endsWith_QLatin1String_QChar() { endsWith_impl<QLatin1String, QChar>(); }
|
||||
|
||||
private:
|
||||
void split_data(bool rhsHasVariableLength = true);
|
||||
template <typename Haystack, typename Needle> void split_impl() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
// test all combinations of {QString, QStringRef} x {QString, QLatin1String, QChar}:
|
||||
void split_QString_QString_data() { split_data(); }
|
||||
void split_QString_QString() { split_impl<QString, QString>(); }
|
||||
void split_QString_QLatin1String_data() { split_data(); }
|
||||
void split_QString_QLatin1String() { split_impl<QString, QLatin1String>(); }
|
||||
void split_QString_QChar_data() { split_data(false); }
|
||||
void split_QString_QChar() { split_impl<QString, QChar>(); }
|
||||
|
||||
void split_QStringRef_QString_data() { split_data(); }
|
||||
void split_QStringRef_QString() { split_impl<QStringRef, QString>(); }
|
||||
void split_QStringRef_QLatin1String_data() { split_data(); }
|
||||
void split_QStringRef_QLatin1String() { split_impl<QStringRef, QLatin1String>(); }
|
||||
void split_QStringRef_QChar_data() { split_data(false); }
|
||||
void split_QStringRef_QChar() { split_impl<QStringRef, QChar>(); }
|
||||
|
||||
private:
|
||||
void mid_data();
|
||||
template <typename String> void mid_impl();
|
||||
@ -780,6 +808,119 @@ void tst_QStringApiSymmetry::endsWith_impl() const
|
||||
QCOMPARE(haystack.endsWith(needle, Qt::CaseInsensitive), resultCIS);
|
||||
}
|
||||
|
||||
void tst_QStringApiSymmetry::split_data(bool rhsHasVariableLength)
|
||||
{
|
||||
QTest::addColumn<QStringRef>("haystackU16");
|
||||
QTest::addColumn<QLatin1String>("haystackL1");
|
||||
QTest::addColumn<QStringRef>("needleU16");
|
||||
QTest::addColumn<QLatin1String>("needleL1");
|
||||
QTest::addColumn<QStringList>("resultCS");
|
||||
QTest::addColumn<QStringList>("resultCIS");
|
||||
|
||||
if (rhsHasVariableLength) {
|
||||
QTest::addRow("null ~= null$") << QStringRef{} << QLatin1String{}
|
||||
<< QStringRef{} << QLatin1String{}
|
||||
<< QStringList{{}, {}} << QStringList{{}, {}};
|
||||
QTest::addRow("empty ~= null$") << QStringRef{&empty} << QLatin1String("")
|
||||
<< QStringRef{} << QLatin1String{}
|
||||
<< QStringList{empty, empty} << QStringList{empty, empty};
|
||||
QTest::addRow("a ~= null$") << QStringRef{&a} << QLatin1String{"a"}
|
||||
<< QStringRef{} << QLatin1String{}
|
||||
<< QStringList{empty, a, empty} << QStringList{empty, a, empty};
|
||||
QTest::addRow("null ~= empty$") << QStringRef{} << QLatin1String{}
|
||||
<< QStringRef{&empty} << QLatin1String{""}
|
||||
<< QStringList{{}, {}} << QStringList{{}, {}};
|
||||
QTest::addRow("a ~= empty$") << QStringRef{&a} << QLatin1String{"a"}
|
||||
<< QStringRef{&empty} << QLatin1String{""}
|
||||
<< QStringList{empty, a, empty} << QStringList{empty, a, empty};
|
||||
QTest::addRow("empty ~= empty$") << QStringRef{&empty} << QLatin1String{""}
|
||||
<< QStringRef{&empty} << QLatin1String{""}
|
||||
<< QStringList{empty, empty} << QStringList{empty, empty};
|
||||
}
|
||||
QTest::addRow("null ~= a$") << QStringRef{} << QLatin1String{}
|
||||
<< QStringRef{&a} << QLatin1String{"a"}
|
||||
<< QStringList{{}} << QStringList{{}};
|
||||
QTest::addRow("empty ~= a$") << QStringRef{&empty} << QLatin1String{""}
|
||||
<< QStringRef{&a} << QLatin1String{"a"}
|
||||
<< QStringList{empty} << QStringList{empty};
|
||||
|
||||
#define ROW(h, n, cs, cis) \
|
||||
QTest::addRow("%s ~= %s$", #h, #n) << QStringRef(&h) << QLatin1String(#h) \
|
||||
<< QStringRef(&n) << QLatin1String(#n) \
|
||||
<< QStringList cs << QStringList cis
|
||||
ROW(a, a, ({empty, empty}), ({empty, empty}));
|
||||
ROW(a, A, {a}, ({empty, empty}));
|
||||
ROW(a, b, {a}, {a});
|
||||
|
||||
if (rhsHasVariableLength)
|
||||
ROW(b, ab, {b}, {b});
|
||||
|
||||
ROW(ab, b, ({a, empty}), ({a, empty}));
|
||||
if (rhsHasVariableLength) {
|
||||
ROW(ab, ab, ({empty, empty}), ({empty, empty}));
|
||||
ROW(ab, aB, {ab}, ({empty, empty}));
|
||||
ROW(ab, Ab, {ab}, ({empty, empty}));
|
||||
}
|
||||
ROW(ab, c, {ab}, {ab});
|
||||
|
||||
if (rhsHasVariableLength)
|
||||
ROW(bc, abc, {bc}, {bc});
|
||||
|
||||
ROW(Abc, c, ({Ab, empty}), ({Ab, empty}));
|
||||
#if 0
|
||||
if (rhsHasVariableLength) {
|
||||
ROW(Abc, bc, 1, 1);
|
||||
ROW(Abc, bC, 0, 1);
|
||||
ROW(Abc, Bc, 0, 1);
|
||||
ROW(Abc, BC, 0, 1);
|
||||
ROW(aBC, bc, 0, 1);
|
||||
ROW(aBC, bC, 0, 1);
|
||||
ROW(aBC, Bc, 0, 1);
|
||||
ROW(aBC, BC, 1, 1);
|
||||
}
|
||||
#endif
|
||||
ROW(ABC, b, {ABC}, ({A, C}));
|
||||
ROW(ABC, a, {ABC}, ({empty, BC}));
|
||||
#undef ROW
|
||||
}
|
||||
|
||||
static QStringList skipped(const QStringList &sl)
|
||||
{
|
||||
QStringList result;
|
||||
result.reserve(sl.size());
|
||||
for (const QString &s : sl) {
|
||||
if (!s.isEmpty())
|
||||
result.push_back(s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Haystack, typename Needle>
|
||||
void tst_QStringApiSymmetry::split_impl() const
|
||||
{
|
||||
QFETCH(const QStringRef, haystackU16);
|
||||
QFETCH(const QLatin1String, haystackL1);
|
||||
QFETCH(const QStringRef, needleU16);
|
||||
QFETCH(const QLatin1String, needleL1);
|
||||
QFETCH(const QStringList, resultCS);
|
||||
QFETCH(const QStringList, resultCIS);
|
||||
|
||||
const QStringList skippedResultCS = skipped(resultCS);
|
||||
const QStringList skippedResultCIS = skipped(resultCIS);
|
||||
|
||||
const auto haystackU8 = haystackU16.toUtf8();
|
||||
const auto needleU8 = needleU16.toUtf8();
|
||||
|
||||
const auto haystack = make<Haystack>(haystackU16, haystackL1, haystackU8);
|
||||
const auto needle = make<Needle>(needleU16, needleL1, needleU8);
|
||||
|
||||
QCOMPARE(toQStringList(haystack.split(needle)), resultCS);
|
||||
QCOMPARE(toQStringList(haystack.split(needle, Qt::KeepEmptyParts, Qt::CaseSensitive)), resultCS);
|
||||
QCOMPARE(toQStringList(haystack.split(needle, Qt::KeepEmptyParts, Qt::CaseInsensitive)), resultCIS);
|
||||
QCOMPARE(toQStringList(haystack.split(needle, Qt::SkipEmptyParts, Qt::CaseSensitive)), skippedResultCS);
|
||||
QCOMPARE(toQStringList(haystack.split(needle, Qt::SkipEmptyParts, Qt::CaseInsensitive)), skippedResultCIS);
|
||||
}
|
||||
|
||||
void tst_QStringApiSymmetry::mid_data()
|
||||
{
|
||||
QTest::addColumn<QStringRef>("unicode");
|
||||
|
Loading…
Reference in New Issue
Block a user