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 <QStringList>
19 #include "kcoreaddons_debug.h"
20 
21 #include "kpluginloader.h"
22 #include "kaboutdata.h"
23 
24 class KPluginMetaDataPrivate : public QSharedData
25 {
26 public:
27  QString metaDataFileName;
28 };
29 
31 {
32 }
33 
35  : m_metaData(other.m_metaData), m_fileName(other.fileName()), d(other.d)
36 {
37 }
38 
40 {
41  m_metaData = other.m_metaData;
42  m_fileName = other.m_fileName;
43  d = other.d;
44  return *this;
45 }
46 
48 {
49 }
50 
52 {
53  if (file.endsWith(QLatin1String(".desktop"))) {
54  loadFromDesktopFile(file, QStringList());
55  } else if (file.endsWith(QLatin1String(".json"))) {
56  d = new KPluginMetaDataPrivate;
57  QFile f(file);
58  bool b = f.open(QIODevice::ReadOnly);
59  if (!b) {
60  qCWarning(KCOREADDONS_DEBUG) << "Couldn't open" << file;
61  return;
62  }
63 
64  QJsonParseError error;
65  m_metaData = QJsonDocument::fromJson(f.readAll(), &error).object();
66  if (error.error) {
67  qCWarning(KCOREADDONS_DEBUG) << "error parsing" << file << error.errorString();
68  }
69  m_fileName = file;
70  d->metaDataFileName = file;
71  } else {
72  QPluginLoader loader(file);
73  m_fileName = QFileInfo(loader.fileName()).absoluteFilePath();
74  m_metaData = loader.metaData().value(QStringLiteral("MetaData")).toObject();
75  }
76 }
77 
79 {
80  m_fileName = QFileInfo(loader.fileName()).absoluteFilePath();
81  m_metaData = loader.metaData().value(QStringLiteral("MetaData")).toObject();
82 }
83 
85 {
86  m_fileName = QFileInfo(loader.fileName()).absoluteFilePath();
87  m_metaData = loader.metaData().value(QStringLiteral("MetaData")).toObject();
88 }
89 
91 {
92  m_fileName = file;
93  m_metaData = metaData;
94 }
95 
96 KPluginMetaData::KPluginMetaData(const QJsonObject &metaData, const QString &pluginFile, const QString &metaDataFile)
97 {
98  m_fileName = pluginFile;
99  m_metaData = metaData;
100  if (!metaDataFile.isEmpty()) {
101  d = new KPluginMetaDataPrivate;
102  d->metaDataFileName = metaDataFile;
103  }
104 }
105 
107 {
108  KPluginMetaData result;
109  result.loadFromDesktopFile(file, serviceTypes);
110  return result;
111 }
112 
113 void KPluginMetaData::loadFromDesktopFile(const QString &file, const QStringList &serviceTypes)
114 {
115  QString libraryPath;
116  if (!DesktopFileParser::convert(file, serviceTypes, m_metaData, &libraryPath)) {
117  Q_ASSERT(!isValid());
118  return; // file could not be parsed for some reason, leave this object invalid
119  }
120  d = new KPluginMetaDataPrivate;
121  d->metaDataFileName = QFileInfo(file).absoluteFilePath();
122  if (!libraryPath.isEmpty()) {
123  // this was a plugin with a shared library
124  m_fileName = libraryPath;
125  } else {
126  // no library, make filename point to the .desktop file
127  m_fileName = d->metaDataFileName;
128  }
129 }
130 
132 {
133  return m_metaData;
134 }
135 
137 {
138  return m_fileName;
139 }
140 
142 {
143  return d ? d->metaDataFileName : m_fileName;
144 }
145 
146 
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 
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 << "to be a string list."
177  " Treating it as a list with a single entry:" << asString << id.toLatin1().constData();
178  return QStringList(asString);
179  }
180 }
181 
183 {
184  QString languageWithCountry = QLocale().name();
185  auto it = jo.constFind(key + QLatin1Char('[') + languageWithCountry + QLatin1Char(']'));
186  if (it != jo.constEnd()) {
187  return it.value();
188  }
189  const QStringRef language = languageWithCountry.midRef(0, languageWithCountry.indexOf(QLatin1Char('_')));
190  it = jo.constFind(key + QLatin1Char('[') + language + QLatin1Char(']'));
191  if (it != jo.constEnd()) {
192  return it.value();
193  }
194  // no translated value found -> check key
195  it = jo.constFind(key);
196  if (it != jo.constEnd()) {
197  return jo.value(key);
198  }
199  return defaultValue;
200 }
201 
202 QString KPluginMetaData::readTranslatedString(const QJsonObject &jo, const QString &key, const QString &defaultValue)
203 {
204  return readTranslatedValue(jo, key, defaultValue).toString(defaultValue);
205 }
206 
207 static inline void addPersonFromJson(const QJsonObject &obj, QList<KAboutPerson>* out) {
208  KAboutPerson person = KAboutPerson::fromJSON(obj);
209  if (person.name().isEmpty()) {
210  qCWarning(KCOREADDONS_DEBUG) << "Invalid plugin metadata: Attempting to create a KAboutPerson from json without 'Name' property:" << obj;
211  return;
212  }
213  out->append(person);
214 }
215 
216 static QList<KAboutPerson> aboutPersonFromJSON(const QJsonValue &people)
217 {
219  if (people.isObject()) {
220  // single author
221  addPersonFromJson(people.toObject(), &ret);
222  } else if (people.isArray()) {
223  const QJsonArray peopleArray = people.toArray();
224  for (const QJsonValue &val : peopleArray) {
225  if (val.isObject()) {
226  addPersonFromJson(val.toObject(), &ret);
227  }
228  }
229  }
230  return ret;
231 }
232 
234 {
235  return aboutPersonFromJSON(rootObject()[QStringLiteral("Authors")]);
236 }
237 
239 {
240  return aboutPersonFromJSON(rootObject()[QStringLiteral("Translators")]);
241 }
242 
244 {
245  return aboutPersonFromJSON(rootObject()[QStringLiteral("OtherContributors")]);
246 }
247 
249 {
250  return rootObject()[QStringLiteral("Category")].toString();
251 }
252 
254 {
255  return readTranslatedString(rootObject(), QStringLiteral("Description"));
256 }
257 
259 {
260  return rootObject()[QStringLiteral("Icon")].toString();
261 }
262 
264 {
265  return rootObject()[QStringLiteral("License")].toString();
266 }
267 
269 {
270  return readTranslatedString(rootObject(), QStringLiteral("Name"));
271 }
272 
274 {
275  return readTranslatedString(rootObject(), QStringLiteral("Copyright"));
276 }
277 
279 {
280  return readTranslatedString(rootObject(), QStringLiteral("ExtraInformation"));
281 }
282 
284 {
285  QJsonObject root = rootObject();
286  auto nameFromMetaData = root.constFind(QStringLiteral("Id"));
287  if (nameFromMetaData != root.constEnd()) {
288  const QString id = nameFromMetaData.value().toString();
289  if (!id.isEmpty()) {
290  return id;
291  }
292  }
293  // passing QFileInfo an empty string gives the CWD, which is not what we want
294  if (m_fileName.isEmpty()) {
295  return QString();
296  }
297  return QFileInfo(m_fileName).baseName();
298 }
299 
301 {
302  return rootObject()[QStringLiteral("Version")].toString();
303 }
304 
306 {
307  return rootObject()[QStringLiteral("Website")].toString();
308 }
309 
311 {
312  return readStringList(rootObject(), QStringLiteral("Dependencies"));
313 }
314 
316 {
317  return readStringList(rootObject(), QStringLiteral("ServiceTypes"));
318 }
319 
321 {
322  return readStringList(rootObject(), QStringLiteral("MimeTypes"));
323 }
324 
325 bool KPluginMetaData::supportsMimeType(const QString &mimeType) const
326 {
327  QMimeDatabase db;
328  const QMimeType mime = db.mimeTypeForName(mimeType);
329  if (!mime.isValid()) {
330  return false;
331  }
332 
333  const QStringList mimes = mimeTypes();
334  auto inherits = [&](const QString &supportedMimeName) {
335  return mime.inherits(supportedMimeName);
336  };
337  return std::find_if(mimes.begin(), mimes.end(), inherits) != mimes.end();
338 }
339 
341 {
342  return readStringList(rootObject(), QStringLiteral("FormFactors"));
343 }
344 
346 {
347  QJsonValue val = rootObject()[QStringLiteral("EnabledByDefault")];
348  if (val.isBool()) {
349  return val.toBool();
350  } else if (val.isString()) {
351  return val.toString() == QLatin1String("true");
352  }
353  return false;
354 }
355 
357 {
358  return rootObject()[QStringLiteral("InitialPreference")].toInt();
359 }
360 
361 QString KPluginMetaData::value(const QString &key, const QString &defaultValue) const
362 {
363  const QJsonValue value = m_metaData.value(key);
364  if (value.isString()) {
365  return value.toString(defaultValue);
366  } else if (value.isArray()) {
367  qCWarning(KCOREADDONS_DEBUG) << "Expected JSON property" << key << "to be a single string."
368  " but it is a stringlist";
369  const QStringList list = value.toVariant().toStringList();
370  if (list.isEmpty()) {
371  return defaultValue;
372  }
373  return list.join(QChar::fromLatin1(','));
374  } else if (value.isBool()) {
375  qCWarning(KCOREADDONS_DEBUG) << "Expected JSON property" << key << "to be a single string."
376  " but it is a bool";
377  return value.toBool() ? QStringLiteral("true") : QStringLiteral("false");
378  }
379  return defaultValue;
380 }
381 
383 {
384  return m_fileName == other.m_fileName && m_metaData == other.m_metaData;
385 }
386 
388 {
389  return QPluginLoader(m_fileName).instance();
390 }
391 
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
QString fileName() const
int initialPreference() const
QString website() const
QString name() const
QStringList formFactors() const
QList< KAboutPerson > translators() 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
QList< KAboutPerson > otherContributors() const
bool isUndefined() const const
bool isEmpty() 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
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
QList< KAboutPerson > authors() 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
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
QByteArray toLatin1() const const
QStringList toStringList() const const
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-2020 The KDE developers.
Generated on Mon Jul 6 2020 23:01:20 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.