aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKitsune Ral <Kitsune-Ral@users.sf.net>2016-08-24 20:26:43 +0900
committerKitsune Ral <Kitsune-Ral@users.sf.net>2016-08-25 09:12:52 +0900
commit6a288217f1279fff1867fbd4c2d2b22e44e43d59 (patch)
tree289a0eebebff9760b10494042d562ab57692a4a2
parent596533a1840961857a51652f89c3a51435794d02 (diff)
downloadlibquotient-6a288217f1279fff1867fbd4c2d2b22e44e43d59.tar.gz
libquotient-6a288217f1279fff1867fbd4c2d2b22e44e43d59.zip
Structured parsing, folded repetitive initialization code
-rw-r--r--events/roommessageevent.cpp181
-rw-r--r--events/roommessageevent.h77
2 files changed, 135 insertions, 123 deletions
diff --git a/events/roommessageevent.cpp b/events/roommessageevent.cpp
index 0da35527..513534ad 100644
--- a/events/roommessageevent.cpp
+++ b/events/roommessageevent.cpp
@@ -70,6 +70,18 @@ MessageEventContent::Base* RoomMessageEvent::content() const
return d->content;
}
+template <class ContentT>
+MessageEventContent::Base* make(const QJsonObject& json)
+{
+ return new ContentT(json);
+}
+
+template <class ContentT>
+MessageEventContent::Base* make2(const QJsonObject& json)
+{
+ return new ContentT(json["url"].toString(), json["info"].toObject());
+};
+
RoomMessageEvent* RoomMessageEvent::fromJson(const QJsonObject& obj)
{
RoomMessageEvent* e = new RoomMessageEvent();
@@ -82,104 +94,53 @@ RoomMessageEvent* RoomMessageEvent::fromJson(const QJsonObject& obj)
}
if( obj.contains("content") )
{
- using namespace MessageEventContent;
-
- QJsonObject content = obj.value("content").toObject();
- QString msgtype = content.value("msgtype").toString();
-
- if( msgtype == "m.text" )
- {
- e->d->msgtype = MessageEventType::Text;
- e->d->content = new TextContent(obj);
- }
- else if( msgtype == "m.emote" )
- {
- e->d->msgtype = MessageEventType::Emote;
- e->d->content = new TextContent(obj);
- }
- else if( msgtype == "m.notice" )
- {
- e->d->msgtype = MessageEventType::Notice;
- e->d->content = new TextContent(obj);
- }
- else if( msgtype == "m.image" )
- {
- e->d->msgtype = MessageEventType::Image;
- ImageContent* c = new ImageContent;
- c->url = QUrl(content.value("url").toString());
- QJsonObject info = content.value("info").toObject();
- c->height = info.value("h").toInt();
- c->width = info.value("w").toInt();
- c->size = info.value("size").toInt();
- c->mimetype = info.value("mimetype").toString();
- e->d->content = c;
- }
- else if( msgtype == "m.file" )
- {
- e->d->msgtype = MessageEventType::File;
- FileContent* c = new FileContent;
- c->filename = content.value("filename").toString();
- c->url = QUrl(content.value("url").toString());
- QJsonObject info = content.value("info").toObject();
- c->size = info.value("size").toInt();
- c->mimetype = info.value("mimetype").toString();
- e->d->content = c;
- }
- else if( msgtype == "m.location" )
+ const QJsonObject content = obj["content"].toObject();
+ if ( content.contains("msgtype") && content.contains("body") )
{
- e->d->msgtype = MessageEventType::Location;
- LocationContent* c = new LocationContent;
- c->geoUri = content.value("geo_uri").toString();
- c->thumbnailUrl = QUrl(content.value("thumbnail_url").toString());
- QJsonObject info = content.value("thumbnail_info").toObject();
- c->thumbnailHeight = info.value("h").toInt();
- c->thumbnailWidth = info.value("w").toInt();
- c->thumbnailSize = info.value("size").toInt();
- c->thumbnailMimetype = info.value("mimetype").toString();
- e->d->content = c;
- }
- else if( msgtype == "m.video" )
- {
- e->d->msgtype = MessageEventType::Video;
- VideoContent* c = new VideoContent;
- c->url = QUrl(content.value("url").toString());
- QJsonObject info = content.value("info").toObject();
- c->height = info.value("h").toInt();
- c->width = info.value("w").toInt();
- c->duration = info.value("duration").toInt();
- c->size = info.value("size").toInt();
- c->thumbnailUrl = QUrl(info.value("thumnail_url").toString());
- QJsonObject thumbnailInfo = content.value("thumbnail_info").toObject();
- c->thumbnailHeight = thumbnailInfo.value("h").toInt();
- c->thumbnailWidth = thumbnailInfo.value("w").toInt();
- c->thumbnailSize = thumbnailInfo.value("size").toInt();
- c->thumbnailMimetype = thumbnailInfo.value("mimetype").toString();
- e->d->content = c;
- }
- else if( msgtype == "m.audio" )
- {
- e->d->msgtype = MessageEventType::Audio;
- AudioContent* c = new AudioContent;
- c->url = QUrl(content.value("url").toString());
- QJsonObject info = content.value("info").toObject();
- c->duration = info.value("duration").toInt();
- c->mimetype = info.value("mimetype").toString();
- c->size = info.value("size").toInt();
- e->d->content = c;
+ using namespace MessageEventContent;
+
+ e->d->plainBody = content["body"].toString();
+
+ struct Factory
+ {
+ QString jsonTag;
+ MessageEventType enumTag;
+ MessageEventContent::Base*(*make)(const QJsonObject& json);
+ };
+
+ const Factory factories[] {
+ { "m.text", MessageEventType::Text, make<TextContent> },
+ { "m.emote", MessageEventType::Emote, make<TextContent> },
+ { "m.notice", MessageEventType::Notice, make<TextContent> },
+ { "m.image", MessageEventType::Image, make<ImageContent> },
+ { "m.file", MessageEventType::File, make<FileContent> },
+ { "m.location", MessageEventType::Location, make<LocationContent> },
+ { "m.video", MessageEventType::Video, make2<VideoContent> },
+ { "m.audio", MessageEventType::Audio, make2<AudioContent> },
+ // Insert new message types before this line
+ };
+
+ QString msgtype = content.value("msgtype").toString();
+ for (auto f: factories)
+ {
+ if (msgtype == f.jsonTag)
+ {
+ e->d->msgtype = f.enumTag;
+ e->d->content = f.make(content);
+ break;
+ }
+ }
+ if (e->d->msgtype == MessageEventType::Unknown)
+ {
+ qDebug() << "RoomMessageEvent: unknown msgtype: " << msgtype;
+ qDebug() << obj;
+ e->d->content = new Base;
+ }
}
else
{
- qDebug() << "RoomMessageEvent: unknown msgtype: " << msgtype;
+ qWarning() << "RoomMessageEvent(" << e->id() << "): no body or msgtype";
qDebug() << obj;
- e->d->msgtype = MessageEventType::Unknown;
- e->d->content = new Base;
- }
-
- if( content.contains("body") )
- {
- e->d->plainBody = content.value("body").toString();
- } else {
- qDebug() << "RoomMessageEvent: body not found";
}
}
return e;
@@ -203,3 +164,37 @@ TextContent::TextContent(const QJsonObject& json)
mimeType = db.mimeTypeForData(body.toUtf8());
}
}
+
+FileInfo::FileInfo(QUrl u, const QJsonObject& infoJson, QString originalFilename)
+ : url(u)
+ , fileSize(infoJson["size"].toInt())
+ , mimetype(QMimeDatabase().mimeTypeForName(infoJson["mimetype"].toString()))
+ , originalName(originalFilename)
+{
+ if (!mimetype.isValid())
+ mimetype = QMimeDatabase().mimeTypeForData(QByteArray());
+}
+
+ImageInfo::ImageInfo(QUrl u, const QJsonObject& infoJson)
+ : FileInfo(u, infoJson)
+ , imageSize(infoJson["w"].toInt(), infoJson["h"].toInt())
+{ }
+
+LocationContent::LocationContent(const QJsonObject& json)
+ : geoUri(json["geo_uri"].toString())
+ , thumbnail(json["thumbnail_url"].toString(),
+ json["thumbnail_info"].toObject())
+{ }
+
+VideoContent::VideoContent(QUrl u, const QJsonObject& infoJson)
+ : FileInfo(u, infoJson)
+ , duration(infoJson["duration"].toInt())
+ , imageSize(infoJson["w"].toInt(), infoJson["h"].toInt())
+ , thumbnail(infoJson["thumbnail_url"].toString(),
+ infoJson["thumbnail_info"].toObject())
+{ }
+
+AudioContent::AudioContent(QUrl u, const QJsonObject& infoJson)
+ : FileInfo(u, infoJson)
+ , duration(infoJson["duration"].toInt())
+{ }
diff --git a/events/roommessageevent.h b/events/roommessageevent.h
index 7b6394ba..83022b4d 100644
--- a/events/roommessageevent.h
+++ b/events/roommessageevent.h
@@ -21,6 +21,7 @@
#include <QtCore/QUrl>
#include <QtCore/QMimeType>
+#include <QtCore/QSize>
#include "event.h"
@@ -66,6 +67,10 @@ namespace QMatrixClient
namespace MessageEventContent
{
+ // The below structures fairly follow CS spec 11.2.1.6. The overall
+ // set of attributes for each content types is a superset of the spec
+ // but specific aggregation structure is altered.
+
class TextContent: public Base
{
public:
@@ -75,59 +80,71 @@ namespace QMatrixClient
QString body;
};
- class ImageContent: public Base
+ class FileInfo: public Base
{
public:
+ FileInfo(QUrl u, const QJsonObject& infoJson,
+ QString originalFilename = QString());
+
QUrl url;
- int height;
- int width;
- int size;
- QString mimetype;
+ int fileSize;
+ QMimeType mimetype;
+ QString originalName;
};
- class FileContent: public Base
+ class ImageInfo: public FileInfo
{
public:
- QString filename;
- QString mimetype;
- int size;
- QUrl url;
+ ImageInfo(QUrl u, const QJsonObject& infoJson);
+
+ QSize imageSize;
};
+ template <class ContentInfoT>
+ class ThumbnailedContent: public ContentInfoT
+ {
+ public:
+ ThumbnailedContent(const QJsonObject& json)
+ : ContentInfoT(json["url"].toString(), json["info"].toObject())
+ , thumbnail(json["thumbnail_url"].toString(),
+ json["thumbnail_info"].toObject())
+ { }
+
+ ImageInfo thumbnail;
+ };
+
+ using ImageContent = ThumbnailedContent<ImageInfo>;
+ using FileContent = ThumbnailedContent<FileInfo>;
+
class LocationContent: public Base
{
public:
+ LocationContent(const QJsonObject& json);
+
QString geoUri;
- int thumbnailHeight;
- int thumbnailWidth;
- QString thumbnailMimetype;
- int thumbnailSize;
- QUrl thumbnailUrl;
+ ImageInfo thumbnail;
};
- class VideoContent: public Base
+ // The spec structures m.video messages differently for some reason -
+ // instead of putting a thumbnail block on the top level, as with
+ // file and image, it puts it inside "info" key. So instead of
+ // using ThumbnailContent<> base, we add the thumbnail into VideoInfo explicitly.
+ class VideoContent: public FileInfo
{
public:
- QUrl url;
+ VideoContent(QUrl u, const QJsonObject& infoJson);
+
int duration;
- int width;
- int height;
- int size;
- QString mimetype;
- int thumbnailWidth;
- int thumbnailHeight;
- int thumbnailSize;
- QString thumbnailMimetype;
- QUrl thumbnailUrl;
+ QSize imageSize;
+ ImageInfo thumbnail;
};
- class AudioContent: public Base
+ class AudioContent: public FileInfo
{
public:
- QUrl url;
- int size;
+ AudioContent(QUrl u, const QJsonObject& infoJson);
+
int duration;
- QString mimetype;
};
}
}