diff options
| author | Linus Jahn <lnj@kaidan.im> | 2021-08-01 20:53:05 +0200 |
|---|---|---|
| committer | Linus Jahn <lnj@kaidan.im> | 2021-08-22 16:09:02 +0200 |
| commit | 3e465fd0d273c52c66b3aeb3dc4ae147c914bdd6 (patch) | |
| tree | b83645613b8ec0f96010554d462ad251adb5e131 /src/base | |
| parent | c1b6788bc22f68cef18eb01c5f30db0667293442 (diff) | |
| download | qxmpp-3e465fd0d273c52c66b3aeb3dc4ae147c914bdd6.tar.gz | |
Introduce data form parsing abstractions
Diffstat (limited to 'src/base')
| -rw-r--r-- | src/base/QXmppDataForm.cpp | 11 | ||||
| -rw-r--r-- | src/base/QXmppDataForm.h | 2 | ||||
| -rw-r--r-- | src/base/QXmppDataFormBase.cpp | 237 | ||||
| -rw-r--r-- | src/base/QXmppDataFormBase.h | 140 |
4 files changed, 390 insertions, 0 deletions
diff --git a/src/base/QXmppDataForm.cpp b/src/base/QXmppDataForm.cpp index 74c176c5..307e6f9e 100644 --- a/src/base/QXmppDataForm.cpp +++ b/src/base/QXmppDataForm.cpp @@ -25,6 +25,7 @@ #include "QXmppDataForm.h" #include "QXmppConstants_p.h" +#include "QXmppDataFormBase.h" #include "QXmppUtils.h" #include <optional> @@ -684,6 +685,16 @@ QXmppDataForm::QXmppDataForm(Type type, d->instructions = instructions; } +/// +/// Constructs a data form from any type based on QXmppDataFormBase. +/// +/// \since QXmpp 1.5 +/// +QXmppDataForm::QXmppDataForm(const QXmppDataFormBase &based) +{ + *this = based.toDataForm(); +} + /// Constructs a copy of \a other. QXmppDataForm::QXmppDataForm(const QXmppDataForm &other) = default; diff --git a/src/base/QXmppDataForm.h b/src/base/QXmppDataForm.h index 0cfa5e23..35d1019e 100644 --- a/src/base/QXmppDataForm.h +++ b/src/base/QXmppDataForm.h @@ -36,6 +36,7 @@ class QMimeType; class QUrl; +class QXmppDataFormBase; class QXmppDataFormPrivate; class QXmppDataFormFieldPrivate; class QXmppDataFormMediaPrivate; @@ -195,6 +196,7 @@ public: const QList<Field> &fields, const QString &title = {}, const QString &instructions = {}); + QXmppDataForm(const QXmppDataFormBase &based); QXmppDataForm(const QXmppDataForm &other); ~QXmppDataForm(); diff --git a/src/base/QXmppDataFormBase.cpp b/src/base/QXmppDataFormBase.cpp new file mode 100644 index 00000000..1cf9691d --- /dev/null +++ b/src/base/QXmppDataFormBase.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2008-2021 The QXmpp developers + * + * Author: + * Linus Jahn + * + * Source: + * https://github.com/qxmpp-project/qxmpp + * + * This file is a part of QXmpp library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#include "QXmppDataFormBase.h" + +#include "QXmppDataForm.h" + +#include <QDateTime> + +/// +/// \class QXmppDataFormBase +/// +/// QXmppDataFormBase is an abstract class types that can be serialized to data +/// forms. +/// +/// QXmppDataFormBase based types can easily be converted to QXmppDataForms, it +/// is as simple as this: +/// \code +/// MyDataFormBase foo; +/// QXmppDataForm dataForm(foo); +/// \endcode +/// +/// To make this work, you will need to at least implement the toDataForm() +/// method. For parsing your type you should also create a static creator +/// method, like this: +/// \code +/// static std::optional<MyType> fromDataForm(const QXmppDataForm &); +/// \endcode +/// +/// \since QXmpp 1.5 +/// + +/// +/// Serializes all fields to a QXmppDataForm. +/// +QXmppDataForm QXmppDataFormBase::toDataForm() const +{ + QXmppDataForm form(QXmppDataForm::Form); + + // add FORM_TYPE + if (const auto type = formType(); !type.isEmpty()) { + form.fields() << QXmppDataForm::Field(QXmppDataForm::Field::HiddenField, + QStringLiteral("FORM_TYPE"), + type); + } + + // manual serialization parts + serializeForm(form); + + return form; +} + +/// +/// Parses the QXmppDataForm. +/// +bool QXmppDataFormBase::fromDataForm(const QXmppDataForm &form, QXmppDataFormBase &output) +{ + output.parseForm(form); + return true; +} + +/// +/// \fn QXmppDataFormBase::formType +/// +/// Returns the 'FORM_TYPE' value of the parsed form. +/// + +/// +/// \fn QXmppDataFormBase::parseForm +/// +/// This is called when a QXmppDataForm is parsed. You can parse all values from +/// the given form and its fields. +/// + +/// +/// \fn QXmppDataFormBase::serializeForm +/// +/// This is called the object is serialized to a QXmppDataForm. You need to +/// create a new QXmppDataForm and serialize all fields and values. +/// + +/// +/// \fn QXmppDataFormBase::parseUInt +/// +/// Parses an unsigned int from a QVariant (QString). Returns std::nullopt if +/// the no number could be parsed. +/// + +/// +/// \fn QXmppDataFormBase::parseULongLong +/// +/// Parses an unsigned long long from a QVariant (QString). Returns std::nullopt +/// if the no number could be parsed. +/// + +/// +/// \fn QXmppDataFormBase::parseBool +/// +/// Returns the contained boolean value if the QVariant contains a bool. +/// + +/// +/// \fn QXmppDataFormBase::serializeValue +/// +/// Adds a new field to the form with the given field type, field name and value. +/// + +/// +/// \fn QXmppDataFormBase::serializeNullable +/// +/// Adds a new field to the form if \code !value.isNull() \endcode. +/// + +/// +/// \fn QXmppDataFormBase::serializeEmptyable +/// +/// Adds a new field to the form if \code !value.isEmpty() \endcode. +/// + +/// +/// \fn QXmppDataFormBase::serializeOptional +/// +/// Adds a new field to the form if \code optional.has_value() \endcode. +/// + +/// +/// \fn QXmppDataFormBase::serializeOptionalNumber +/// +/// Adds a new field to the form if \code optional.has_value() \endcode. +/// Converts the optional's value to QString using QString::number(). +/// + +/// +/// Adds a new field to the form if the passed QDateTime is valid and formats it +/// as ISO timestamp and always uses UTC. +/// +void QXmppDataFormBase::serializeDatetime(QXmppDataForm &form, const QString &name, const QDateTime &datetime, QXmppDataForm::Field::Type type) +{ + if (datetime.isValid()) { + serializeValue(form, type, name, datetime.toUTC().toString(Qt::ISODate)); + } +} + +/// +/// \class QXmppExtensibleDataFormBase +/// +/// This class is used for parsing a QXmppDataForm in an extensible way with +/// inheritance and keeping additional unknown fields. +/// +/// When inheriting you need to reimplement parseField(), serializeForm() and +/// formType(). Also you should add a static parsing function (e.g. +/// QXmppPubSubMetadata::fromDataForm()). +/// +/// \since QXmpp 1.5 +/// + +class QXmppExtensibleDataFormBasePrivate : public QSharedData +{ +public: + QList<QXmppDataForm::Field> unknownFields; +}; + +QXmppExtensibleDataFormBase::QXmppExtensibleDataFormBase() + : d(new QXmppExtensibleDataFormBasePrivate) +{ +} + +/// \cond +QXmppExtensibleDataFormBase::QXmppExtensibleDataFormBase(const QXmppExtensibleDataFormBase &) = default; +QXmppExtensibleDataFormBase::~QXmppExtensibleDataFormBase() = default; +QXmppExtensibleDataFormBase &QXmppExtensibleDataFormBase::operator=(const QXmppExtensibleDataFormBase &) = default; +/// \endcond + +/// +/// Returns all fields that couldn't be parsed. +/// +QList<QXmppDataForm::Field> QXmppExtensibleDataFormBase::unknownFields() const +{ + return d->unknownFields; +} + +/// +/// Sets all additional fields to be serialized. +/// +void QXmppExtensibleDataFormBase::setUnknownFields(const QList<QXmppDataForm::Field> &unknownFields) +{ + d->unknownFields = unknownFields; +} + +void QXmppExtensibleDataFormBase::parseForm(const QXmppDataForm &form) +{ + const auto fields = form.fields(); + for (const auto &field : fields) { + // FORM_TYPE fields are not saved (override this function to save them) + if (!parseField(field) && + !(field.type() == QXmppDataForm::Field::HiddenField && + field.key() == QStringLiteral("FORM_TYPE"))) { + d->unknownFields << field; + } + } +} + +void QXmppExtensibleDataFormBase::serializeForm(QXmppDataForm &form) const +{ + form.fields() << d->unknownFields; +} + +/// +/// Returns true if a field has been parsed. +/// +/// Should be reimplemented to do actual parsing. All fields that can't be +/// parsed end up as unknownFields(). +/// +bool QXmppExtensibleDataFormBase::parseField(const QXmppDataForm::Field &) +{ + return false; +} diff --git a/src/base/QXmppDataFormBase.h b/src/base/QXmppDataFormBase.h new file mode 100644 index 00000000..b46dfe5e --- /dev/null +++ b/src/base/QXmppDataFormBase.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2008-2021 The QXmpp developers + * + * Author: + * Linus Jahn + * + * Source: + * https://github.com/qxmpp-project/qxmpp + * + * This file is a part of QXmpp library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#ifndef QXMPPDATAFORMBASED_H +#define QXMPPDATAFORMBASED_H + +#include "QXmppDataForm.h" + +#include <optional> + +class QXmppDataForm; + +class QXMPP_EXPORT QXmppDataFormBase +{ +public: + virtual ~QXmppDataFormBase() = default; + + virtual QXmppDataForm toDataForm() const; + +protected: + static bool fromDataForm(const QXmppDataForm &form, QXmppDataFormBase &parent); + + virtual QString formType() const = 0; + virtual void parseForm(const QXmppDataForm &) = 0; + virtual void serializeForm(QXmppDataForm &) const = 0; + + std::optional<quint32> parseUInt(const QVariant &variant) + { + bool ok; + if (const auto result = variant.toString().toUInt(&ok); ok) { + return result; + } + return std::nullopt; + } + + std::optional<quint64> parseULongLong(const QVariant &variant) + { + bool ok; + if (const auto result = variant.toString().toULongLong(&ok); ok) { + return result; + } + return std::nullopt; + } + + std::optional<bool> parseBool(const QVariant &variant) + { + if (variant.type() == QVariant::Bool) { + return variant.toBool(); + } + return std::nullopt; + } + + template<typename T> + static void serializeValue(QXmppDataForm &form, QXmppDataForm::Field::Type type, const QString &name, const T &value) + { + form.fields() << QXmppDataForm::Field(type, name, value); + } + + template<typename T> + static void serializeNullable(QXmppDataForm &form, QXmppDataForm::Field::Type type, const QString &name, const T &value) + { + if (!value.isNull()) { + serializeValue(form, type, name, value); + } + } + + template<typename T> + static void serializeEmptyable(QXmppDataForm &form, QXmppDataForm::Field::Type type, const QString &name, const T &value) + { + if (!value.isEmpty()) { + serializeValue(form, type, name, value); + } + } + + template<typename T, typename ValueConverter = T (*)(T)> + static void serializeOptional(QXmppDataForm &form, QXmppDataForm::Field::Type type, const QString &name, const std::optional<T> &optional, ValueConverter convert = [](T a) { return a; }) + { + if (optional.has_value()) { + serializeValue(form, type, name, convert(*optional)); + } + } + + template<typename T> + static void serializeOptionalNumber(QXmppDataForm &form, QXmppDataForm::Field::Type type, const QString &name, std::optional<T> optional) + { + if (optional.has_value()) { + serializeValue(form, type, name, QString::number(*optional)); + } + } + + static void serializeDatetime(QXmppDataForm &form, const QString &name, const QDateTime &datetime, QXmppDataForm::Field::Type type = QXmppDataForm::Field::TextSingleField); +}; + +class QXmppExtensibleDataFormBasePrivate; + +class QXMPP_EXPORT QXmppExtensibleDataFormBase : public QXmppDataFormBase +{ +public: + QXmppExtensibleDataFormBase(); + /// \cond + QXmppExtensibleDataFormBase(const QXmppExtensibleDataFormBase &); + virtual ~QXmppExtensibleDataFormBase(); + + QXmppExtensibleDataFormBase &operator=(const QXmppExtensibleDataFormBase &); + /// \endcond + + QList<QXmppDataForm::Field> unknownFields() const; + void setUnknownFields(const QList<QXmppDataForm::Field> &unknownFields); + +protected: + void parseForm(const QXmppDataForm &) override; + void serializeForm(QXmppDataForm &) const override; + + virtual bool parseField(const QXmppDataForm::Field &); + +private: + QSharedDataPointer<QXmppExtensibleDataFormBasePrivate> d; +}; + +#endif // QXMPPDATAFORMBASED_H |
