aboutsummaryrefslogtreecommitdiff
path: root/src/base
diff options
context:
space:
mode:
authorLinus Jahn <lnj@kaidan.im>2021-08-01 20:53:05 +0200
committerLinus Jahn <lnj@kaidan.im>2021-08-22 16:09:02 +0200
commit3e465fd0d273c52c66b3aeb3dc4ae147c914bdd6 (patch)
treeb83645613b8ec0f96010554d462ad251adb5e131 /src/base
parentc1b6788bc22f68cef18eb01c5f30db0667293442 (diff)
downloadqxmpp-3e465fd0d273c52c66b3aeb3dc4ae147c914bdd6.tar.gz
Introduce data form parsing abstractions
Diffstat (limited to 'src/base')
-rw-r--r--src/base/QXmppDataForm.cpp11
-rw-r--r--src/base/QXmppDataForm.h2
-rw-r--r--src/base/QXmppDataFormBase.cpp237
-rw-r--r--src/base/QXmppDataFormBase.h140
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