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 <[email protected]>
6  SPDX-FileCopyrightText: 2004 Ingo Kloecker <[email protected]>
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 <KPluginLoader>
17 
18 #include <QJsonArray>
19 #include <QMimeDatabase>
20 
21 #include <assert.h>
22 
23 using namespace MimeTreeParser;
24 
25 BodyPartFormatterFactoryPrivate::BodyPartFormatterFactoryPrivate(BodyPartFormatterFactory *factory)
26  : q(factory)
27 {
28 }
29 
30 BodyPartFormatterFactoryPrivate::~BodyPartFormatterFactoryPrivate()
31 {
33  while (i.hasNext()) {
34  i.next();
35  auto formatterInfo = i.value();
36  formatterInfo.erase(formatterInfo.begin(), formatterInfo.end());
37  }
38 }
39 
40 void BodyPartFormatterFactoryPrivate::setup()
41 {
42  if (registry.empty()) {
43  messageviewer_create_builtin_bodypart_formatters();
44  q->loadPlugins();
45  }
46  assert(!registry.empty());
47 }
48 
49 void BodyPartFormatterFactoryPrivate::insert(const QString &mimeType, const Interface::BodyPartFormatter *formatter, int priority)
50 {
51  if (mimeType.isEmpty() || !formatter) {
52  return;
53  }
54 
55  QMimeDatabase db;
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 
68 void BodyPartFormatterFactoryPrivate::appendFormattersForType(const QString &mimeType, QVector<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 
79 BodyPartFormatterFactory::BodyPartFormatterFactory()
80  : d(new BodyPartFormatterFactoryPrivate(this))
81 {
82 }
83 
84 BodyPartFormatterFactory::~BodyPartFormatterFactory()
85 {
86  delete d;
87 }
88 
89 BodyPartFormatterFactory *BodyPartFormatterFactory::instance()
90 {
91  static BodyPartFormatterFactory s_instance;
92  return &s_instance;
93 }
94 
95 void BodyPartFormatterFactory::insert(const QString &mimeType, const Interface::BodyPartFormatter *formatter, int priority)
96 {
97  d->insert(mimeType.toLower(), formatter, priority);
98 }
99 
101 {
103  d->setup();
104 
105  QMimeDatabase db;
106  std::vector<QString> processedTypes;
107  processedTypes.push_back(mimeType.toLower());
108 
109  // add all formatters we have along the mimetype hierarchy
110  for (std::size_t i = 0; i < processedTypes.size(); ++i) {
111  const auto mt = db.mimeTypeForName(processedTypes[i]);
112  if (mt.isValid()) {
113  processedTypes[i] = mt.name(); // resolve alias if necessary
114  }
115  if (processedTypes[i] == QLatin1String("application/octet-stream")) { // we'll deal with that later
116  continue;
117  }
118  d->appendFormattersForType(processedTypes[i], r);
119 
120  const auto parentTypes = mt.parentMimeTypes();
121  for (const auto &parentType : parentTypes) {
122  if (std::find(processedTypes.begin(), processedTypes.end(), parentType) != processedTypes.end()) {
123  continue;
124  }
125  processedTypes.push_back(parentType);
126  }
127  }
128 
129  // make sure we always have a suitable fallback formatter
130  if (mimeType.startsWith(QLatin1String("multipart/"))) {
131  if (mimeType != QLatin1String("multipart/mixed")) {
132  d->appendFormattersForType(QStringLiteral("multipart/mixed"), r);
133  }
134  } else {
135  d->appendFormattersForType(QStringLiteral("application/octet-stream"), r);
136  }
137  assert(!r.empty());
138  return r;
139 }
140 
141 void BodyPartFormatterFactory::loadPlugins()
142 {
143  KPluginLoader::forEachPlugin(QStringLiteral("messageviewer/bodypartformatter"), [this](const QString &path) {
144  QPluginLoader loader(path);
145  const auto formatterData = loader.metaData().value(QLatin1String("MetaData")).toObject().value(QLatin1String("formatter")).toArray();
146  if (formatterData.isEmpty()) {
147  return;
148  }
149 
150  auto plugin = qobject_cast<MimeTreeParser::Interface::BodyPartFormatterPlugin *>(loader.instance());
151  if (!plugin) {
152  return;
153  }
154 
156  for (int i = 0; (bfp = plugin->bodyPartFormatter(i)) && i < formatterData.size(); ++i) {
157  const auto metaData = formatterData.at(i).toObject();
158  const auto mimetype = metaData.value(QLatin1String("mimetype")).toString();
159  if (mimetype.isEmpty()) {
160  qCWarning(MIMETREEPARSER_LOG) << "BodyPartFormatterFactory: plugin" << path << "returned empty mimetype specification for index" << i;
161  break;
162  }
163  // priority should always be higher than the built-in ones, otherwise what's the point?
164  const auto priority = metaData.value(QLatin1String("priority")).toInt() + 100;
165  qCDebug(MIMETREEPARSER_LOG) << "plugin for " << mimetype << priority;
166  insert(mimetype, bfp, priority);
167  }
168  });
169 }
The place to obtain BodyPartFormatter candidates for a given mime type.
QVector< const Interface::BodyPartFormatter * > formattersForType(const QString &mimeType) const
Returns all suitable formatters for the given mimetype.
QJsonObject metaData() const const
QJsonObject toObject() const const
int toInt(bool *ok, int base) const const
QJsonArray toArray() const const
bool isEmpty() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QObject * instance()
static void forEachPlugin(const QString &directory, std::function< void(const QString &)> callback=std::function< void(const QString &)>())
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
interface for BodyPartFormatter plugins
QString toLower() const const
void push_back(const T &value)
QJsonValue value(const QString &key) const const
bool empty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Wed Jan 27 2021 23:13:29 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.