1 /* This file is part of the KDE project
2 Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
3 Copyright (C) 2007 David Faure <faure@kde.org>
4 Copyright (C) 2015 Jarosław Staniek <staniek@kde.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #include "KexiJsonTrader.h"
23 #include "utils.h"
24
25 #include <QDebug>
26 #include <QList>
27 #include <QPluginLoader>
28 #include <QJsonObject>
29 #include <QJsonArray>
30 #include <QDirIterator>
31 #include <QDir>
32 #include <QCoreApplication>
33
34 class Q_DECL_HIDDEN KexiJsonTrader::Private
35 {
36 public:
Private()37 Private() : pluginPathFound(false)
38 {
39 }
40 QString subDir;
41 bool pluginPathFound;
42 QStringList pluginPaths;
43 };
44
45 // ---
46
KexiJsonTrader(const QString & subDir)47 KexiJsonTrader::KexiJsonTrader(const QString& subDir)
48 : d(new Private)
49 {
50 Q_ASSERT(!subDir.isEmpty());
51 Q_ASSERT(!subDir.contains(' '));
52 d->subDir = subDir;
53 }
54
~KexiJsonTrader()55 KexiJsonTrader::~KexiJsonTrader()
56 {
57 delete d;
58 }
59
60 //! @return true if at least one service type from @a serviceTypeNames exists in @a foundServiceTypes
supportsAtLeastServiceType(const QStringList & foundServiceTypes,const QStringList & serviceTypeNames)61 static bool supportsAtLeastServiceType(const QStringList &foundServiceTypes,
62 const QStringList &serviceTypeNames)
63 {
64 foreach(const QString &serviceTypeName, serviceTypeNames) {
65 if (foundServiceTypes.contains(serviceTypeName)) {
66 return true;
67 }
68 }
69 return false;
70 }
71
72 //static
metaDataObjectForPluginLoader(const QPluginLoader & pluginLoader)73 QJsonObject KexiJsonTrader::metaDataObjectForPluginLoader(const QPluginLoader &pluginLoader)
74 {
75 return pluginLoader.metaData().value(QLatin1String("MetaData")).toObject();
76 }
77
78 //static
rootObjectForPluginLoader(const QPluginLoader & pluginLoader)79 QJsonObject KexiJsonTrader::rootObjectForPluginLoader(const QPluginLoader &pluginLoader)
80 {
81 QJsonObject json = metaDataObjectForPluginLoader(pluginLoader);
82 if (json.isEmpty()) {
83 return QJsonObject();
84 }
85 return json.value(QLatin1String("KPlugin")).toObject();
86 }
87
88 //! Checks loader @a loader
checkLoader(QPluginLoader * loader,const QStringList & servicetypes,const QString & mimetype)89 static bool checkLoader(QPluginLoader *loader, const QStringList &servicetypes,
90 const QString &mimetype)
91 {
92 const QJsonObject pluginData = KexiJsonTrader::rootObjectForPluginLoader(*loader);
93 if (pluginData.isEmpty()) {
94 //qDebug() << dirIter.filePath() << "has no json!";
95 return false;
96 }
97 const QJsonArray foundServiceTypesAray = pluginData.value(QLatin1String("ServiceTypes")).toArray();
98 if (foundServiceTypesAray.isEmpty()) {
99 qWarning() << "No ServiceTypes defined for plugin" << loader->fileName() << "-- skipping!";
100 return false;
101 }
102 QStringList foundServiceTypes = KexiUtils::convertTypesUsingMethod<QVariant, QString, &QVariant::toString>(foundServiceTypesAray.toVariantList());
103 if (!supportsAtLeastServiceType(foundServiceTypes, servicetypes)) {
104 return false;
105 }
106
107 if (!mimetype.isEmpty()) {
108 QJsonObject json = KexiJsonTrader::metaDataObjectForPluginLoader(*loader);
109 QStringList mimeTypes = json.value(QLatin1String("X-KDE-ExtraNativeMimeTypes"))
110 .toString().split(QLatin1Char(','));
111 mimeTypes += json.value(QLatin1String("MimeType")).toString().split(QLatin1Char(';'));
112 mimeTypes += json.value(QLatin1String("X-KDE-NativeMimeType")).toString();
113 if (! mimeTypes.contains(mimetype)) {
114 return false;
115 }
116 }
117 return true;
118 }
119
findPlugins(const QString & path,const QStringList & servicetypes,const QString & mimetype)120 static QList<QPluginLoader *> findPlugins(const QString &path, const QStringList &servicetypes,
121 const QString &mimetype)
122 {
123 QList<QPluginLoader*> list;
124 QDirIterator dirIter(path,
125 /* QDirIterator::Subdirectories -- Since 3.0.1: Don't look into subdirs
126 because there may be 3.x dirs from
127 future Kexi versions. We will look
128 into subdirs since 3.1 again. */
129 QDirIterator::FollowSymlinks);
130 while (dirIter.hasNext()) {
131 dirIter.next();
132 if (dirIter.fileInfo().isFile()) {
133 QPluginLoader *loader = new QPluginLoader(dirIter.filePath());
134 if (checkLoader(loader, servicetypes, mimetype)) {
135 list.append(loader);
136 } else {
137 delete loader;
138 }
139 }
140 }
141 return list;
142 }
143
query(const QStringList & servicetypes,const QString & mimetype)144 QList<QPluginLoader *> KexiJsonTrader::query(const QStringList &servicetypes,
145 const QString &mimetype)
146 {
147 if (!d->pluginPathFound) {
148 QStringList searchDirs;
149 searchDirs += QCoreApplication::libraryPaths();
150 foreach(const QString &dir, searchDirs) {
151 //qDebug() << dir;
152 QString possiblePath = dir + QLatin1Char('/') + d->subDir;
153 if (QDir(possiblePath).exists()) {
154 d->pluginPaths += possiblePath;
155 }
156 }
157 d->pluginPathFound = true;
158 }
159
160 QList<QPluginLoader *> list;
161 foreach(const QString &path, d->pluginPaths) {
162 list += findPlugins(path, servicetypes, mimetype);
163 }
164 return list;
165 }
166
query(const QString & servicetype,const QString & mimetype)167 QList<QPluginLoader *> KexiJsonTrader::query(const QString &servicetype, const QString &mimetype)
168 {
169 QStringList servicetypes;
170 servicetypes << servicetype;
171 return query(servicetypes, mimetype);
172 }
173