Remove QRegExp from QVariant

Add an operator QVariant() to QRegExp to keep things at source compatible
as possible.

Add a hack to QVariant::load/save() to recognize the old typeid
for QRegExp and stream them correctly as long as the streaming operators
for QRegExp are registered.

Also move the datastream test for QRegExp to tst_qregexp, and adjust it to
the qvariant changes.

Change-Id: I120b38a7541b43ec07a21b17f7f35c55f071eb75
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Lars Knoll 2020-04-03 20:58:30 +02:00 committed by Fabian Kosmale
parent b2ee684a13
commit 45ed28a9d3
16 changed files with 223 additions and 149 deletions

View File

@ -53,7 +53,6 @@
#include "quuid.h"
#include "qvariant.h"
#include "qdatastream.h"
#include "qregexp.h"
#include "qmetatypeswitcher_p.h"
#if QT_CONFIG(regularexpression)
@ -352,7 +351,6 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry)
\value QRect QRect
\value QPoint QPoint
\value QUrl QUrl
\value QRegExp QRegExp
\value QRegularExpression QRegularExpression
\value QDateTime QDateTime
\value QPointF QPointF

View File

@ -137,7 +137,6 @@ inline Q_DECL_CONSTEXPR int qMetaTypeId();
F(QLineF, 24, QLineF) \
F(QPoint, 25, QPoint) \
F(QPointF, 26, QPointF) \
F(QRegExp, 27, QRegExp) \
QT_FOR_EACH_STATIC_EASINGCURVE(F) \
F(QUuid, 30, QUuid) \
F(QVariant, 41, QVariant) \
@ -474,7 +473,7 @@ public:
QChar = 7, QString = 10, QStringList = 11, QByteArray = 12,
QBitArray = 13, QDate = 14, QTime = 15, QDateTime = 16, QUrl = 17,
QLocale = 18, QRect = 19, QRectF = 20, QSize = 21, QSizeF = 22,
QLine = 23, QLineF = 24, QPoint = 25, QPointF = 26, QRegExp = 27,
QLine = 23, QLineF = 24, QPoint = 25, QPointF = 26,
QEasingCurve = 29, QUuid = 30, QVariant = 41, QModelIndex = 42,
QPersistentModelIndex = 50, QRegularExpression = 44,
QJsonValue = 45, QJsonObject = 46, QJsonArray = 47, QJsonDocument = 48,

View File

