PluginManager.cpp 10.5 KB
Newer Older
Torsten Rahn's avatar
Torsten Rahn committed
1
//
2
// This file is part of the Marble Virtual Globe.
Torsten Rahn's avatar
Torsten Rahn committed
3 4 5 6 7
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
8 9
// Copyright 2008 Torsten Rahn <tackat@kde.org>
// Copyright 2009 Jens-Michael Hoffmann <jensmh@gmx.de>
Torsten Rahn's avatar
Torsten Rahn committed
10 11 12 13 14 15 16
//


// Own
#include "PluginManager.h"

// Qt
17 18
#include <QList>
#include <QPluginLoader>
19
#include <QElapsedTimer>
20
#include <QMessageBox>
Torsten Rahn's avatar
Torsten Rahn committed
21 22 23

// Local dir
#include "MarbleDirs.h"
24
#include "MarbleDebug.h"
25
#include "RenderPlugin.h"
26
#include "PositionProviderPlugin.h"
27 28 29 30
#include "ParseRunnerPlugin.h"
#include "ReverseGeocodingRunnerPlugin.h"
#include "RoutingRunnerPlugin.h"
#include "SearchRunnerPlugin.h"
31
#include <config-marble.h>
Torsten Rahn's avatar
Torsten Rahn committed
32

33 34
namespace Marble
{
Torsten Rahn's avatar
Torsten Rahn committed
35 36 37 38

class PluginManagerPrivate
{
 public:
39 40 41
    PluginManagerPrivate(PluginManager* parent)
            : m_pluginsLoaded(false),
              m_parent(parent)
42 43 44
    {
    }

45 46
    ~PluginManagerPrivate();

47 48 49
    void loadPlugins();

