Messagelib

bodypartformatterfactory.cpp
1/*
2 bodypartformatterfactory.cpp
3
4 This file is part of KMail, the KDE mail client.
5 SPDX-FileCopyrightText: 2004 Marc Mutz <mutz@kde.org>
6 SPDX-FileCopyrightText: 2004 Ingo Kloecker <kloecker@kde.org>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10
11#include "bodypartformatterfactory.h"
12#include "bodypartformatterfactory_p.h"
13#include "interfaces/bodypartformatter.h"
14#include "mimetreeparser_debug.h"
15
16#include <KPluginMetaData>
17#include <QJsonArray>
18#include <QMimeDatabase>
19
20#include <QPluginLoader>
21#include <cassert>
22
23using namespace MimeTreeParser;
24
25BodyPartFormatterFactoryPrivate::BodyPartFormatterFactoryPrivate(BodyPartFormatterFactory *factory)
26 : q(factory)
27{
28}
29
30BodyPartFormatterFactoryPrivate::~BodyPartFormatterFactoryPrivate()
31{
33 while (i.hasNext()) {
34 i.next();
35 auto formatterInfo = i.value();
36 formatterInfo.erase(formatterInfo.begin(), formatterInfo.end());
37 }
38}
39
40void BodyPartFormatterFactoryPrivate::setup()
41{
42 if (registry.empty()) {
43 messageviewer_create_builtin_bodypart_formatters();
44 q->loadPlugins();
45 }
46 assert(!registry.empty());
47}
48
49void BodyPartFormatterFactoryPrivate::insert(const QString &mimeType, const Interface::BodyPartFormatter *formatter, int priority)
50{
51 if (mimeType.isEmpty() || !formatter) {
52 return;
53 }
54
56 const auto mt = db.mimeTypeForName(mimeType);
57 FormatterInfo info;
58 info.formatter = formatter;
59 info.priority = priority;
60
61 auto &v = registry[mt.isValid() ? mt.name() : mimeType];
62 v.push_back(info);
63 std::stable_sort(v.begin(), v.end(), [](FormatterInfo lhs, FormatterInfo rhs) {
64 return lhs.priority > rhs.priority;
65 });
66}
67
68void BodyPartFormatterFactoryPrivate::appendFormattersForType(const QString &mimeType, QList<const Interface::BodyPartFormatter *> &formatters)
69{
70 const auto it = registry.constFind(mimeType);
71 if (it == registry.constEnd()) {
72 return;
73 }
74 for (const auto &f : it.value()) {
75 formatters.push_back(f.formatter);
76 }
77}
78
79BodyPartFormatterFactory::BodyPartFormatterFactory()
80 : d(new BodyPartFormatterFactoryPrivate(this))
81{
82}
83
84BodyPartFormatterFactory::~BodyPartFormatterFactory() = default;
85
86BodyPartFormatterFactory *BodyPartFormatterFactory::instance()
87{
88 static BodyPartFormatterFactory s_instance;
89 return &s_instance;
90}
91
92void BodyPartFormatterFactory::insert(const QString &mimeType, const Interface::BodyPartFormatter *formatter, int priority)
93{
94 d->insert(mimeType.toLower(), formatter, priority);
95}
96
98{
100 d->setup();
101
102 QMimeDatabase db;
103 std::vector<QString> processedTypes;
104 processedTypes.push_back(mimeType.toLower());
105
106 // add all formatters we have along the mimetype hierarchy
107 for (std::size_t i = 0; i < processedTypes.size(); ++i) {
108 const auto mt = db.mimeTypeForName(processedTypes[i]);
109 if (mt.isValid()) {
110 processedTypes[i] = mt.name(); // resolve alias if necessary
111 }
112 if (processedTypes[i] == QLatin1StringView("application/octet-stream")) { // we'll deal with that later
113 continue;
114 }
115 d->appendFormattersForType(processedTypes[i], r);
116
117 const auto parentTypes = mt.parentMimeTypes();
118 for (const auto &parentType : parentTypes) {
119 if (std::find(processedTypes.begin(), processedTypes.end(), parentType) != processedTypes.end()) {
120 continue;
121 }
122 processedTypes.push_back(parentType);
123 }
124 }
125
126 // make sure we always have a suitable fallback formatter
127 if (mimeType.startsWith(QLatin1StringView("multipart/"))) {
128 if (mimeType != QLatin1StringView("multipart/mixed")) {
129 d->appendFormattersForType(QStringLiteral("multipart/mixed"), r);
130 }
131 } else {
132 d->appendFormattersForType(QStringLiteral("application/octet-stream"), r);
133 }
134 assert(!r.empty());
135 return r;
136}
137
138void BodyPartFormatterFactory::loadPlugins()
139{
140 const QList<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("pim6/messageviewer/bodypartformatter"));
141 for (const auto &md : plugins) {
142 const auto formatterData = md.rawData().value(QLatin1StringView("formatter")).toArray();
143 if (formatterData.isEmpty()) {
144 continue;
145 }
146 QPluginLoader loader(md.fileName());
147 auto plugin = qobject_cast<MimeTreeParser::Interface::BodyPartFormatterPlugin *>(loader.instance());
148 if (!plugin) {
149 continue;
150 }
151
153 for (int i = 0; (bfp = plugin->bodyPartFormatter(i)) && i < formatterData.size(); ++i) {
154 const auto metaData = formatterData.at(i).toObject();
155 const auto mimetype = metaData.value(QLatin1StringView("mimetype")).toString();
156 if (mimetype.isEmpty()) {
157 qCWarning(MIMETREEPARSER_LOG) << "BodyPartFormatterFactory: plugin" << md.fileName() << "returned empty mimetype specification for index" << i;
158 break;
159 }
160 // priority should always be higher than the built-in ones, otherwise what's the point?
161 const auto priority = metaData.value(QLatin1StringView("priority")).toInt() + 100;
162 qCDebug(MIMETREEPARSER_LOG) << "plugin for " << mimetype << priority;
163 insert(mimetype, bfp, priority);
164 }
165 }
166}
static QList< KPluginMetaData > findPlugins(const QString &directory, std::function< bool(const KPluginMetaData &)> filter={}, KPluginMetaDataOptions options={})
The place to obtain BodyPartFormatter candidates for a given mime type.
QList< const Interface::BodyPartFormatter * > formattersForType(const QString &mimeType) const
Returns all suitable formatters for the given mimetype.
KCALUTILS_EXPORT QString mimeType()
KIOCORE_EXPORT MimetypeJob * mimetype(const QUrl &url, JobFlags flags=DefaultFlags)
bool empty() const const
void push_back(parameter_type value)
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
bool isEmpty() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QString toLower() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:33:26 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.