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
8// Own
9#include "PluginManager.h"
10
11// Qt
12#include <QPluginLoader>
13#include <QElapsedTimer>
14#include <QMessageBox>
15
16// Local dir
17#include "MarbleDirs.h"
18#include "MarbleDebug.h"
19#include "RenderPlugin.h"
20#include "PositionProviderPlugin.h"
21#include "ParseRunnerPlugin.h"
22#include "ReverseGeocodingRunnerPlugin.h"
23#include "RoutingRunnerPlugin.h"
24#include "SearchRunnerPlugin.h"
25#include <config-marble.h>
26
27namespace Marble
28{
29
30class PluginManagerPrivate
31{
32 public:
33 PluginManagerPrivate(PluginManager* parent)
34 : m_pluginsLoaded(false),
35 m_parent(parent)
36 {
37 }
38
39 ~PluginManagerPrivate();
40
41 void loadPlugins();
42 bool addPlugin(QObject *obj, const QPluginLoader *loader);
43
44 bool m_pluginsLoaded;
45 QList<const RenderPlugin *> m_renderPluginTemplates;
46 QList<const PositionProviderPlugin *> m_positionProviderPluginTemplates;
47 QList<const SearchRunnerPlugin *> m_searchRunnerPlugins;
48 QList<const ReverseGeocodingRunnerPlugin *> m_reverseGeocodingRunnerPlugins;
49 QList<RoutingRunnerPlugin *> m_routingRunnerPlugins;
50 QList<const ParseRunnerPlugin *> m_parsingRunnerPlugins;
51 PluginManager* m_parent;
52 static QStringList m_blacklist;
53 static QStringList m_whitelist;
54
55#ifdef Q_OS_ANDROID
56 QStringList m_pluginPaths;
57#endif
58};
59
60QStringList PluginManagerPrivate::m_blacklist;
61QStringList PluginManagerPrivate::m_whitelist;
62
63PluginManagerPrivate::~PluginManagerPrivate()
64{
65 // nothing to do
66}
67
68PluginManager::PluginManager( QObject *parent ) : 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 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 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 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 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 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 emit parseRunnerPluginsChanged();
158}
159
160void PluginManager::blacklistPlugin(const QString &filename)
161{
162 PluginManagerPrivate::m_blacklist << MARBLE_SHARED_LIBRARY_PREFIX + filename;
163}
164
165void PluginManager::whitelistPlugin(const QString &filename)
166{
167 PluginManagerPrivate::m_whitelist << 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()
177 << "plugin loaded from" << (loader ? loader->fileName() : "<static>");
178 auto plugin = qobject_cast<Plugin>( obj );
179 Q_ASSERT( plugin ); // checked above
180 plugins << plugin;
181 return true;
182 }
183
184 return false;
185}
186
187bool PluginManagerPrivate::addPlugin(QObject *obj, const QPluginLoader *loader)
188{
189 bool isPlugin = appendPlugin<RenderPluginInterface>
190 ( obj, loader, m_renderPluginTemplates );
191 isPlugin = isPlugin || appendPlugin<PositionProviderPluginInterface>
192 ( obj, loader, m_positionProviderPluginTemplates );
193 isPlugin = isPlugin || appendPlugin<SearchRunnerPlugin>
194 ( obj, loader, m_searchRunnerPlugins );
195 isPlugin = isPlugin || appendPlugin<ReverseGeocodingRunnerPlugin>
196 ( obj, loader, m_reverseGeocodingRunnerPlugins );
197 isPlugin = isPlugin || appendPlugin<RoutingRunnerPlugin>
198 ( obj, loader, m_routingRunnerPlugins );
199 isPlugin = isPlugin || appendPlugin<ParseRunnerPlugin>
200 ( obj, loader, m_parsingRunnerPlugins );
201 if ( !isPlugin ) {
202 qWarning() << "Ignoring the following plugin since it couldn't be loaded:" << (loader ? loader->fileName() : "<static>");
203 mDebug() << "Plugin failure:" << (loader ? loader->fileName() : "<static>") << "is a plugin, but it does not implement the "
204 << "right interfaces or it was compiled against an old version of Marble. Ignoring it.";
205 }
206 return isPlugin;
207}
208
209void PluginManagerPrivate::loadPlugins()
210{
211 if (m_pluginsLoaded)
212 {
213 return;
214 }
215
217 t.start();
218 mDebug() << "Starting to load Plugins.";
219
220 QStringList pluginFileNameList = MarbleDirs::pluginEntryList( "", QDir::Files );
221
222 MarbleDirs::debug();
223
224 Q_ASSERT( m_renderPluginTemplates.isEmpty() );
225 Q_ASSERT( m_positionProviderPluginTemplates.isEmpty() );
226 Q_ASSERT( m_searchRunnerPlugins.isEmpty() );
227 Q_ASSERT( m_reverseGeocodingRunnerPlugins.isEmpty() );
228 Q_ASSERT( m_routingRunnerPlugins.isEmpty() );
229 Q_ASSERT( m_parsingRunnerPlugins.isEmpty() );
230
231 bool foundPlugin = false;
232 for( const QString &fileName: pluginFileNameList ) {
233 QString const baseName = QFileInfo(fileName).baseName();
234 QString const libBaseName = MARBLE_SHARED_LIBRARY_PREFIX + QFileInfo(fileName).baseName();
235 if (!m_whitelist.isEmpty() && !m_whitelist.contains(baseName) && !m_whitelist.contains(libBaseName)) {
236 mDebug() << "Ignoring non-whitelisted plugin " << fileName;
237 continue;
238 }
239 if (m_blacklist.contains(baseName) || m_blacklist.contains(libBaseName)) {
240 mDebug() << "Ignoring blacklisted plugin " << fileName;
241 continue;
242 }
243
244 // mDebug() << fileName << " - " << MarbleDirs::pluginPath( fileName );
245 QString const path = MarbleDirs::pluginPath( fileName );
246#ifdef Q_OS_ANDROID
247 QFileInfo targetFile( path );
248 if ( !m_pluginPaths.contains( targetFile.canonicalFilePath() ) ) {
249 // @todo Delete the file here?
250 qDebug() << "Ignoring file " << path << " which is not among the currently installed plugins";
251 continue;
252 }
253#endif
254 QPluginLoader* loader = new QPluginLoader( path, m_parent );
255
256 QObject * obj = loader->instance();
257
258 if ( obj ) {
259 bool isPlugin = addPlugin(obj, loader);
260 if (!isPlugin) {
261 delete loader;
262 } else {
263 foundPlugin = true;
264 }
265 } else {
266 qWarning() << "Ignoring to load the following file since it doesn't look like a valid Marble plugin:" << path << endl
267 << "Reason:" << loader->errorString();
268 delete loader;
269 }
270 }
271
272 const auto staticPlugins = QPluginLoader::staticInstances();
273 for (auto obj : staticPlugins) {
274 if (addPlugin(obj, nullptr)) {
275 foundPlugin = true;
276 }
277 }
278
279 if ( !foundPlugin ) {
280#ifdef Q_OS_WIN
281 QString pluginPaths = "Plugin Path: " + MarbleDirs::marblePluginPath();
282 if ( MarbleDirs::marblePluginPath().isEmpty() )
283 pluginPaths = "";
284 pluginPaths += "System Path: " + MarbleDirs::pluginSystemPath() + "\nLocal Path: " + MarbleDirs::pluginLocalPath();
285
286 QMessageBox::warning( nullptr,
287 "No plugins loaded",
288 "No plugins were loaded, please check if the plugins were installed in one of the following paths:\n" + pluginPaths
289 + "\n\nAlso check if the plugin is compiled against the right version of Marble. " +
290 "Analyzing the debug messages inside a debugger might give more insight." );
291#else
292 qWarning() << "No plugins loaded. Please check if the plugins were installed in the correct path,"
293 << "or if any errors occurred while loading plugins.";
294#endif
295 }
296
297 m_pluginsLoaded = true;
298
299 mDebug() << "Time elapsed:" << t.elapsed() << "ms";
300}
301
302#ifdef Q_OS_ANDROID
303 void PluginManager::installPluginsFromAssets() const
304 {
305 d->m_pluginPaths.clear();
306 QStringList copyList = MarbleDirs::pluginEntryList(QString());
307 QDir pluginHome(MarbleDirs::localPath());
308 pluginHome.mkpath(MarbleDirs::pluginLocalPath());
309 pluginHome.setCurrent(MarbleDirs::pluginLocalPath());
310
311 QStringList pluginNameFilter = QStringList() << "lib*.so";
312 QStringList const existingPlugins = QDir(MarbleDirs::pluginLocalPath()).entryList(pluginNameFilter, QDir::Files);
313 for(const QString &existingPlugin: existingPlugins) {
314 QFile::remove(existingPlugin);
315 }
316
317 for (const QString & file: copyList) {
318 QString const target = MarbleDirs::pluginLocalPath() + QLatin1Char('/') + file;
319 if (QFileInfo(MarbleDirs::pluginSystemPath() + QLatin1Char('/') + file).isDir()) {
320 pluginHome.mkpath(target);
321 }
322 else {
323 QFile temporaryFile(MarbleDirs::pluginSystemPath() + QLatin1Char('/') + file);
324 temporaryFile.copy(target);
325 QFileInfo targetFile(target);
326 d->m_pluginPaths << targetFile.canonicalFilePath();
327 }
328 }
329 }
330#endif
331
332}
333
334#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()
QTextStream & endl(QTextStream &stream)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 21 2024 12:00:07 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.