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 <QFileInfo>
13 #include <QJsonArray>
14 #include <QJsonDocument>
15 #include <QLocale>
16 #include <QMimeDatabase>
17 #include <QPluginLoader>
18 #include "kcoreaddons_debug.h"
19 
20 #include "kpluginloader.h"
21 #include "kaboutdata.h"
22 
23 class KPluginMetaDataPrivate : public QSharedData
24 {
25 public:
26  QString metaDataFileName;
27 };
28 
30 {
31 }
32 
34  : m_metaData(other.m_metaData), m_fileName(other.fileName()), d(other.d)
35 {
36 }
37 
39 {
40  m_metaData = other.m_metaData;
41  m_fileName = other.m_fileName;
42  d = other.d;
43  return *this;
44 }
45 
47 {
48 }
49 
51 {
52  if (file.endsWith(QLatin1String(".desktop"))) {
53  loadFromDesktopFile(file, QStringList());
54  } else if (file.endsWith(QLatin1String(".json"))) {
55  d = new KPluginMetaDataPrivate;
56  QFile f(file);
57  bool b = f.open(QIODevice::ReadOnly);
58  if (!b) {
59  qCWarning(KCOREADDONS_DEBUG) << "Couldn't open" << file;
60  return;
61  }
62 
63  QJsonParseError error;
64  m_metaData = QJsonDocument::fromJson(f.readAll(), &error).object();
65  if (error.error) {
66  qCWarning(KCOREADDONS_DEBUG) << "error parsing" << file << error.errorString();
67  }
68  m_fileName = file;
69  d->metaDataFileName = file;
70  } else {
71  QPluginLoader loader(file);
72  m_fileName = QFileInfo(loader.fileName()).absoluteFilePath();
73  m_metaData = loader.metaData().value(QStringLiteral("MetaData")).toObject();
74  }
75 }
76 
78 {
79  m_fileName = QFileInfo(loader.fileName()).absoluteFilePath();
80  m_metaData = loader.metaData().value(QStringLiteral("MetaData")).toObject();
81 }
82 
84 {
85  m_fileName = QFileInfo(loader.fileName()).absoluteFilePath();
86  m_metaData = loader.metaData().value(QStringLiteral("MetaData")).toObject();
87 }
88 
90 {
91  m_fileName = file;
92  m_metaData = metaData;
93 }
94 
95 KPluginMetaData::KPluginMetaData(const QJsonObject &metaData, const QString &pluginFile, const QString &metaDataFile)
96 {
97  m_fileName = pluginFile;
98  m_metaData = metaData;
99  if (!metaDataFile.isEmpty()) {
100  d = new KPluginMetaDataPrivate;
101  d->metaDataFileName = metaDataFile;
102  }
103 }
104 
106 {
107  KPluginMetaData result;
108  result.loadFromDesktopFile(file, serviceTypes);
109  return result;
110 }
111 
112 void KPluginMetaData::loadFromDesktopFile(const QString &file, const QStringList &serviceTypes)
113 {
114  QString libraryPath;
115  if (!DesktopFileParser::convert(file, serviceTypes, m_metaData, &libraryPath)) {
116  Q_ASSERT(!isValid());
117  return; // file could not be parsed for some reason, leave this object invalid
118  }
119  d = new KPluginMetaDataPrivate;
120  d->metaDataFileName = QFileInfo(file).absoluteFilePath();
121  if (!libraryPath.isEmpty()) {
122  // this was a plugin with a shared library
123  m_fileName = libraryPath;
124  } else {
125  // no library, make filename point to the .desktop file
126  m_fileName = d->metaDataFileName;
127  }
128 }
129 
131 {
132  return m_metaData;
133 }
134 
136 {
137  return m_fileName;
138 }
139 
141 {
142  return d ? d->metaDataFileName : m_fileName;
143 }
144 
145 
146 bool KPluginMetaData::isValid() const
147 {
148  // it can be valid even if m_fileName is empty (as long as the plugin id is set in the metadata)
149  return !pluginId().isEmpty() && !m_metaData.isEmpty();
150 }
151 
152 bool KPluginMetaData::isHidden() const
153 {
154  return rootObject()[QStringLiteral("Hidden")].toBool();
155 }
156 
157 QJsonObject KPluginMetaData::rootObject() const
158 {
159  return m_metaData[QStringLiteral("KPlugin")].toObject();
160 }
161 
163 {
164  const QJsonValue value = obj.value(key);
165  if (value.isUndefined() || value.isObject() || value.isNull()) {
166  return QStringList();
167  } else if (value.isArray()) {
168  return value.toVariant().toStringList();
169  } else {
170  QString asString = value.isString() ? value.toString() : value.toVariant().toString();
171  if (asString.isEmpty()) {
172  return QStringList();
173  }
174  const QString id = obj.value(QStringLiteral("KPlugin")).toObject().value(QStringLiteral("Id")).toString();
175  qCWarning(KCOREADDONS_DEBUG) << "Expected JSON property" << key << "to be a string list."
176  " Treating it as a list with a single entry:" << asString << id.toLatin1().constData();
177  return QStringList(asString);
178  }
179 }
180 
182 {
183  QString languageWithCountry = QLocale().name();
184  auto it = jo.constFind(key + QLatin1Char('[') + languageWithCountry + QLatin1Char(']'));
185  if (it != jo.constEnd()) {
186  return it.value();
187  }
188  const QStringRef language = languageWithCountry.midRef(0, languageWithCountry.indexOf(QLatin1Char('_')));
189  it = jo.constFind(key + QLatin1Char('[') + language + QLatin1Char(']'));
190  if (it != jo.constEnd()) {
191  return it.value();
192  }
193  // no translated value found -> check key
194  it = jo.constFind(key);
195  if (it != jo.constEnd()) {
196  return jo.value(key);
197  }
198  return defaultValue;
199 }
200 
201 QString KPluginMetaData::readTranslatedString(const QJsonObject &jo, const QString &key, const QString &defaultValue)
202 {
203  return readTranslatedValue(jo, key, defaultValue).toString(defaultValue);
204 }
205 
206 static inline void addPersonFromJson(const QJsonObject &obj, QList<KAboutPerson>* out) {
207  KAboutPerson person = KAboutPerson::fromJSON(obj);
208  if (person.name().isEmpty()) {
209  qCWarning(KCOREADDONS_DEBUG) << "Invalid plugin metadata: Attempting to create a KAboutPerson from json without 'Name' property:" << obj;
210  return;
211  }
212  out->append(person);
213 }
214 
215 static QList<KAboutPerson> aboutPersonFromJSON(const QJsonValue &people)
216 {
218  if (people.isObject()) {
219  // single author
220  addPersonFromJson(people.toObject(), &ret);
221  } else if (people.isArray()) {
222  const QJsonArray peopleArray = people.toArray();
223  for (const QJsonValue &val : peopleArray) {
224  if (val.isObject()) {
225  addPersonFromJson(val.toObject(), &ret);
226  }
227  }
228  }
229  return ret;
230 }
231 
233 {
234  return aboutPersonFromJSON(rootObject()[QStringLiteral("Authors")]);
235 }
236 
238 {
239  return aboutPersonFromJSON(rootObject()[QStringLiteral("Translators")]);
240 }
241 
243 {
244  return aboutPersonFromJSON(rootObject()[QStringLiteral("OtherContributors")]);
245 }
246 
248 {
249  return rootObject()[QStringLiteral("Category")].toString();
250 }
251 
253 {
254  return readTranslatedString(rootObject(), QStringLiteral("Description"));
255 }
256 
258 {
259  return rootObject()[QStringLiteral("Icon")].toString();
260 }
261 
263 {
264  return rootObject()[QStringLiteral("License")].toString();
265 }
266 
268 {
269  return KAboutLicense::byKeyword(license()).text();
270 }
271 
273 {
274  return readTranslatedString(rootObject(), QStringLiteral("Name"));
275 }
276 
278 {
279  return readTranslatedString(rootObject(), QStringLiteral("Copyright"));
280 }
281 
283 {
284  return readTranslatedString(rootObject(), QStringLiteral("ExtraInformation"));
285 }
286 
288 {
289  QJsonObject root = rootObject();
290  auto nameFromMetaData = root.constFind(QStringLiteral("Id"));
291  if (nameFromMetaData != root.constEnd()) {
292  const QString id = nameFromMetaData.value().toString();
293  if (!id.isEmpty()) {
294  return id;
295  }
296  }
297  // passing QFileInfo an empty string gives the CWD, which is not what we want
298  if (m_fileName.isEmpty()) {
299  return QString();
300  }
301  return QFileInfo(m_fileName).baseName();
302 }
303 
305 {
306  return rootObject()[QStringLiteral("Version")].toString();
307 }
308 
310 {
311  return rootObject()[QStringLiteral("Website")].toString();
312 }
313 
314 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 79)
316 {
317  return readStringList(rootObject(), QStringLiteral("Dependencies"));
318 }
319 #endif
320 
322 {
323  return readStringList(rootObject(), QStringLiteral("ServiceTypes"));
324 }
325 
327 {
328  return readStringList(rootObject(), QStringLiteral("MimeTypes"));
329 }
330 
331 bool KPluginMetaData::supportsMimeType(const QString &mimeType) const
332 {
333  QMimeDatabase db;
334  const QMimeType mime = db.mimeTypeForName(mimeType);
335  if (!mime.isValid()) {
336  return false;
337  }
338 
339  const QStringList mimes = mimeTypes();
340  auto inherits = [&](const QString &supportedMimeName) {
341  return mime.inherits(supportedMimeName);
342  };
343  return std::find_if(mimes.begin(), mimes.end(), inherits) != mimes.end();
344 }
345 
347 {
348  return readStringList(rootObject(), QStringLiteral("FormFactors"));
349 }
350 
352 {
353  QJsonValue val = rootObject()[QStringLiteral("EnabledByDefault")];
354  if (val.isBool()) {
355  return val.toBool();
356  } else if (val.isString()) {
357  return val.toString() == QLatin1String("true");
358  }
359  return false;
360 }
361 
363 {
364  return rootObject()[QStringLiteral("InitialPreference")].toInt();
365 }
366 
367 QString KPluginMetaData::value(const QString &key, const QString &defaultValue) const
368 {
369  const QJsonValue value = m_metaData.value(key);
370  if (value.isString()) {
371  return value.toString(defaultValue);
372  } else if (value.isArray()) {
373  qCWarning(KCOREADDONS_DEBUG) << "Expected JSON property" << key << "to be a single string."
374  " but it is a stringlist";
375  const QStringList list = value.toVariant().toStringList();
376  if (list.isEmpty()) {
377  return defaultValue;
378  }
379  return list.join(QChar::fromLatin1(','));
380  } else if (value.isBool()) {
381  qCWarning(KCOREADDONS_DEBUG) << "Expected JSON property" << key << "to be a single string."
382  " but it is a bool";
383  return value.toBool() ? QStringLiteral("true") : QStringLiteral("false");
384  }
385  return defaultValue;
386 }
387 
389 {
390  return m_fileName == other.m_fileName && m_metaData == other.m_metaData;
391 }
392 
394 {
395  return QPluginLoader(m_fileName).instance();
396 }
397 
398 template <class T>
399 QVariantList listToVariant(const QList<T>& values)
400 {
401  QVariantList ret;
402  ret.reserve(values.count());
403  for(const auto &license: values) {
405  }
406  return ret;
407 }
408 
409 QVariantList KPluginMetaData::authorsVariant() const
410 {
411  return listToVariant(authors());
412 }
413 
414 QVariantList KPluginMetaData::translatorsVariant() const
415 {
416  return listToVariant(translators());
417 }
418 
419 QVariantList KPluginMetaData::otherContributorsVariant() const
420 {
421  return listToVariant(otherContributors());
422 }
423 
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:60
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:55
static KAboutPerson fromJSON(const QJsonObject &obj)
Creates a KAboutPerson from a JSON object with the following structure:
Definition: kaboutdata.cpp:109
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:397
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 Sat Jan 16 2021 23:04:11 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.