@ -164,9 +164,6 @@ template<> struct TypeDefinition<QLineF> { static const bool IsAvailable = false
template<> struct TypeDefinition<QPoint> { static const bool IsAvailable = false; };
template<> struct TypeDefinition<QPointF> { static const bool IsAvailable = false; };
#endif
#ifdef QT_NO_REGEXP
template<> struct TypeDefinition<QRegExp> { static const bool IsAvailable = false; };
#endif
#if !QT_CONFIG(regularexpression)
template<> struct TypeDefinition<QRegularExpression> { static const bool IsAvailable = false; };
#endif

View File

@ -57,7 +57,6 @@
#include "qstringlist.h"
#include "qurl.h"
#include "qlocale.h"
#include "qregexp.h"
#include "quuid.h"
#if QT_CONFIG(itemmodel)
#include "qabstractitemmodel.h"
@ -1681,7 +1680,6 @@ Q_CORE_EXPORT void QVariantPrivate::registerHandler(const int /* Modules::Names
\value Quaternion a QQuaternion
\value Rect a QRect
\value RectF a QRectF
\value RegExp a QRegExp
\value RegularExpression a QRegularExpression
\value Region a QRegion
\value Size a QSize
@ -2076,12 +2074,6 @@ QVariant::QVariant(const char *val)
Constructs a new variant with a locale value, \a l.
*/
/*!
\fn QVariant::QVariant(const QRegExp &regExp)
Constructs a new variant with the regexp value \a regExp.
*/
/*!
\fn QVariant::QVariant(const QRegularExpression &re)
@ -2216,11 +2208,6 @@ QVariant::QVariant(const QUrl &u)
QVariant::QVariant(const QLocale &l)
: d(Locale)
{ v_construct<QLocale>(&d, l); }
#ifndef QT_NO_REGEXP
QVariant::QVariant(const QRegExp &regExp)
: d(RegExp)
{ v_construct<QRegExp>(&d, regExp); }
#endif // QT_NO_REGEXP
#if QT_CONFIG(regularexpression)
QVariant::QVariant(const QRegularExpression &re)
: d(RegularExpression)
@ -2262,7 +2249,7 @@ QVariant::QVariant(const QPersistentModelIndex &modelIndex)
Note that return values in the ranges QVariant::Char through
QVariant::RegExp and QVariant::Font through QVariant::Transform
correspond to the values in the ranges QMetaType::QChar through
QMetaType::QRegExp and QMetaType::QFont through QMetaType::QQuaternion.
QMetaType::QRegularExpression and QMetaType::QFont through QMetaType::QQuaternion.
Pay particular attention when working with char and QChar
variants. Note that there is no QVariant constructor specifically
@ -2493,7 +2480,10 @@ void QVariant::load(QDataStream &s)
qint8 is_null = false;
if (s.version() >= QDataStream::Qt_4_2)
s >> is_null;
if (typeId == QVariant::UserType) {
if (typeId == 27) {
// used to be QRegExp in Qt 4/5
typeId = QMetaType::type("QRegExp");
} else if (typeId == QVariant::UserType) {
QByteArray name;
s >> name;
typeId = QMetaType::type(name.constData());
@ -2532,9 +2522,11 @@ void QVariant::load(QDataStream &s)
void QVariant::save(QDataStream &s) const
{
quint32 typeId = d.type().id();
if (typeId >= QMetaType::User)
bool saveAsUserType = false;
if (typeId >= QMetaType::User) {
typeId = QMetaType::User;
bool fakeUserType = false;
saveAsUserType = true;
}
if (s.version() < QDataStream::Qt_4_0) {
int i;
for (i = 0; i <= MapFromThreeCount - 1; ++i) {
@ -2550,6 +2542,7 @@ void QVariant::save(QDataStream &s) const
} else if (s.version() < QDataStream::Qt_5_0) {
if (typeId == QMetaType::User) {
typeId = 127; // QVariant::UserType had this value in Qt4
saveAsUserType = true;
} else if (typeId >= 128 - 97 && typeId <= LastCoreType) {
// In Qt4 id == 128 was FirstExtCoreType. In Qt5 ExtCoreTypes set was merged to CoreTypes
// by moving all ids down by 97.
@ -2566,15 +2559,22 @@ void QVariant::save(QDataStream &s) const
} else if (typeId == QMetaType::QPolygonF || typeId == QMetaType::QUuid) {
// These existed in Qt 4 only as a custom type
typeId = 127;
fakeUserType = true;
saveAsUserType = true;
}
}
const char *typeName = nullptr;
if (saveAsUserType) {
typeName = QMetaType::typeName(d.type().id());
if (!strcmp(typeName, "QRegExp")) {
typeId = 27; // QRegExp in Qt 4/5
typeName = nullptr;
}
}
s << typeId;
if (s.version() >= QDataStream::Qt_4_2)
s << qint8(d.is_null);
if (d.type().id() >= int(QVariant::UserType) || fakeUserType) {
if (typeName)
s << QMetaType::typeName(userType());
}
if (!isValid()) {
if (s.version() < QDataStream::Qt_5_0)
@ -2938,22 +2938,6 @@ QLocale QVariant::toLocale() const
return qVariantToHelper<QLocale>(d, handlerManager);
}
/*!
\fn QRegExp QVariant::toRegExp() const
\since 4.1
Returns the variant as a QRegExp if the variant has userType()
\l QMetaType::QRegExp; otherwise returns an empty QRegExp.
\sa canConvert(int targetTypeId), convert()
*/
#ifndef QT_NO_REGEXP
QRegExp QVariant::toRegExp() const
{
return qVariantToHelper<QRegExp>(d, handlerManager);
}
#endif
#if QT_CONFIG(regularexpression)
/*!
\fn QRegularExpression QVariant::toRegularExpression() const
@ -3356,7 +3340,7 @@ static const quint32 qCanConvertMatrix[QMetaType::LastCoreType + 1] =
/*QPointF*/ 1 << QMetaType::QPoint,
/*QRegExp*/ 0,
/*unused, was: QRegExp*/ 0,
/*QHash*/ 0,

View File

@ -81,9 +81,6 @@ class QSize;
class QSizeF;
class QRect;
class QRectF;
#ifndef QT_NO_REGEXP
class QRegExp;
#endif // QT_NO_REGEXP
#if QT_CONFIG(regularexpression)
class QRegularExpression;
#endif // QT_CONFIG(regularexpression)
@ -160,7 +157,6 @@ class Q_CORE_EXPORT QVariant
LineF = QMetaType::QLineF,
Point = QMetaType::QPoint,
PointF = QMetaType::QPointF,
RegExp = QMetaType::QRegExp,
#if QT_CONFIG(regularexpression)
RegularExpression = QMetaType::QRegularExpression,
#endif
@ -252,9 +248,6 @@ class Q_CORE_EXPORT QVariant
QVariant(const QRectF &rect);
#endif
QVariant(const QLocale &locale);
#ifndef QT_NO_REGEXP
QVariant(const QRegExp &regExp);
#endif // QT_NO_REGEXP
#if QT_CONFIG(regularexpression)
QVariant(const QRegularExpression &re);
#endif // QT_CONFIG(regularexpression)
@ -329,9 +322,6 @@ class Q_CORE_EXPORT QVariant
QRectF toRectF() const;
#endif
QLocale toLocale() const;
#ifndef QT_NO_REGEXP
QRegExp toRegExp() const;
#endif // QT_NO_REGEXP
#if QT_CONFIG(regularexpression)
QRegularExpression toRegularExpression() const;
#endif // QT_CONFIG(regularexpression)

View File

@ -4388,6 +4388,18 @@ bool QRegExp::exactMatch(const QString &str) const
}
}
/*!
Returns the regexp as a QVariant
*/
QRegExp::operator QVariant() const
{
QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
QVariant v;
v.setValue(*this);
return v;
QT_WARNING_POP
}
// ### Qt 5: make non-const
/*!
Attempts to find a match in \a str from position \a offset (0 by

View File

@ -45,6 +45,7 @@
#ifndef QT_NO_REGEXP
#include <QtCore/qstring.h>
#include <QtCore/qvariant.h>
QT_BEGIN_NAMESPACE
@ -93,6 +94,8 @@ public:
bool exactMatch(const QString &str) const;
operator QVariant() const;
int indexIn(const QString &str, int offset = 0, CaretMode caretMode = CaretAtZero) const;
int lastIndexIn(const QString &str, int offset = -1, CaretMode caretMode = CaretAtZero) const;
int matchedLength() const;
@ -130,8 +133,6 @@ private:
QRegExpPrivate *priv;
};
Q_DECLARE_TYPEINFO(QRegExp, Q_MOVABLE_TYPE);
#ifndef QT_NO_DATASTREAM
Q_CORE_EXPORT QDataStream &operator<<(QDataStream &out, const QRegExp &regExp);
Q_CORE_EXPORT QDataStream &operator>>(QDataStream &in, QRegExp &regExp);
@ -143,6 +144,8 @@ Q_CORE_EXPORT QDebug operator<<(QDebug, const QRegExp &);
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QRegExp)
#endif // QT_NO_REGEXP
#endif // QREGEXP_H

View File

@ -223,16 +223,6 @@ template<> struct TestValueFactory<QMetaType::QPersistentModelIndex> {
template<> struct TestValueFactory<QMetaType::Nullptr> {
static std::nullptr_t *create() { return new std::nullptr_t; }
};
template<> struct TestValueFactory<QMetaType::QRegExp> {
static QRegExp *create()
{
#ifndef QT_NO_REGEXP
return new QRegExp("A*");
#else
return 0;
#endif
}
};
template<> struct TestValueFactory<QMetaType::QRegularExpression> {
static QRegularExpression *create()
{

View File

@ -171,7 +171,6 @@ private slots:
void toLocale();
void toRegExp();
void toRegularExpression();
void url();
@ -1219,14 +1218,6 @@ void tst_QVariant::toLocale()
loc = variant.toLocale();
}
void tst_QVariant::toRegExp()
{
QVariant variant;
QRegExp rx = variant.toRegExp();
variant = QRegExp("foo");
rx = variant.toRegExp();
}
void tst_QVariant::toRegularExpression()
{
QVariant variant;
@ -1320,8 +1311,6 @@ void tst_QVariant::writeToReadFromDataStream_data()
QTest::newRow( "uint_valid" ) << QVariant( (uint)123 ) << false;
QTest::newRow( "qchar" ) << QVariant(QChar('a')) << false;
QTest::newRow( "qchar_null" ) << QVariant(QChar(0)) << true;
QTest::newRow( "regexp" ) << QVariant(QRegExp("foo", Qt::CaseInsensitive)) << false;
QTest::newRow( "regexp_empty" ) << QVariant(QRegExp()) << false;
QTest::newRow( "regularexpression" ) << QVariant(QRegularExpression("abc.*def")) << false;
QTest::newRow( "regularexpression_empty" ) << QVariant(QRegularExpression()) << false;
@ -1768,7 +1757,6 @@ void tst_QVariant::typeName_data()
QTest::newRow("38") << int(QVariant::LineF) << QByteArray("QLineF");
QTest::newRow("39") << int(QVariant::RectF) << QByteArray("QRectF");
QTest::newRow("40") << int(QVariant::PointF) << QByteArray("QPointF");
QTest::newRow("41") << int(QVariant::RegExp) << QByteArray("QRegExp");
QTest::newRow("44") << int(QVariant::Transform) << QByteArray("QTransform");
QTest::newRow("45") << int(QVariant::Hash) << QByteArray("QVariantHash");
QTest::newRow("46") << int(QVariant::Matrix4x4) << QByteArray("QMatrix4x4");
@ -3902,7 +3890,6 @@ void tst_QVariant::implicitConstruction()
F(LineF) \
F(Point) \
F(PointF) \
F(RegExp) \
F(EasingCurve) \
F(Uuid) \
F(ModelIndex) \

View File

@ -111,9 +111,6 @@ private slots:
void stream_QString_data();
void stream_QString();
void stream_QRegExp_data();
void stream_QRegExp();
#if QT_CONFIG(regularexpression)
void stream_QRegularExpression_data();
void stream_QRegularExpression();
@ -232,7 +229,6 @@ private:
void writeQRegion(QDataStream *s);
void writeQSize(QDataStream *s);
void writeQString(QDataStream* dev);
void writeQRegExp(QDataStream* dev);
#if QT_CONFIG(regularexpression)
void writeQRegularExpression(QDataStream *dev);
#endif
@ -266,7 +262,6 @@ private:
void readQRegion(QDataStream *s);
void readQSize(QDataStream *s);
void readQString(QDataStream *s);
void readQRegExp(QDataStream *s);
#if QT_CONFIG(regularexpression)
void readQRegularExpression(QDataStream *s);
#endif
@ -519,71 +514,6 @@ void tst_QDataStream::readQString(QDataStream *s)
// ************************************
static QRegExp QRegExpData(int index)
{
switch (index) {
case 0: return QRegExp();
case 1: return QRegExp("");
case 2: return QRegExp("A", Qt::CaseInsensitive);
case 3: return QRegExp("ABCDE FGHI", Qt::CaseSensitive, QRegExp::Wildcard);
case 4: return QRegExp("This is a long string", Qt::CaseInsensitive, QRegExp::FixedString);
case 5: return QRegExp("And again a string with a \nCRLF", Qt::CaseInsensitive, QRegExp::RegExp);
case 6:
{
QRegExp rx("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRESTUVWXYZ 1234567890 ~`!@#$%^&*()_-+={[}]|\\:;\"'<,>.?/");
rx.setMinimal(true);
return rx;
}
}
return QRegExp("foo");
}
#define MAX_QREGEXP_DATA 7
void tst_QDataStream::stream_QRegExp_data()
{
stream_data(MAX_QREGEXP_DATA);
}
void tst_QDataStream::stream_QRegExp()
{
STREAM_IMPL(QRegExp);
}
void tst_QDataStream::writeQRegExp(QDataStream* s)
{
QRegExp test(QRegExpData(dataIndex(QTest::currentDataTag())));
*s << test;
*s << QString("Her er det noe tekst");
*s << test;
*s << QString("nonempty");
*s << test;
*s << QVariant(test);
}
void tst_QDataStream::readQRegExp(QDataStream *s)
{
QRegExp R;
QString S;
QVariant V;
QRegExp test(QRegExpData(dataIndex(QTest::currentDataTag())));
*s >> R;
QCOMPARE(R, test);
*s >> S;
QCOMPARE(S, QString("Her er det noe tekst"));
*s >> R;
QCOMPARE(R, test);
*s >> S;
QCOMPARE(S, QString("nonempty"));
*s >> R;
QCOMPARE(R, test);
*s >> V;
QCOMPARE(V.type(), QVariant::RegExp);
QCOMPARE(V.toRegExp(), test);
}
// ************************************
#if QT_CONFIG(regularexpression)
static QRegularExpression QRegularExpressionData(int index)
{

View File

@ -2,3 +2,4 @@ CONFIG += testcase
TARGET = tst_qregexp
QT = core testlib
SOURCES = tst_qregexp.cpp
RESOURCES += qregexp.qrc

View File

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file>data/qdatastream_4.9.bin</file>
<file>data/qdatastream_5.0.bin</file>
</qresource>
</RCC>

View File

@ -1,3 +1,4 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
@ -83,6 +84,15 @@ private slots:
void filterList();
void replaceInList();
void datastream_data();
void datastream();
void datastream2();
private:
void readQRegExp(QDataStream *s);
void writeQRegExp(QDataStream* dev);
};
// Testing get/set functions
@ -1545,5 +1555,172 @@ void tst_QRegExp::replaceInList()
QCOMPARE( list5, list6 );
}
static QRegExp QRegExpData(int index)
{
switch (index) {
case 0: return QRegExp();
case 1: return QRegExp("");
case 2: return QRegExp("A", Qt::CaseInsensitive);
case 3: return QRegExp("ABCDE FGHI", Qt::CaseSensitive, QRegExp::Wildcard);
case 4: return QRegExp("This is a long string", Qt::CaseInsensitive, QRegExp::FixedString);
case 5: return QRegExp("And again a string with a \nCRLF", Qt::CaseInsensitive, QRegExp::RegExp);
case 6:
{
QRegExp rx("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRESTUVWXYZ 1234567890 ~`!@#$%^&*()_-+={[}]|\\:;\"'<,>.?/");
rx.setMinimal(true);
return rx;
}
}
return QRegExp("foo");
}
#define MAX_QREGEXP_DATA 7
void tst_QRegExp::datastream_data()
{
QTest::addColumn<QString>("device");
QTest::addColumn<QString>("byteOrder");
const char * const devices[] = {
"file",
"bytearray",
"buffer",
0
};
for (int d=0; devices[d] != 0; d++) {
QString device = devices[d];
for (int b=0; b<2; b++) {
QString byte_order = b == 0 ? "BigEndian" : "LittleEndian";
QString tag = device + QLatin1Char('_') + byte_order;
for (int e = 0; e < MAX_QREGEXP_DATA; e++) {
QTest::newRow(qPrintable(tag + QLatin1Char('_') + QString::number(e))) << device << byte_order;
}
}
}
}
static int dataIndex(const QString &tag)
{
int pos = tag.lastIndexOf(QLatin1Char('_'));
if (pos >= 0) {
int ret = 0;
QString count = tag.mid(pos + 1);
bool ok;
ret = count.toInt(&ok);
if (ok)
return ret;
}
return -1;
}
void tst_QRegExp::datastream()
{
QFETCH(QString, device); \
qRegisterMetaTypeStreamOperators<QRegExp>("QRegExp");
if (device == "bytearray") { \
QByteArray ba; \
QDataStream sout(&ba, QIODevice::WriteOnly); \
writeQRegExp(&sout); \
QDataStream sin(&ba, QIODevice::ReadOnly); \
readQRegExp(&sin); \
} else if (device == "file") { \
QString fileName = "qdatastream.out"; \
QFile fOut(fileName); \
QVERIFY(fOut.open(QIODevice::WriteOnly)); \
QDataStream sout(&fOut); \
writeQRegExp(&sout); \
fOut.close(); \
QFile fIn(fileName); \
QVERIFY(fIn.open(QIODevice::ReadOnly)); \
QDataStream sin(&fIn); \
readQRegExp(&sin); \
fIn.close(); \
} else if (device == "buffer") { \
QByteArray ba(10000, '\0'); \
QBuffer bOut(&ba); \
bOut.open(QIODevice::WriteOnly); \
QDataStream sout(&bOut); \
writeQRegExp(&sout); \
bOut.close(); \
QBuffer bIn(&ba); \
bIn.open(QIODevice::ReadOnly); \
QDataStream sin(&bIn); \
readQRegExp(&sin); \
bIn.close(); \
}
}
static void saveQVariantFromDataStream(const QString &fileName, QDataStream::Version version)
{
QFile file(fileName);
QVERIFY(file.open(QIODevice::ReadOnly));
QDataStream dataFileStream(&file);
QString typeName;
dataFileStream >> typeName;
QByteArray data = file.readAll();
const int id = QMetaType::type(typeName.toLatin1());
QBuffer buffer;
buffer.open(QIODevice::ReadWrite);
QDataStream stream(&buffer);
stream.setVersion(version);
QVariant constructedVariant(static_cast<QVariant::Type>(id));
QCOMPARE(constructedVariant.userType(), id);
stream << constructedVariant;
// We are testing QVariant there is no point in testing full array.
QCOMPARE(buffer.data().left(5), data.left(5));
buffer.seek(0);
QVariant recunstructedVariant;
stream >> recunstructedVariant;
QCOMPARE(recunstructedVariant.userType(), constructedVariant.userType());
}
void tst_QRegExp::datastream2()
{
saveQVariantFromDataStream(QLatin1String(":/data/qdatastream_4.9.bin"), QDataStream::Qt_4_9);
saveQVariantFromDataStream(QLatin1String(":/data/qdatastream_5.0.bin"), QDataStream::Qt_5_0);
}
void tst_QRegExp::writeQRegExp(QDataStream* s)
{
QRegExp test(QRegExpData(dataIndex(QTest::currentDataTag())));
*s << test;
*s << QString("Her er det noe tekst");
*s << test;
*s << QString("nonempty");
*s << test;
*s << QVariant(test);
}
void tst_QRegExp::readQRegExp(QDataStream *s)
{
QRegExp R;
QString S;
QVariant V;
QRegExp test(QRegExpData(dataIndex(QTest::currentDataTag())));
*s >> R;
QCOMPARE(R, test);
*s >> S;
QCOMPARE(S, QString("Her er det noe tekst"));
*s >> R;
QCOMPARE(R, test);
*s >> S;
QCOMPARE(S, QString("nonempty"));
*s >> R;
QCOMPARE(R, test);
*s >> V;
QCOMPARE(V.userType(), qMetaTypeId<QRegExp>());
QCOMPARE(qvariant_cast<QRegExp>(V), test);
}
QTEST_APPLESS_MAIN(tst_QRegExp)
#include "tst_qregexp.moc"