KCoreAddons

kpluginmetadata.cpp
1 /*
2  This file is part of the KDE project
3 
4  SPDX-FileCopyrightText: 2014 Alex Richardson <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-only
7 */
8 
9 #include "kpluginmetadata.h"
10 #include "desktopfileparser_p.h"
11 
12 #include "kcoreaddons_debug.h"
13 #include <QFileInfo>
14 #include <QJsonArray>
15 #include <QJsonDocument>
16 #include <QLocale>
17 #include <QMimeDatabase>
18 #include <QPluginLoader>
19 
20 #include "kaboutdata.h"
21 #include "kpluginloader.h"
22 
23 class KPluginMetaDataPrivate : public QSharedData
24 {
25 public:
26  QString metaDataFileName;
27 };
28 
30 {
31 }
32 
34  : m_metaData(other.m_metaData)
35  , m_fileName(other.fileName())
36  , d(other.d)
37 {
38 }
39 
41 {
42  m_metaData = other.m_metaData;
43  m_fileName = other.m_fileName;
44  d = other.d;
45  return *this;
46 }
47 
49 {
50 }
51 
53 {
54  if (file.endsWith(QLatin1String(".desktop"))) {
55  loadFromDesktopFile(file, QStringList());
56  } else if (file.endsWith(QLatin1String(".json"))) {
57  d = new KPluginMetaDataPrivate;
58  QFile f(file);
59  bool b = f.open(QIODevice::ReadOnly);
60  if (!b) {
61  qCWarning(KCOREADDONS_DEBUG) << "Couldn't open" << file;
62  return;
63  }
64 
65  QJsonParseError error;
66  m_metaData = QJsonDocument::fromJson(f.readAll(), &error).object();
67  if (error.error) {
68  qCWarning(KCOREADDONS_DEBUG) << "error parsing" << file << error.errorString();
69  }
70  m_fileName = file;
71  d->metaDataFileName = file;
72  } else {
73  QPluginLoader loader(file);
74  m_fileName = QFileInfo(loader.fileName()).absoluteFilePath();
75  m_metaData = loader.metaData().value(QStringLiteral("MetaData")).toObject();
76  }
77 }
78 
80 {
81  m_fileName = QFileInfo(loader.fileName()).absoluteFilePath();
82  m_metaData = loader.metaData().value(QStringLiteral("MetaData")).toObject();
83 }
84 
86 {
87  m_fileName = QFileInfo(loader.fileName()).absoluteFilePath();
88  m_metaData = loader.metaData().value(QStringLiteral("MetaData")).toObject();
89 }
90 
92 {
93  m_fileName = file;
94  m_metaData = metaData;
95 }
96 
97 KPluginMetaData::KPluginMetaData(const QJsonObject &metaData, const QString &pluginFile, const QString &metaDataFile)
98 {
99  m_fileName = pluginFile;
100  m_metaData = metaData;
101  if (!metaDataFile.isEmpty()) {
102  d = new KPluginMetaDataPrivate;
103  d->metaDataFileName = metaDataFile;
104  }
105 }
106 
108 {
109  KPluginMetaData result;
110  result.loadFromDesktopFile(file, serviceTypes);
111  return result;
112 }
113 
114 void KPluginMetaData::loadFromDesktopFile(const QString &file, const QStringList &serviceTypes)
115 {
116  QString libraryPath;
117  if (!DesktopFileParser::convert(file, serviceTypes, m_metaData, &libraryPath)) {
118  Q_ASSERT(!isValid());
119  return; // file could not be parsed for some reason, leave this object invalid
120  }
121  d = new KPluginMetaDataPrivate;
122  d->metaDataFileName = QFileInfo(file).absoluteFilePath();
123  if (!libraryPath.isEmpty()) {
124  // this was a plugin with a shared library
125  m_fileName = libraryPath;
126  } else {
127  // no library, make filename point to the .desktop file
128  m_fileName = d->metaDataFileName;
129  }
130 }
131 
133 {
134  return m_metaData;
135 }
136 
138 {
139  return m_fileName;
140 }
141 
143 {
144  return d ? d->metaDataFileName : m_fileName;
145 }
146 
147 bool KPluginMetaData::isValid() const
148 {
149  // it can be valid even if m_fileName is empty (as long as the plugin id is set in the metadata)
150  return !pluginId().isEmpty() && !m_metaData.isEmpty();
151 }
152 
153 bool KPluginMetaData::isHidden() const
154 {
155  return rootObject()[QStringLiteral("Hidden")].toBool();
156 }
157 
158 QJsonObject KPluginMetaData::rootObject() const
159 {
160  return m_metaData[QStringLiteral("KPlugin")].toObject();
161 }
162 
164 {
165  const QJsonValue value = obj.value(key);
166  if (value.isUndefined() || value.isObject() || value.isNull()) {
167  return QStringList();
168  } else if (value.isArray()) {
169  return value.toVariant().toStringList();
170  } else {
171  QString asString = value.isString() ? value.toString() : value.toVariant().toString();
172  if (asString.isEmpty()) {
173  return QStringList();
174  }
175  const QString id = obj.value(QStringLiteral("KPlugin")).toObject().value(QStringLiteral("Id")).toString();
176  qCWarning(KCOREADDONS_DEBUG) << "Expected JSON property" << key
177  << "to be a string list."
178  " Treating it as a list with a single entry:"
179  << asString << id.toLatin1().constData();
180  return QStringList(asString);
181  }
182 }
183 
185 {
186  QString languageWithCountry = QLocale().name();
187  auto it = jo.constFind(key + QLatin1Char('[') + languageWithCountry + QLatin1Char(']'));
188  if (it != jo.constEnd()) {
189  return it.value();
190  }
191  const QStringRef language = languageWithCountry.midRef(0, languageWithCountry.indexOf(QLatin1Char('_')));
192  it = jo.constFind(key + QLatin1Char('[') + language + QLatin1Char(']'));
193  if (it != jo.constEnd()) {
194  return it.value();
195  }
196  // no translated value found -> check key
197  it = jo.constFind(key);
198  if (it != jo.constEnd()) {
199  return jo.value(key);
200  }
201  return defaultValue;
202 }
203 
204 QString KPluginMetaData::readTranslatedString(const QJsonObject &jo, const QString &key, const QString &defaultValue)
205 {
206  return readTranslatedValue(jo, key, defaultValue).toString(defaultValue);
207 }
208 
209 static inline void addPersonFromJson(const QJsonObject &obj, QList<KAboutPerson> *out)
210 {
211  KAboutPerson person = KAboutPerson::fromJSON(obj);
212  if (person.name().isEmpty()) {
213  qCWarning(KCOREADDONS_DEBUG) << "Invalid plugin metadata: Attempting to create a KAboutPerson from json without 'Name' property:" << obj;
214  return;
215  }
216  out->append(person);
217 }
218 
219 static QList<KAboutPerson> aboutPersonFromJSON(const QJsonValue &people)
220 {
222  if (people.isObject()) {
223  // single author
224  addPersonFromJson(people.toObject(), &ret);
225  } else if (people.isArray()) {
226  const QJsonArray peopleArray = people.toArray();
227  for (const QJsonValue &val : peopleArray) {
228  if (val.isObject()) {
229  addPersonFromJson(val.toObject(), &ret);
230  }
231  }
232  }
233  return ret;
234 }
235 
237 {
238  return aboutPersonFromJSON(rootObject()[QStringLiteral("Authors")]);
239 }
240 
242 {
243  return aboutPersonFromJSON(rootObject()[QStringLiteral("Translators")]);
244 }
245 
247 {
248  return aboutPersonFromJSON(rootObject()[QStringLiteral("OtherContributors")]);
249 }
250 
252 {
253  return rootObject()[QStringLiteral("Category")].toString();
254 }
255 
257 {
258  return readTranslatedString(rootObject(), QStringLiteral("Description"));
259 }
260 
262 {
263  return rootObject()[QStringLiteral("Icon")].toString();
264 }
265 
267 {
268  return rootObject()[QStringLiteral("License")].toString();
269 }
270 
272 {
273  return KAboutLicense::byKeyword(license()).text();
274 }
275 
277 {
278  return readTranslatedString(rootObject(), QStringLiteral("Name"));
279 }
280 
282 {
283  return readTranslatedString(rootObject(), QStringLiteral("Copyright"));
284 }
285 
287 {
288  return readTranslatedString(rootObject(), QStringLiteral("ExtraInformation"));
289 }
290 
292 {
293  QJsonObject root = rootObject();
294  auto nameFromMetaData = root.constFind(QStringLiteral("Id"));
295  if (nameFromMetaData != root.constEnd()) {
296  const QString id = nameFromMetaData.value().toString();
297  if (!id.isEmpty()) {
298  return id;
299  }
300  }
301  // passing QFileInfo an empty string gives the CWD, which is not what we want
302  if (m_fileName.isEmpty()) {
303  return QString();
304  }
305  return QFileInfo(m_fileName).baseName();
306 }
307 
309 {
310  return rootObject()[QStringLiteral("Version")].toString();
311 }
312 
314 {
315  return rootObject()[QStringLiteral("Website")].toString();
316 }
317 
318 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 79)
320 {
321  return readStringList(rootObject(), QStringLiteral("Dependencies"));
322 }
323 #endif
324 
326 {
327  return readStringList(rootObject(), QStringLiteral("ServiceTypes"));
328 }
329 
331 {
332  return readStringList(rootObject(), QStringLiteral("MimeTypes"));
333 }
334 
335 bool KPluginMetaData::supportsMimeType(const QString &mimeType) const
336 {
337  QMimeDatabase db;
338  const QMimeType mime = db.mimeTypeForName(mimeType);
339  if (!mime.isValid()) {
340  return false;
341  }
342 
343  const QStringList mimes = mimeTypes();
344  auto inherits = [&](const QString &supportedMimeName) {
345  return mime.inherits(supportedMimeName);
346  };
347  return std::find_if(mimes.begin(), mimes.end(), inherits) != mimes.end();
348 }
349 
351 {
352  return readStringList(rootObject(), QStringLiteral("FormFactors"));
353 }
354 
356 {
357  QJsonValue val = rootObject()[QStringLiteral("EnabledByDefault")];
358  if (val.isBool()) {
359  return val.toBool();
360  } else if (val.isString()) {
361  return val.toString() == QLatin1String("true");
362  }
363  return false;
364 }
365 
367 {
368  return rootObject()[QStringLiteral("InitialPreference")].toInt();
369 }
370 
371 QString KPluginMetaData::value(const QString &key, const QString &defaultValue) const
372 {
373  const QJsonValue value = m_metaData.value(key);
374  if (value.isString()) {
375  return value.toString(defaultValue);
376  } else if (value.isArray()) {
377  qCWarning(KCOREADDONS_DEBUG) << "Expected JSON property" << key
378  << "to be a single string."
379  " but it is a stringlist";
380  const QStringList list = value.toVariant().toStringList();
381  if (list.isEmpty()) {
382  return defaultValue;
383  }
384  return list.join(QChar::fromLatin1(','));
385  } else if (value.isBool()) {
386  qCWarning(KCOREADDONS_DEBUG) << "Expected JSON property" << key
387  << "to be a single string."
388  " but it is a bool";
389  return value.toBool() ? QStringLiteral("true") : QStringLiteral("false");
390  }
391  return defaultValue;
392 }
393 
395 {
396  return m_fileName == other.m_fileName && m_metaData == other.m_metaData;
397 }
398 
400 {
401  return QPluginLoader(m_fileName).instance();
402 }
403 
404 template<class T>
405 QVariantList listToVariant(const QList<T> &values)
406 {
407  QVariantList ret;
408  ret.reserve(values.count());
409  for (const auto &license : values) {
411  }
412  return ret;
413 }
414 
415 QVariantList KPluginMetaData::authorsVariant() const
416 {
417  return listToVariant(authors());
418 }
419 
420 QVariantList KPluginMetaData::translatorsVariant() const
421 {
422  return listToVariant(translators());
423 }
424 
425 QVariantList KPluginMetaData::otherContributorsVariant() const
426 {
427  return listToVariant(otherContributors());
428 }
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QStringList serviceTypes() const
Returns the service types that this plugin implements.
static QJsonValue readTranslatedValue(const QJsonObject &jo, const QString &key, const QJsonValue &defaultValue=QJsonValue())
Reads a value from jo but unlike QJsonObject::value() it allows different entries for each locale Thi...
QString value(const QString &key, const QString &defaultValue=QString()) const
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QString description() const
QList< KAboutPerson > authors() const
QString fileName() const
int initialPreference() const
QString website() const
QString name() const
QStringList formFactors() const
QJsonObject::const_iterator constEnd() const const
QStringList dependencies() const
QObject * instantiate() const
Tries to instantiate this plugin using KPluginMetaData::fileName().
bool inherits(const QString &mimeTypeName) const const
bool isObject() const const
QString join(const QString &separator) const const
QJsonObject metaData() const const
QStringList mimeTypes() const
QVector< V > values(const QMultiHash< K, V > &c)
bool isUndefined() const const
bool isEmpty() const const
int count(const T &value) const const
void append(const T &value)
QString toString() const const
QString metaDataFileName() const
This class behaves largely like QPluginLoader (and, indeed, uses it internally), but additionally rea...
Definition: kpluginloader.h:61
QString licenseText() const
QJsonObject toObject() const const
QString iconName() const
QChar fromLatin1(char c)
QJsonArray toArray() const const
bool isEmpty() const const
static QString readTranslatedString(const QJsonObject &jo, const QString &key, const QString &defaultValue=QString())
QString absoluteFilePath() const const
bool isEmpty() const const
QJsonObject metaData() const
Returns the meta data for the plugin.
const char * constData() const const
QByteArray readAll()
QObject * instance()
bool toBool(bool defaultValue) const const
QJsonObject rawData() const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
bool isArray() const const
QString name() const const
static QStringList readStringList(const QJsonObject &jo, const QString &key)
KPluginMetaData()
Creates an invalid KPluginMetaData instance.
virtual bool open(QIODevice::OpenMode mode) override
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
bool isEnabledByDefault() const
QVariant toVariant() const const
QList::iterator end()
bool isString() const const
bool isValid() const const
QStringRef midRef(int position, int n) const const
QVariant fromValue(const T &value)
This class is used to store information about a person or developer.
Definition: kaboutdata.h:56
static KAboutPerson fromJSON(const QJsonObject &obj)
Creates a KAboutPerson from a JSON object with the following structure:
Definition: kaboutdata.cpp:93
QString license() const
bool isNull() const const
QList< KAboutPerson > otherContributors() const
QByteArray toLatin1() const const
QStringList toStringList() const const
QList< KAboutPerson > translators() const
static KAboutLicense byKeyword(const QString &keyword)
Fetch a known license by a keyword/spdx ID.
Definition: kaboutdata.cpp:370
This class allows easily accessing some standardized values from the JSON metadata that can be embedd...
QJsonValue value(const QString &key) const const
QString category() const
bool supportsMimeType(const QString &mimeType) const
QString copyrightText() const
QString errorString() const const
bool operator==(const KPluginMetaData &other) const
static KPluginMetaData fromDesktopFile(const QString &file, const QStringList &serviceTypes=QStringList())
Load a KPluginMetaData instace from a .desktop file.
QString toString() const const
QString baseName() const const
QList::iterator begin()
bool isValid() const
QJsonObject::const_iterator constFind(const QString &key) const const
QString pluginId() const
KPluginMetaData & operator=(const KPluginMetaData &)
Copy assignment.
~KPluginMetaData()
Destructor.
QString version() const
bool isBool() const const
bool isHidden() const
QString extraInformation() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Apr 13 2021 23:01:32 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.