File indexing completed on 2024-06-16 03:42:36

0001 /*
0002     File                 : PluginManager.cpp
0003     Project              : LabPlot/SciDAVis
0004     Description          : This class manages all plugins.
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2009 Tilman Benkert <thzs*gmx.net  (use @ for *)>
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "backend/core/plugin/PluginManager.h"
0011 #include "backend/core/plugin/PluginLoader.h"
0012 #include <QPluginLoader>
0013 #include <QSet>
0014 #include <QSettings>
0015 #include <QtDebug>
0016 
0017 /**
0018  * \class PluginManager
0019  * \brief This class manages all plugins.
0020  *
0021  * This class is used to persistently (using QSettings) enable/disable dynamic plugins and
0022  * to make the difference between static and dynamic plugins transparent
0023  * to the application. You typically do the following:
0024    \code
0025    // let the user select a plugin from a file dialog
0026    PluginManager::enablePlugin(absoluteFilePath);
0027    ...
0028    // somewhere else in the application
0029    foreach(QObject *plugin, PluginManager::plugins()) {
0030      MyInterface *myObject = qobject_cast<MyInterface *>(plugin);
0031         if (myObject)
0032            // found a suitable plugin
0033    }
0034    \endcode
0035  *
0036  */
0037 
0038 #ifdef Q_OS_MAC // Mac
0039 #define SETTINGS_FORMAT QSettings::IniFormat
0040 #else
0041 #define SETTINGS_FORMAT QSettings::NativeFormat
0042 #endif
0043 
0044 PluginManager::PluginManager() = default;
0045 
0046 QList<PluginLoader*> PluginManager::m_loadedPlugins;
0047 QList<PluginLoader*> PluginManager::m_pluginsWithErrors;
0048 QObjectList PluginManager::m_staticPlugins;
0049 bool PluginManager::m_loaded = false;
0050 QObjectList PluginManager::m_allPlugins;
0051 
0052 /**
0053  * \brief Load a plugin and append it to the plugin list in the settings.
0054  *
0055  * \param absolutePath Absolute path to the plugin file.
0056  * \return True, if the plugin was successfully loaded. Even if false is returned,
0057  * the path is still appended to the settings.
0058  */
0059 bool PluginManager::enablePlugin(const QString& absolutePath) {
0060     loadAll();
0061 
0062     // save the plugin in the settings
0063     // TODO
0064     //  QSettings settings(SETTINGS_FORMAT, QSettings::UserScope, SciDAVis::appName, SciDAVis::appName);
0065     //  settings.beginGroup("PluginManager");
0066     //  QStringList pluginPaths = settings.value("enabledPlugins", QStringList()).toStringList();
0067     //  pluginPaths.removeDuplicates();
0068     //  if (!pluginPaths.contains(absolutePath)) {
0069     //      pluginPaths << absolutePath;
0070     //      settings.setValue("enabledPlugins", pluginPaths);
0071     //  }
0072     //  settings.endGroup();
0073 
0074     // check whether it's already loaded
0075     bool result = true;
0076     bool alreadyLoaded = false;
0077     for (auto* loader : m_loadedPlugins) {
0078         if (loader->fileName() == absolutePath) {
0079             alreadyLoaded = true;
0080             break;
0081         }
0082     }
0083 
0084     if (!alreadyLoaded) {
0085         PluginLoader* pluginLoader = nullptr;
0086         // check whether a loader for this plugin already exists
0087         for (auto* loader : m_pluginsWithErrors) {
0088             if (loader->fileName() == absolutePath) {
0089                 pluginLoader = loader;
0090                 m_pluginsWithErrors.removeAll(loader);
0091                 break;
0092             }
0093         }
0094 
0095         if (!pluginLoader)
0096             pluginLoader = new PluginLoader(absolutePath);
0097 
0098         // try to load the plugin
0099         if (!pluginLoader->load()) {
0100             m_pluginsWithErrors << pluginLoader;
0101             result = false;
0102         } else {
0103             m_loadedPlugins << pluginLoader;
0104             m_allPlugins << pluginLoader->instance();
0105         }
0106     }
0107 
0108     return result;
0109 }
0110 
0111 /**
0112  * \brief Remove the plugin from the plugin list in the settings (and unload it if rightNow == true).
0113  *
0114  * \param rightNow Set this to true if you want to unload the plugin immediately (which is not
0115  * always a good idea). The default behavior is to disable the plugin for the next program startup.
0116  */
0117 void PluginManager::disablePlugin(const QString& absolutePath, bool rightNow) {
0118     loadAll();
0119 
0120     // remove the plugin from the settings
0121     // TODO
0122     //  QSettings settings(SETTINGS_FORMAT, QSettings::UserScope, SciDAVis::appName, SciDAVis::appName);
0123     //  settings.beginGroup("PluginManager");
0124     //  QStringList pluginPaths = settings.value("enabledPlugins", QStringList()).toStringList();
0125     //  pluginPaths.removeDuplicates();
0126     //  int index = pluginPaths.indexOf(absolutePath);
0127     //  if (index > -1) {
0128     //      pluginPaths.removeAt(index);
0129     //      settings.setValue("enabledPlugins", pluginPaths);
0130     //  }
0131     //  settings.endGroup();
0132 
0133     // remove the related loader if it is in the list of failed loaders
0134     for (auto* loader : m_pluginsWithErrors) {
0135         if (loader->fileName() == absolutePath) {
0136             m_pluginsWithErrors.removeAll(loader);
0137             delete loader;
0138             break;
0139         }
0140     }
0141 
0142     if (rightNow) {
0143         // unload the plugin and remove the loader
0144         for (auto* loader : m_loadedPlugins) {
0145             if (loader->fileName() == absolutePath) {
0146                 m_allPlugins.removeAll(loader->instance());
0147                 m_loadedPlugins.removeAll(loader);
0148                 loader->unload();
0149                 delete loader;
0150                 break;
0151             }
0152         }
0153     }
0154 }
0155 
0156 /**
0157  * \brief Get all plugin root instances.
0158  *
0159  * This function will return a list of all static plugin root instances
0160  * as well as those from all successfully loaded dynamic plugins.
0161  */
0162 QObjectList PluginManager::plugins() {
0163     loadAll();
0164     return m_allPlugins;
0165 }
0166 
0167 /**
0168  * \brief Load all plugins contained in the "enabledPlugins" setting.
0169  *
0170  * This method also fetches a list of all static plugins.
0171  */
0172 void PluginManager::loadAll() {
0173     if (!m_loaded) {
0174         m_staticPlugins = QPluginLoader::staticInstances();
0175         m_allPlugins << m_staticPlugins;
0176 
0177         // TODO:
0178         //      QSettings settings(SETTINGS_FORMAT, QSettings::UserScope, SciDAVis::appName, SciDAVis::appName);
0179         //      settings.beginGroup("PluginManager");
0180         //      QStringList plugins = settings.value("enabledPlugins", QStringList()).toStringList();
0181         //      plugins.removeDuplicates();
0182         //      foreach (const QString& plugin, plugins) {
0183         //          PluginLoader *pluginLoader = new PluginLoader(plugin);
0184         //          if (!pluginLoader->load()) {
0185         //              m_pluginsWithErrors << pluginLoader;
0186         //          } else {
0187         //              m_loadedPlugins << pluginLoader;
0188         //              m_allPlugins << pluginLoader->instance();
0189         //          }
0190         //      }
0191         //      settings.endGroup();
0192 
0193         m_loaded = true;
0194     }
0195 }
0196 
0197 /**
0198  * \brief Get the file names of all loaded plugins.
0199  */
0200 QStringList PluginManager::loadedPluginFileNames() {
0201     QStringList result;
0202     for (auto* loader : m_loadedPlugins)
0203         result << loader->fileName();
0204     return result;
0205 }
0206 
0207 /**
0208  * \brief Get the file names of all plugins that failed to load.
0209  */
0210 QStringList PluginManager::failedPluginFileNames() {
0211     QStringList result;
0212     for (auto* loader : m_pluginsWithErrors)
0213         result << loader->fileName();
0214     return result;
0215 }
0216 
0217 /**
0218  * \brief Get the error messages of a plugin that failed to load.
0219  */
0220 QString PluginManager::errorOfPlugin(const QString& fileName) {
0221     QString result;
0222     for (auto* loader : m_pluginsWithErrors)
0223         if (loader->fileName() == fileName)
0224             result = loader->statusString();
0225     return result;
0226 }
0227 
0228 /**
0229  * \brief Get the plugin root instance for a given file name.
0230  */
0231 QObject* PluginManager::instanceOfPlugin(const QString& fileName) {
0232     QObject* result = nullptr;
0233     for (auto* loader : m_loadedPlugins)
0234         if (loader->fileName() == fileName)
0235             result = loader->instance();
0236     return result;
0237 }
0238 
0239 #ifdef QT_DEBUG
0240 void PluginManager::printAll() {
0241     loadAll();
0242     for (auto* loader : m_loadedPlugins) {
0243         qDebug() << "Plugin" << loader->fileName() << "loaded";
0244     }
0245 
0246     for (auto* loader : m_pluginsWithErrors) {
0247         qDebug() << loader->statusString();
0248     }
0249 }
0250 #endif