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 <KPluginMetaData>
17 #include <QJsonArray>
18 #include <QMimeDatabase>
19 
20 #include <QPluginLoader>
21 #include <cassert>
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() = default;
85 
86 BodyPartFormatterFactory *BodyPartFormatterFactory::instance()
87 {
88  static BodyPartFormatterFactory s_instance;
89  return &s_instance;
90 }
91 
92 void 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] == QLatin1String("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(QLatin1String("multipart/"))) {
128  if (mimeType != QLatin1String("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 
138 void BodyPartFormatterFactory::loadPlugins()
139 {
140  const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("messageviewer/bodypartformatter"));
141  for (const auto &md : plugins) {
142  const auto formatterData = md.rawData().value(QLatin1String("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(QLatin1String("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(QLatin1String("priority")).toInt() + 100;
162  qCDebug(MIMETREEPARSER_LOG) << "plugin for " << mimetype << priority;
163  insert(mimetype, bfp, priority);
164  }
165  }
166 }
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.
bool isEmpty() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
interface for BodyPartFormatter plugins
QString toLower() const const
static QVector< KPluginMetaData > findPlugins(const QString &directory, std::function< bool(const KPluginMetaData &)> filter={})
void push_back(const T &value)
bool empty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Fri Nov 26 2021 23:16:41 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.