Marble

PluginManager.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2008 Torsten Rahn <tackat@kde.org>
4// SPDX-FileCopyrightText: 2009 Jens-Michael Hoffmann <jensmh@gmx.de>
5//
6
7// Own
8#include "PluginManager.h"
9
10// Qt
11#include <QElapsedTimer>
12#include <QMessageBox>
13#include <QPluginLoader>
14
15// Local dir
16#include "MarbleDebug.h"
17#include "MarbleDirs.h"
18#include "ParseRunnerPlugin.h"
19#include "PositionProviderPlugin.h"
20#include "RenderPlugin.h"
21#include "ReverseGeocodingRunnerPlugin.h"
22#include "RoutingRunnerPlugin.h"
23#include "SearchRunnerPlugin.h"
24#include <config-marble.h>
25
26namespace Marble
27{
28
29class PluginManagerPrivate
30{
31public:
32 PluginManagerPrivate(PluginManager *parent)
33 : m_pluginsLoaded(false)
34 , m_parent(parent)
35 {
36 }
37
38 ~PluginManagerPrivate();
39
40 void loadPlugins();
41 bool addPlugin(QObject *obj, const QPluginLoader *loader);
42
43 bool m_pluginsLoaded;
44 QList<const RenderPlugin *> m_renderPluginTemplates;
45 QList<const PositionProviderPlugin *> m_positionProviderPluginTemplates;
46 QList<const SearchRunnerPlugin *> m_searchRunnerPlugins;
47 QList<const ReverseGeocodingRunnerPlugin *> m_reverseGeocodingRunnerPlugins;
48 QList<RoutingRunnerPlugin *> m_routingRunnerPlugins;
49 QList<const ParseRunnerPlugin *> m_parsingRunnerPlugins;
50 PluginManager *m_parent;
51 static QStringList m_blacklist;
52 static QStringList m_whitelist;
53
54#ifdef Q_OS_ANDROID
55 QStringList m_pluginPaths;
56#endif
57};
58
59QStringList PluginManagerPrivate::m_blacklist;
60QStringList PluginManagerPrivate::m_whitelist;
61
62PluginManagerPrivate::~PluginManagerPrivate()
63{
64 // nothing to do
65}
66
67PluginManager::PluginManager(QObject *parent)
68 : QObject(parent)
69 , d(new PluginManagerPrivate(this))
70{
71 // Checking assets:/plugins for uninstalled plugins
72#ifdef Q_OS_ANDROID
73 installPluginsFromAssets();
74#endif
75}
76
77PluginManager::~PluginManager()
78{
79 delete d;
80}
81
82QList<const RenderPlugin *> PluginManager::renderPlugins() const
83{
84 d->loadPlugins();
85 return d->m_renderPluginTemplates;
86}
87
88void PluginManager::addRenderPlugin(const RenderPlugin *plugin)
89{
90 d->loadPlugins();
91 d->m_renderPluginTemplates << plugin;
92 Q_EMIT renderPluginsChanged();
93}
94
95QList<const PositionProviderPlugin *> PluginManager::positionProviderPlugins() const
96{
97 d->loadPlugins();
98 return d->m_positionProviderPluginTemplates;
99}
100
101void PluginManager::addPositionProviderPlugin(const PositionProviderPlugin *plugin)
102{
103 d->loadPlugins();
104 d->m_positionProviderPluginTemplates << plugin;
105 Q_EMIT positionProviderPluginsChanged();
106}
107
108QList<const SearchRunnerPlugin *> PluginManager::searchRunnerPlugins() const
109{
110 d->loadPlugins();
111 return d->m_searchRunnerPlugins;
112}
113
114void PluginManager::addSearchRunnerPlugin(const SearchRunnerPlugin *plugin)
115{
116 d->loadPlugins();
117 d->m_searchRunnerPlugins << plugin;
118 Q_EMIT searchRunnerPluginsChanged();
119}
120
121QList<const ReverseGeocodingRunnerPlugin *> PluginManager::reverseGeocodingRunnerPlugins() const
122{
123 d->loadPlugins();
124 return d->m_reverseGeocodingRunnerPlugins;
125}
126
127void PluginManager::addReverseGeocodingRunnerPlugin(const ReverseGeocodingRunnerPlugin *plugin)
128{
129 d->loadPlugins();
130 d->m_reverseGeocodingRunnerPlugins << plugin;
131 Q_EMIT reverseGeocodingRunnerPluginsChanged();
132}
133
134QList<RoutingRunnerPlugin *> PluginManager::routingRunnerPlugins() const
135{
136 d->loadPlugins();
137 return d->m_routingRunnerPlugins;
138}
139
140void PluginManager::addRoutingRunnerPlugin(RoutingRunnerPlugin *plugin)
141{
142 d->loadPlugins();
143 d->m_routingRunnerPlugins << plugin;
144 Q_EMIT routingRunnerPluginsChanged();
145}
146
147QList<const ParseRunnerPlugin *> PluginManager::parsingRunnerPlugins() const
148{
149 d->loadPlugins();
150 return d->m_parsingRunnerPlugins;
151}
152
153void PluginManager::addParseRunnerPlugin(const ParseRunnerPlugin *plugin)
154{
155 d->loadPlugins();
156 d->m_parsingRunnerPlugins << plugin;
157 Q_EMIT parseRunnerPluginsChanged();
158}
159
160void PluginManager::blacklistPlugin(const QString &filename)
161{
162 PluginManagerPrivate::m_blacklist << QString::fromLatin1(MARBLE_SHARED_LIBRARY_PREFIX) + filename;
163}
164
165void PluginManager::whitelistPlugin(const QString &filename)
166{
167 PluginManagerPrivate::m_whitelist << QString::fromLatin1(MARBLE_SHARED_LIBRARY_PREFIX) + filename;
168}
169
170/** Append obj to the given plugins list if it inherits both T and U */
171template<class Iface, class Plugin>
172bool appendPlugin(QObject *obj, const QPluginLoader *loader, QList<Plugin> &plugins)
173{
174 if (qobject_cast<Iface *>(obj) && qobject_cast<Plugin>(obj)) {
175 Q_ASSERT(obj->metaObject()->superClass()); // all our plugins have a super class
176 mDebug() << obj->metaObject()->superClass()->className() << "plugin loaded from" << (loader ? loader->fileName() : QStringLiteral("<static>"));
177 auto plugin = qobject_cast<Plugin>(obj);
178 Q_ASSERT(plugin); // checked above
179 plugins << plugin;
180 return true;
181 }
182
183 return false;
184}
185
186bool PluginManagerPrivate::addPlugin(QObject *obj, const QPluginLoader *loader)
187{
188 bool isPlugin = appendPlugin<RenderPluginInterface>(obj, loader, m_renderPluginTemplates);
189 isPlugin = isPlugin || appendPlugin<PositionProviderPluginInterface>(obj, loader, m_positionProviderPluginTemplates);
190 isPlugin = isPlugin || appendPlugin<SearchRunnerPlugin>(obj, loader, m_searchRunnerPlugins);
191 isPlugin = isPlugin || appendPlugin<ReverseGeocodingRunnerPlugin>(obj, loader, m_reverseGeocodingRunnerPlugins);
192 isPlugin = isPlugin || appendPlugin<RoutingRunnerPlugin>(obj, loader, m_routingRunnerPlugins);
193 isPlugin = isPlugin || appendPlugin<ParseRunnerPlugin>(obj, loader, m_parsingRunnerPlugins);
194 if (!isPlugin) {
195 qWarning() << "Ignoring the following plugin since it couldn't be loaded:" << (loader ? loader->fileName() : QStringLiteral("<static>"));
196 mDebug() << "Plugin failure:" << (loader ? loader->fileName() : QStringLiteral("<static>")) << "is a plugin, but it does not implement the "
197 << "right interfaces or it was compiled against an old version of Marble. Ignoring it.";
198 }
199 return isPlugin;
200}
201
202void PluginManagerPrivate::loadPlugins()
203{
204 if (m_pluginsLoaded) {
205 return;
206 }
207
209 t.start();
210 mDebug() << "Starting to load Plugins.";
211
212 QStringList pluginFileNameList = MarbleDirs::pluginEntryList(QString(), QDir::Files);
213
214 MarbleDirs::debug();
215
216 Q_ASSERT(m_renderPluginTemplates.isEmpty());
217 Q_ASSERT(m_positionProviderPluginTemplates.isEmpty());
218 Q_ASSERT(m_searchRunnerPlugins.isEmpty());
219 Q_ASSERT(m_reverseGeocodingRunnerPlugins.isEmpty());
220 Q_ASSERT(m_routingRunnerPlugins.isEmpty());
221 Q_ASSERT(m_parsingRunnerPlugins.isEmpty());
222
223 bool foundPlugin = false;
224 for (const QString &fileName : pluginFileNameList) {
225 QString const baseName = QFileInfo(fileName).baseName();
226 QString const libBaseName = QString::fromLatin1(MARBLE_SHARED_LIBRARY_PREFIX) + QFileInfo(fileName).baseName();
227 if (!m_whitelist.isEmpty() && !m_whitelist.contains(baseName) && !m_whitelist.contains(libBaseName)) {
228 mDebug() << "Ignoring non-whitelisted plugin " << fileName;
229 continue;
230 }
231 if (m_blacklist.contains(baseName) || m_blacklist.contains(libBaseName)) {
232 mDebug() << "Ignoring blacklisted plugin " << fileName;
233 continue;
234 }
235
236 // mDebug() << fileName << " - " << MarbleDirs::pluginPath( fileName );
237 QString const path = MarbleDirs::pluginPath(fileName);
238#ifdef Q_OS_ANDROID
239 QFileInfo targetFile(path);
240 if (!m_pluginPaths.contains(targetFile.canonicalFilePath())) {
241 // @todo Delete the file here?
242 qDebug() << "Ignoring file " << path << " which is not among the currently installed plugins";
243 continue;
244 }
245#endif
246 auto loader = new QPluginLoader(path, m_parent);
247
248 QObject *obj = loader->instance();
249
250 if (obj) {
251 bool isPlugin = addPlugin(obj, loader);
252 if (!isPlugin) {
253 delete loader;
254 } else {
255 foundPlugin = true;
256 }
257 } else {
258 qWarning() << "Ignoring to load the following file since it doesn't look like a valid Marble plugin:" << path << Qt::endl
259 << "Reason:" << loader->errorString();
260 delete loader;
261 }
262 }
263
264 const auto staticPlugins = QPluginLoader::staticInstances();
265 for (auto obj : staticPlugins) {
266 if (addPlugin(obj, nullptr)) {
267 foundPlugin = true;
268 }
269 }
270
271 if (!foundPlugin) {
272#ifdef Q_OS_WIN
273 QString pluginPaths = "Plugin Path: " + MarbleDirs::marblePluginPath();
274 if (MarbleDirs::marblePluginPath().isEmpty())
275 pluginPaths = "";
276 pluginPaths += "System Path: " + MarbleDirs::pluginSystemPath() + "\nLocal Path: " + MarbleDirs::pluginLocalPath();
277
278 QMessageBox::warning(nullptr,
279 "No plugins loaded",
280 "No plugins were loaded, please check if the plugins were installed in one of the following paths:\n" + pluginPaths
281 + "\n\nAlso check if the plugin is compiled against the right version of Marble. "
282 + "Analyzing the debug messages inside a debugger might give more insight.");
283#else
284 qWarning() << "No plugins loaded. Please check if the plugins were installed in the correct path,"
285 << "or if any errors occurred while loading plugins.";
286#endif
287 }
288
289 m_pluginsLoaded = true;
290
291 mDebug() << "Time elapsed:" << t.elapsed() << "ms";
292}
293
294#ifdef Q_OS_ANDROID
295void PluginManager::installPluginsFromAssets() const
296{
297 d->m_pluginPaths.clear();
298 QStringList copyList = MarbleDirs::pluginEntryList(QString());
299 QDir pluginHome(MarbleDirs::localPath());
300 pluginHome.mkpath(MarbleDirs::pluginLocalPath());
301 pluginHome.setCurrent(MarbleDirs::pluginLocalPath());
302
303 QStringList pluginNameFilter = QStringList() << "lib*.so";
304 QStringList const existingPlugins = QDir(MarbleDirs::pluginLocalPath()).entryList(pluginNameFilter, QDir::Files);
305 for (const QString &existingPlugin : existingPlugins) {
306 QFile::remove(existingPlugin);
307 }
308
309 for (const QString &file : copyList) {
310 QString const target = MarbleDirs::pluginLocalPath() + QLatin1Char('/') + file;
311 if (QFileInfo(MarbleDirs::pluginSystemPath() + QLatin1Char('/') + file).isDir()) {
312 pluginHome.mkpath(target);
313 } else {
314 QFile temporaryFile(MarbleDirs::pluginSystemPath() + QLatin1Char('/') + file);
315 temporaryFile.copy(target);
316 QFileInfo targetFile(target);
317 d->m_pluginPaths << targetFile.canonicalFilePath();
318 }
319 }
320}
321#endif
322
323}
324
325#include "moc_PluginManager.cpp"
A plugin for Marble to execute a parsing task.
The abstract class that provides position information.
The abstract class that creates a renderable item.
A plugin for Marble to execute a reverse geocoding task.
A plugin for Marble to execute a routing task.
A plugin for Marble to execute a placemark search.
QString path(const QString &relativePath)
Binds a QML item to a specific geodetic location in screen coordinates.
bool appendPlugin(QObject *obj, const QPluginLoader *loader, QList< Plugin > &plugins)
Append obj to the given plugins list if it inherits both T and U.
QCA_EXPORT QStringList pluginPaths()
QStringList entryList(Filters filters, SortFlags sort) const const
qint64 elapsed() const const
bool remove()
QString baseName() const const
StandardButton warning(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons, StandardButton defaultButton)
const char * className() const const
const QMetaObject * superClass() const const
virtual const QMetaObject * metaObject() const const
QString errorString() const const
QObject * instance()
QObjectList staticInstances()
QString fromLatin1(QByteArrayView str)
QTextStream & endl(QTextStream &stream)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 8 2024 12:02:44 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.