    bool m_pluginsLoaded;
Bernhard Beschow's avatar
Bernhard Beschow committed
50 51
    QList<const RenderPlugin *> m_renderPluginTemplates;
    QList<const PositionProviderPlugin *> m_positionProviderPluginTemplates;
52 53 54 55
    QList<const SearchRunnerPlugin *> m_searchRunnerPlugins;
    QList<const ReverseGeocodingRunnerPlugin *> m_reverseGeocodingRunnerPlugins;
    QList<RoutingRunnerPlugin *> m_routingRunnerPlugins;
    QList<const ParseRunnerPlugin *> m_parsingRunnerPlugins;
56
    PluginManager* m_parent;
57 58
    static QStringList m_blacklist;
    static QStringList m_whitelist;
59

60
#ifdef Q_OS_ANDROID
61 62
    QStringList m_pluginPaths;
#endif
Torsten Rahn's avatar
Torsten Rahn committed
63 64
};

65 66 67
QStringList PluginManagerPrivate::m_blacklist;
QStringList PluginManagerPrivate::m_whitelist;

68 69
PluginManagerPrivate::~PluginManagerPrivate()
{
70
    // nothing to do
71 72
}

73
PluginManager::PluginManager( QObject *parent ) : QObject( parent ),
74
    d( new PluginManagerPrivate(this) )
Torsten Rahn's avatar
Torsten Rahn committed
75
{
76
    //Checking assets:/plugins for uninstalled plugins
77
#ifdef Q_OS_ANDROID
78 79
        installPluginsFromAssets();
#endif
Torsten Rahn's avatar
Torsten Rahn committed
80 81 82 83
}

PluginManager::~PluginManager()
{
Laurent Montel's avatar
Laurent Montel committed
84
    delete d;
Torsten Rahn's avatar
Torsten Rahn committed
85 86
}

87
QList<const RenderPlugin *> PluginManager::renderPlugins() const
88
{
Bernhard Beschow's avatar
Bernhard Beschow committed
89
    d->loadPlugins();
90
    return d->m_renderPluginTemplates;
91
}
92

Bernhard Beschow's avatar
Bernhard Beschow committed
93
void PluginManager::addRenderPlugin( const RenderPlugin *plugin )
94 95 96 97 98 99
{
    d->loadPlugins();
    d->m_renderPluginTemplates << plugin;
    emit renderPluginsChanged();
}

100
QList<const PositionProviderPlugin *> PluginManager::positionProviderPlugins() const
101
{
Bernhard Beschow's avatar
Bernhard Beschow committed
102
    d->loadPlugins();
103
    return d->m_positionProviderPluginTemplates;
104 105
}

Bernhard Beschow's avatar
Bernhard Beschow committed
106
void PluginManager::addPositionProviderPlugin( const PositionProviderPlugin *plugin )
107 108 109 110 111 112
{
    d->loadPlugins();
    d->m_positionProviderPluginTemplates << plugin;
    emit positionProviderPluginsChanged();
}

113
QList<const SearchRunnerPlugin *> PluginManager::searchRunnerPlugins() const
114 115
{
    d->loadPlugins();
116 117 118
    return d->m_searchRunnerPlugins;
}

Bernhard Beschow's avatar
Bernhard Beschow committed
119
void PluginManager::addSearchRunnerPlugin( const SearchRunnerPlugin *plugin )
120 121 122 123 124 125
{
    d->loadPlugins();
    d->m_searchRunnerPlugins << plugin;
    emit searchRunnerPluginsChanged();
}

126 127 128 129 130 131
QList<const ReverseGeocodingRunnerPlugin *> PluginManager::reverseGeocodingRunnerPlugins() const
{
    d->loadPlugins();
    return d->m_reverseGeocodingRunnerPlugins;
}

Bernhard Beschow's avatar
Bernhard Beschow committed
132
void PluginManager::addReverseGeocodingRunnerPlugin( const ReverseGeocodingRunnerPlugin *plugin )
133 134 135 136 137 138
{
    d->loadPlugins();
    d->m_reverseGeocodingRunnerPlugins << plugin;
    emit reverseGeocodingRunnerPluginsChanged();
}

139 140 141 142 143 144
QList<RoutingRunnerPlugin *> PluginManager::routingRunnerPlugins() const
{
    d->loadPlugins();
    return d->m_routingRunnerPlugins;
}

145 146 147 148 149 150 151
void PluginManager::addRoutingRunnerPlugin( RoutingRunnerPlugin *plugin )
{
    d->loadPlugins();
    d->m_routingRunnerPlugins << plugin;
    emit routingRunnerPluginsChanged();
}

152 153 154 155
QList<const ParseRunnerPlugin *> PluginManager::parsingRunnerPlugins() const
{
    d->loadPlugins();
    return d->m_parsingRunnerPlugins;
156 157
}

Bernhard Beschow's avatar
Bernhard Beschow committed
158
void PluginManager::addParseRunnerPlugin( const ParseRunnerPlugin *plugin )
159 160 161 162 163 164
{
    d->loadPlugins();
    d->m_parsingRunnerPlugins << plugin;
    emit parseRunnerPluginsChanged();
}

165 166 167 168 169 170 171 172 173 174
void PluginManager::blacklistPlugin(const QString &filename)
{
    PluginManagerPrivate::m_blacklist << MARBLE_SHARED_LIBRARY_PREFIX + filename;
}

void PluginManager::whitelistPlugin(const QString &filename)
{
    PluginManagerPrivate::m_whitelist << MARBLE_SHARED_LIBRARY_PREFIX + filename;
}

175
/** Append obj to the given plugins list if it inherits both T and U */
176 177
template<class Iface, class Plugin>
bool appendPlugin( QObject * obj, const QPluginLoader *loader, QList<Plugin> &plugins )
Bernhard Beschow's avatar
Bernhard Beschow committed
178
{
179
    if ( qobject_cast<Iface*>( obj ) && qobject_cast<Plugin>( obj ) ) {
Bernhard Beschow's avatar
Bernhard Beschow committed
180 181
        Q_ASSERT( obj->metaObject()->superClass() ); // all our plugins have a super class
        mDebug() <<  obj->metaObject()->superClass()->className()
182 183
                << "plugin loaded from" << (loader ? loader->fileName() : "<static>");
        auto plugin = qobject_cast<Plugin>( obj );
184
        Q_ASSERT( plugin ); // checked above
185
        plugins << plugin;
186 187 188 189 190 191
        return true;
    }

    return false;
}

192
void PluginManagerPrivate::loadPlugins()
Torsten Rahn's avatar
Torsten Rahn committed
193
{
194 195 196 197 198
    if (m_pluginsLoaded)
    {
        return;
    }

199
    QElapsedTimer t;
200
    t.start();
Patrick Spendrin's avatar
Patrick Spendrin committed
201
    mDebug() << "Starting to load Plugins.";
Torsten Rahn's avatar
Torsten Rahn committed
202

203
    QStringList pluginFileNameList = MarbleDirs::pluginEntryList( "", QDir::Files );
204 205

    MarbleDirs::debug();
Torsten Rahn's avatar
Torsten Rahn committed
206

207 208
    Q_ASSERT( m_renderPluginTemplates.isEmpty() );
    Q_ASSERT( m_positionProviderPluginTemplates.isEmpty() );
209 210 211 212
    Q_ASSERT( m_searchRunnerPlugins.isEmpty() );
    Q_ASSERT( m_reverseGeocodingRunnerPlugins.isEmpty() );
    Q_ASSERT( m_routingRunnerPlugins.isEmpty() );
    Q_ASSERT( m_parsingRunnerPlugins.isEmpty() );
213

214
    bool foundPlugin = false;
Dennis Nienhüser's avatar
Dennis Nienhüser committed
215
    for( const QString &fileName: pluginFileNameList ) {
216 217 218 219 220 221 222 223 224 225
        QString const baseName = QFileInfo(fileName).baseName();
        if (!m_whitelist.isEmpty() && !m_whitelist.contains(baseName)) {
            mDebug() << "Ignoring non-whitelisted plugin " << fileName;
            continue;
        }
        if (m_blacklist.contains(baseName)) {
            mDebug() << "Ignoring blacklisted plugin " << fileName;
            continue;
        }

Patrick Spendrin's avatar
Patrick Spendrin committed
226
        // mDebug() << fileName << " - " << MarbleDirs::pluginPath( fileName );
227
        QString const path = MarbleDirs::pluginPath( fileName );
228
#ifdef Q_OS_ANDROID
229 230 231 232 233 234 235
        QFileInfo targetFile( path );
        if ( !m_pluginPaths.contains( targetFile.canonicalFilePath() ) ) {
            // @todo Delete the file here?
            qDebug() << "Ignoring file " << path << " which is not among the currently installed plugins";
            continue;
        }
#endif
236
        QPluginLoader* loader = new QPluginLoader( path, m_parent );
Torsten Rahn's avatar
Torsten Rahn committed
237

238
        QObject * obj = loader->instance();
239

Torsten Rahn's avatar
 
Torsten Rahn committed
240
        if ( obj ) {
241
            bool isPlugin = appendPlugin<RenderPluginInterface>
242
                       ( obj, loader, m_renderPluginTemplates );
243
            isPlugin = isPlugin || appendPlugin<PositionProviderPluginInterface>
244
                       ( obj, loader, m_positionProviderPluginTemplates );
245
            isPlugin = isPlugin || appendPlugin<SearchRunnerPlugin>
246
                       ( obj, loader, m_searchRunnerPlugins ); // intentionally T==U
247
            isPlugin = isPlugin || appendPlugin<ReverseGeocodingRunnerPlugin>
248
                       ( obj, loader, m_reverseGeocodingRunnerPlugins ); // intentionally T==U
249
            isPlugin = isPlugin || appendPlugin<RoutingRunnerPlugin>
250
                       ( obj, loader, m_routingRunnerPlugins ); // intentionally T==U
251
            isPlugin = isPlugin || appendPlugin<ParseRunnerPlugin>
252
                       ( obj, loader, m_parsingRunnerPlugins ); // intentionally T==U
253
            if ( !isPlugin ) {
Dennis Nienhüser's avatar
Dennis Nienhüser committed
254
                qWarning() << "Ignoring the following plugin since it couldn't be loaded:" << path;
255
                mDebug() << "Plugin failure:" << path << "is a plugin, but it does not implement the "
256
                        << "right interfaces or it was compiled against an old version of Marble. Ignoring it.";
257
                delete loader;
258 259
            } else {
                foundPlugin = true;
260
            }
261
        } else {
262 263
            qWarning() << "Ignoring to load the following file since it doesn't look like a valid Marble plugin:" << path << endl
                       << "Reason:" << loader->errorString();
Dennis Nienhüser's avatar
Dennis Nienhüser committed
264
            delete loader;
Torsten Rahn's avatar
Torsten Rahn committed
265 266
        }
    }
267

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
    if ( !foundPlugin ) {
#ifdef Q_OS_WIN
        QString pluginPaths = "Plugin Path: " + MarbleDirs::marblePluginPath();
        if ( MarbleDirs::marblePluginPath().isEmpty() )
            pluginPaths = "";
        pluginPaths += "System Path: " + MarbleDirs::pluginSystemPath() + "\nLocal Path: " + MarbleDirs::pluginLocalPath();

        QMessageBox::warning( nullptr,
                              "No plugins loaded",
                              "No plugins were loaded, please check if the plugins were installed in one of the following paths:\n" + pluginPaths
                              + "\n\nAlso check if the plugin is compiled against the right version of Marble. " +
                              "Analyzing the debug messages inside a debugger might give more insight." );
#else
        qWarning() << "No plugins loaded. Please check if the plugins were installed in the correct path,"
                   << "or if any errors occurred while loading plugins.";
#endif
    }

286
    m_pluginsLoaded = true;
287 288

    mDebug() << Q_FUNC_INFO << "Time elapsed:" << t.elapsed() << "ms";
Torsten Rahn's avatar
Torsten Rahn committed
289 290
}

291
#ifdef Q_OS_ANDROID
292 293
    void PluginManager::installPluginsFromAssets() const
    {
294
        d->m_pluginPaths.clear();
295 296 297 298
        QStringList copyList = MarbleDirs::pluginEntryList(QString());
        QDir pluginHome(MarbleDirs::localPath());
        pluginHome.mkpath(MarbleDirs::pluginLocalPath());
        pluginHome.setCurrent(MarbleDirs::pluginLocalPath());
299 300 301

        QStringList pluginNameFilter = QStringList() << "lib*.so";
        QStringList const existingPlugins = QDir(MarbleDirs::pluginLocalPath()).entryList(pluginNameFilter, QDir::Files);
Dennis Nienhüser's avatar
Dennis Nienhüser committed
302
        for(const QString &existingPlugin: existingPlugins) {
303 304 305
            QFile::remove(existingPlugin);
        }

Dennis Nienhüser's avatar
Dennis Nienhüser committed
306
        for (const QString & file: copyList) {
307 308
            QString const target = MarbleDirs::pluginLocalPath() + QLatin1Char('/') + file;
            if (QFileInfo(MarbleDirs::pluginSystemPath() + QLatin1Char('/') + file).isDir()) {
309
                pluginHome.mkpath(target);
310
            }
311
            else {
312
                QFile temporaryFile(MarbleDirs::pluginSystemPath() + QLatin1Char('/') + file);
313 314 315
                temporaryFile.copy(target);
                QFileInfo targetFile(target);
                d->m_pluginPaths << targetFile.canonicalFilePath();
316 317 318 319 320
            }
        }
    }
#endif

321
}
322

323
#include "moc_PluginManager.cpp"