diff options
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/services/pluginmanager.h')
| -rw-r--r-- | SQLiteStudio3/coreSQLiteStudio/services/pluginmanager.h | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/pluginmanager.h b/SQLiteStudio3/coreSQLiteStudio/services/pluginmanager.h new file mode 100644 index 0000000..4f822bc --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/services/pluginmanager.h @@ -0,0 +1,528 @@ +#ifndef PLUGINMANAGER_H +#define PLUGINMANAGER_H + +#include "coreSQLiteStudio_global.h" +#include "plugins/plugin.h" +#include "plugins/plugintype.h" +#include "common/global.h" +#include "sqlitestudio.h" +#include <QStringList> + +class Plugin; +class ScriptingPlugin; + +/** @file */ + +/** + * @brief The plugin manager. + * + * It's a singleton accessible with PLUGINS macro. + * + * It provides methods to load, unload and query plugins. It stores loaded + * plugins in configuration on application close and loads that plugins during next startup. + * If there's a plugin which was not defined if it was loaded or not - it is loaded by default. + * + * Description of Plugin interface contains list of directories scanned for plugins. + * + * There's a macro for global access to the PluginManager - ::PLUGINS. It actually calls + * SQLiteStudio::getInstance() and from there it calls SQLiteStudio::getPluginManager(). + * + * Plugins in PluginManager are organized by types. The manager has a list of types and for each type + * there's a list of plugins of that type. Plugin types are represented by PluginType class. + * + * @section querying_plugins Querying available and loaded plugins + * + * To query all plugins available to the application (including those not loaded) use getAllPluginNames(). + * + * To query if certain plugin is loaded use isLoaded(). + * + * To query all plugins loaded to the application use getLoadedPlugins(). It requires either PluginType, + * or plugin interface class (for template method version) to determinate what group of plugins you're + * interested in. To return all plugins (no matter what type), use template method version with Plugin + * as an interface type for parameter. An example of getting all SQL formatter plugins: + * @code + * QList<SqlFormatterPlugin*> formatterPlugins = PLUGINS->getLoadedPlugins<SqlFormatterPlugin>(); + * @endcode + * + * To get list of plugin types use getPluginTypes(). + * + * To get PluginType for given plugin interface use getPluginType<PluginInterfaceClass>(). + * + * These are just the most important methods to query plugins. See full list of methods for more. + * + * @section load_unload Loading and unloading plugins + * + * To load plugin use load(). + * + * To unload plugin use unload(). + * + * Apart from that, all plugins are loaded initially (unless they were unloaded last time during + * application close). + * + * @section plugin_types Specialized plugin types + * + * Each plugin must implement Plugin interface, but it also can implement other interfaces, + * which makes them suitable for fulfilling certain functionalities. For example all plugins + * implementing SqlFormatterPlugin will automatically be available to SqlFormatter object, + * because PluginManager knows which plugins implement SqlFormatterPlugin and can provide full + * list of those plugins to SqlFormatter. This is done by call to registerPluginType(). + * + * The registerPluginType() registers new type of plugins that will be recognizable by PluginManager. + * Once the new interface is registered with this method, all plugins will be tested against + * implementation for that type and those which implement the interface will be stored + * in the proper collection assigned for that plugin type. + * + * This way PluginManager can provide list of all plugins implementing given interface + * with getLoadedPlugins(). + * + * All registered plugin types can be queries by getPluginTypes() method. + */ +class API_EXPORT PluginManager : public QObject +{ + Q_OBJECT + + public: + struct PluginDetails + { + QString name; + QString title; + QString description; + bool builtIn = false; + int version = 0; + QString versionString; + QString filePath; + }; + + /** + * @brief Loads all plugins. + * + * Scans all plugin directories and tries to load all plugins found there. For list of directories + * see description of Plugin class. + */ + virtual void init() = 0; + + /** + * @brief Unloads all loaded plugins. + * + * Also deregisters all plugin types. + */ + virtual void deinit() = 0; + + /** + * @brief Provides list of registered plugin types. + * @return List of registered plugin types. + */ + virtual QList<PluginType*> getPluginTypes() const = 0; + + /** + * @brief Provides list of plugin directories. + * @return List of directory paths (not necessarily absolute paths). + */ + virtual QStringList getPluginDirs() const = 0; + + /** + * @brief Provides absolute path to the plugin's file. + * @param plugin Loaded plugin. + * @return Absolute path to the plugin file. + */ + virtual QString getFilePath(Plugin* plugin) const = 0; + + /** + * @brief Loads instance of built-in plugin into the manager. + * @param plugin Plugin instance. + * @return true on success or false on failure (plugin's type could not be matched to registered plugin types). + * + * Built-in plugins are classes that implement plugin interface, but they are not in separate library. + * Instead they are classes compiled and linked to the main application. Such classes should be instantiated + * and passed to this method, so the PluginManager can treat it as any other plugin. + * + * @note Built-in plugins cannot be loaded or unloaded, so calls to load() or unload() will make no effect. + */ + virtual bool loadBuiltInPlugin(Plugin* plugin) = 0; + + /** + * @brief Loads the plugin. + * @param pluginName Name of the plugin to load. + * @return true on success, or false on failure. + * + * When loading a plugin, PluginManager loads the plugin file and resolves all its symbols inside. + * If that failed, file gets unloaded and the method returns false. + * + * Qt plugins framework will require that the loaded plugin will provide exactly one Plugin interface + * implementation. Otherwise file will be unloaded and this method will return false. + * + * Then the Plugin::init() method is called. It it returns false, then plugin is unloaded + * and this method returns false. + * + * Then meta information is read from the plugin (title, version, author, etc) - see Plugin for details. + * + * Then loaded plugin passes several tests against all registered plugin types. If it implements + * any type, it's added to the plugin list of that type. + * + * Then the loaded() signal is emitted. Finally, the true value is returned. + */ + virtual bool load(const QString& pluginName) = 0; + + /** + * @brief Unloads plugin. + * @param pluginName Plugin name to be unloaded. + * + * If the plugin is not loaded, this method does nothing. + * First the aboutToUnload() signal is emitted. Then Plugin::deinit() is called. + * Then the plugin library is unloaded (which causes Qt's plugins framework to delete the object + * implementing Plugin interface before the actual unloading). + * + * Finally, the unloaded() signal is emitted. + */ + virtual void unload(const QString& pluginName) = 0; + + /** + * @brief Unloads plugin. + * @param plugin Loaded plugin to be unloaded. + * @overload + */ + virtual void unload(Plugin* plugin) = 0; + + /** + * @brief Tests if given plugin is loaded. + * @param pluginName Name of the plugin to test. + * @return true if the plugin is loaded, or false otherwise. + */ + virtual bool isLoaded(const QString& pluginName) const = 0; + + /** + * @brief Tests whether given plugin is one of built-in plugins. + * @param pluginName Name of the plugin to test. + * @return true if the plugin is the built-in one, or false otherwise. + * + * @see loadBuiltInPlugin() + */ + virtual bool isBuiltIn(const QString& pluginName) const = 0; + + /** + * @brief Finds loaded plugin by name. + * @param pluginName Plugin name to look for. + * @return Loaded plugin object, or null of the plugin is not loaded. + */ + virtual Plugin* getLoadedPlugin(const QString& pluginName) const = 0; + + /** + * @brief Provides list of plugin names of given type. + * @param type Type of plugins to get names for. + * @return List of names. + * + * It returns names for all plugins available for the application, + * no matter they're currently loaded or not. + */ + virtual QStringList getAllPluginNames(PluginType* type) const = 0; + + virtual QList<PluginDetails> getAllPluginDetails() const = 0; + virtual QList<PluginDetails> getLoadedPluginDetails() const = 0; + + /** + * @brief Provides list of all plugin names. + * @return All available plugin names, no matter if loaded or not. + */ + virtual QStringList getAllPluginNames() const = 0; + + /** + * @brief Finds plugin's type. + * @param pluginName Plugin name (can be unloaded plugin). + * @return Type of the plugin, or null if plugin was not found by the name. + */ + virtual PluginType* getPluginType(const QString& pluginName) const = 0; + + /** + * @brief Provides plugin's author. + * @param pluginName Name of the plugin (can be unloaded plugin). + * @return Author string defined in the plugin. + */ + virtual QString getAuthor(const QString& pluginName) const = 0; + + /** + * @brief Provides plugin's title. + * @param pluginName Name of the plugin (can be unloaded plugin). + * @return Title string defined in the plugin. + */ + virtual QString getTitle(const QString& pluginName) const = 0; + + /** + * @brief Provides human-readable version of the plugin. + * @param pluginName Name of the plugin (can be unloaded plugin). + * @return Version string defined in the plugin. + */ + virtual QString getPrintableVersion(const QString& pluginName) const = 0; + + /** + * @brief Provides numeric version of the plugin. + * @param pluginName Name of the plugin (can be unloaded plugin). + * @return Numeric version defined in the plugin. + */ + virtual int getVersion(const QString& pluginName) const = 0; + + /** + * @brief Provides detailed description about the plugin. + * @param pluginName Name of the plugin (can be unloaded plugin). + * @return Description defined in the plugin. + */ + virtual QString getDescription(const QString& pluginName) const = 0; + + /** + * @brief Tells plugin's type. + * @param plugin Loaded plugin. + * @return Type of the plugin. + */ + virtual PluginType* getPluginType(Plugin* plugin) const = 0; + + /** + * @brief Provides list of plugins for given plugin type. + * @param type Type of plugins. + * @return List of plugins for given type. + * + * This version of the method takes plugin type object as an discriminator. + * This way you can iterate through all types (using getPluginTypes()) + * and then for each type get list of plugins for that type, using this method. + */ + virtual QList<Plugin*> getLoadedPlugins(PluginType* type) const = 0; + + /** + * @brief Provides list of all loaded plugins. + * @return List of plugins. + */ + virtual QList<Plugin*> getLoadedPlugins() const = 0; + + /** + * @brief Provides names of all loaded plugins. + * @return List of plugin names. + */ + virtual QStringList getLoadedPluginNames() const = 0; + + /** + * @brief Provides scripting plugin for given scripting language if available. + * @param languageName Scripting language name to get plugin for. + * @return Plugin object or null if proper plugin was not found. + * + * Calling this function is similar in results to call to getLoadedPlugins<ScriptingPlugin>() + * and then extracting a single plugin with desired scripting language support, except + * calling this function is much faster. PluginManager keeps scripting language plugins + * internally in hash table with language names as keys, so getting scripting plugin + * for desired language is way faster when using this method. + */ + virtual ScriptingPlugin* getScriptingPlugin(const QString& languageName) const = 0; + + /** + * @brief Loads metadata from given Json object. + * @param The metadata from json file. + * @return Metadata with keys: type, name, title, description, version, author, ui (optional). + */ + virtual QHash<QString,QVariant> readMetaData(const QJsonObject& metaData) = 0; + + /** + * @brief Converts integer version to string version. + * @param version Integer version in XXYYZZ standard (see Plugin::getVersion() for details). + * @return Printable version string. + */ + virtual QString toPrintableVersion(int version) const = 0; + + /** + * @brief Provides list of plugin names that the queried plugin depends on. + * @param pluginName Queried plugin name. + * @return List of plugin names, usually an empty list. + * + * This is the list that is declared in plugins metadata under the "dependencies" key. + * The plugin can be loaded only if all its dependencies were successfully loaded. + */ + virtual QStringList getDependencies(const QString& pluginName) const = 0; + + /** + * @brief Provides list of plugin names that are declared to be in conflict with queries plugin. + * @param pluginName Queried plugin name, + * @return List of plugin names, usually an empty list. + * + * If a plugin declares other plugin (by name) to be its conflict (a "conflicts" key in plugin's metadata), + * then those 2 plugins cannot be loaded at the same time. SQLiteStudio will always refuse to load + * the other one, if the first one is already loaded - and vice versa. + * + * Declaring conflicts for a plugin can be useful for example if somebody wants to proivde an alternative + * implementation of SQLite2 database plugin, etc. In that case SQLiteStudio won't get confused in + * deciding which plugin to use for supporting such databases. + */ + virtual QStringList getConflicts(const QString& pluginName) const = 0; + + /** + * @brief Tells if plugins were already loaded on startup, or is this yet to happen. + * @return true if plugins were loaded, false if they are going to be loaded. + */ + virtual bool arePluginsInitiallyLoaded() const = 0; + + /** + * @brief registerPluginType Registers plugin type for loading and managing. + * @tparam T Interface class (as defined by Qt plugins standard) + * @param form Optional name of form object. + * @param title Optional title for configuration dialog. + * The form object name is different if you register new type by general type plugin. + * Built-in types are defined as the name of page from ConfigDialog. + * Types registered from plugins should use top widget name defined in the ui file. + * The title parameter is required if the configuration form was defined outside (in plugin). + * Title will be used for configuration dialog to display plugin type category (on the left of the dialog). + */ + template <class T> + void registerPluginType(const QString& title, const QString& form = QString()) + { + registerPluginType(new DefinedPluginType<T>(title, form)); + } + + /** + * @brief Gets plugin type for given plugin interface. + * @tparam T Interface class of the plugin. + * @return Type of the plugin for given interface if registered, or null otherwise. + */ + template <class T> + PluginType* getPluginType() const + { + foreach (PluginType* type, getPluginTypes()) + { + if (!dynamic_cast<DefinedPluginType<T>*>(type)) + continue; + + return type; + } + return nullptr; + } + + /** + * @brief Provide list of plugins of given type. + * @tparam T Interface class of plugins, that we want to get. + * + * This method version gets plugin interface type as template parameter, + * so it returns list of loaded plugins that are already casted to requested + * interface type. + */ + template <class T> + QList<T*> getLoadedPlugins() const + { + QList<T*> typedPlugins; + PluginType* type = getPluginType<T>(); + if (!type) + return typedPlugins; + + foreach (Plugin* plugin, getLoadedPlugins(type)) + typedPlugins << dynamic_cast<T*>(plugin); + + return typedPlugins; + } + + /** + * @brief Provide list of plugin names of given type. + * @tparam T Interface class of plugins, that we want to get names for. + * + * This method version gets plugin interface type as template parameter, + * so it returns list of names of loaded plugins. + */ + template <class T> + QStringList getLoadedPluginNames() const + { + QStringList names; + PluginType* type = getPluginType<T>(); + if (!type) + return names; + + foreach (Plugin* plugin, getLoadedPlugins(type)) + names << plugin->getName(); + + return names; + } + + protected: + /** + * @brief Adds given type to registered plugins list. + * @param type Type instance. + * + * This is a helper method for registerPluginType<T>() template function. + * The implementation should register given plugin type, that is - add it to a list of registered types. + */ + virtual void registerPluginType(PluginType* type) = 0; + + signals: + /** + * @brief Emitted just before plugin is unloaded. + * @param plugin Plugin object to be unloaded. + * @param type Type of the plugin. + * + * It's emitted just before call to Plugin::deinit(), destroying plugin object + * and unloading the plugin file. + * + * Any code using certain plugin should listen for this signal and stop using + * the plugin immediately when received this signal. Otherwise application may crash. + */ + void aboutToUnload(Plugin* plugin, PluginType* type); + + /** + * @brief Emitted just after plugin was loaded. + * @param plugin Plugin object from loaded plugin. + * @param type Plugin type. + * + * It's emitted after plugin was loaded and successfully initialized (which includes + * successful Plugin::init() call). + */ + void loaded(Plugin* plugin, PluginType* type); + + /** + * @brief Emitted after plugin was unloaded. + * @param pluginName Name of the plugin that was unloaded. + * @param type Type of the plugin. + * + * Emitted after plugin was deinitialized and unloaded. At this stage a plugin object + * is no longer available, only it's name and other metadata (like description, version, etc). + */ + void unloaded(const QString& pluginName, PluginType* type); + + /** + * @brief Emitted after initial plugin set was loaded. + * + * The initial load is performed at application startup. Any code that relies on + * some plugins being loaded (like for example code that loads list of databases relies on + * database support plugins) should listen to this signal. + */ + void pluginsInitiallyLoaded(); + + /** + * @brief Emitted when the plugin manager is deinitializing and will unload all plugins in a moment. + * + * It's emitted when user closes application, so the plugin manager deinitializes and unloads all plugins. + * This signal is emitted just before plugins get unloaded. + * If some signal handler is not interested in mass plugin unloading, then it can handle this signal + * and disconnect from unloaded() signal. + */ + void aboutToQuit(); + + /** + * @brief Emitted when plugin load was requested, but it failed. + * @param pluginName Name of the plugin that failed to load. + * + * It's used for example by ConfigDialog to uncheck plugin that was requested to load (checked) and it failed. + */ + void failedToLoad(const QString& pluginName); +}; + +/** + * @def PLUGINS + * @brief PluginsManager instance access macro. + * + * Since SQLiteStudio creates only one instance of PluginsManager, + * there is a standard method for accessing it, using code: + * @code + * QList<PluginType*> types = SQLiteStudio::getInstance()->getPluginManager()->getPluginTypes(); + * @endcode + * or there's a slightly simpler way: + * @code + * QList<PluginType*> types = SQLITESTUDIO->getPluginManager()->getPluginTypes(); + * @endcode + * or there is a very simplified method, using this macro: + * @code + * QList<PluginType*> types = PLUGINS->getPluginTypes(); + * @endcode + */ +#define PLUGINS SQLITESTUDIO->getPluginManager() + +#endif // PLUGINMANAGER_H |
