1 #ifndef PLUGINMANAGERIMPL_H
2 #define PLUGINMANAGERIMPL_H
3 
4 #include "services/pluginmanager.h"
5 #include <QPluginLoader>
6 #include <QHash>
7 
8 class API_EXPORT PluginManagerImpl : public PluginManager
9 {
10     Q_OBJECT
11 
12     public:
13         /**
14          * @brief Creates plugin manager.
15          */
16         PluginManagerImpl();
17 
18         /**
19          * @brief Deletes plugin manager.
20          */
21         ~PluginManagerImpl();
22 
23         void init();
24         void deinit();
25         QList<PluginType*> getPluginTypes() const;
26         QStringList getPluginDirs() const;
27         QString getFilePath(Plugin* plugin) const;
28         bool loadBuiltInPlugin(Plugin* plugin);
29         bool load(const QString& pluginName);
30         void unload(const QString& pluginName);
31         void unload(Plugin* plugin);
32         bool isLoaded(const QString& pluginName) const;
33         bool isBuiltIn(const QString& pluginName) const;
34         Plugin* getLoadedPlugin(const QString& pluginName) const;
35         QStringList getAllPluginNames(PluginType* type) const;
36         QStringList getAllPluginNames() const;
37         PluginType* getPluginType(const QString& pluginName) const;
38         QString getAuthor(const QString& pluginName) const;
39         QString getTitle(const QString& pluginName) const;
40         QString getPrintableVersion(const QString& pluginName) const;
41         int getVersion(const QString& pluginName) const;
42         QString getDescription(const QString& pluginName) const;
43         PluginType* getPluginType(Plugin* plugin) const;
44         QList<Plugin*> getLoadedPlugins(PluginType* type) const;
45         ScriptingPlugin* getScriptingPlugin(const QString& languageName) const;
46         QHash<QString,QVariant> readMetaData(const QJsonObject& metaData);
47         QString toPrintableVersion(int version) const;
48         QStringList getDependencies(const QString& pluginName) const;
49         QStringList getConflicts(const QString& pluginName) const;
50         bool arePluginsInitiallyLoaded() const;
51         QList<Plugin*> getLoadedPlugins() const;
52         QStringList getLoadedPluginNames() const;
53         QList<PluginDetails> getAllPluginDetails() const;
54         QList<PluginDetails> getLoadedPluginDetails() const;
55 
56     protected:
57         void registerPluginType(PluginType* type);
58 
59     private:
60         struct PluginDependency
61         {
62             QString name;
63             int minVersion = 0;
64             int maxVersion = 0;
65         };
66 
67         /**
68          * @brief Container for plugin related data.
69          *
70          * The container is used to represent plugin available to the application,
71          * no matter if it's loaded or not. It keeps all plugin related data,
72          * so it's available even the plugin is not loaded.
73          */
74         struct PluginContainer
75         {
76             /**
77              * @brief Name of the plugin.
78              */
79             QString name;
80 
81             /**
82              * @brief Title of the plugin, used on UI.
83              */
84             QString title;
85 
86             /**
87              * @brief Plugin's detailed description.
88              */
89             QString description;
90 
91             /**
92              * @brief Plugin's author.
93              */
94             QString author;
95 
96             /**
97              * @brief Numeric verion of the plugin.
98              */
99             int version;
100 
101             /**
102              * @brief Human-readable version.
103              */
104             QString printableVersion;
105 
106             /**
107              * @brief Type of the plugin.
108              */
109             PluginType* type = nullptr;
110 
111             /**
112              * @brief Full path to the plugin's file.
113              */
114             QString filePath;
115 
116             /**
117              * @brief Plugin's loaded state flag.
118              */
119             bool loaded;
120 
121             /**
122              * @brief Qt's plugin framework loaded for this plugin.
123              */
124             QPluginLoader* loader = nullptr;
125 
126             /**
127              * @brief Plugin object.
128              *
129              * It's null when plugin is not loaded.
130              */
131             Plugin* plugin = nullptr;
132 
133             /**
134              * @brief Flag indicating that the plugin is built in.
135              *
136              * Plugins built-in are classes implementing plugin's interface,
137              * but they are compiled and statically linked to the main application binary.
138              * They cannot be loaded or unloaded - they are loaded by default.
139              */
140             bool builtIn = false;
141 
142             /**
143              * @brief Flag indicating that plugin should be loaded, unless user unloaded it manually.
144              *
145              * If this flag is set to false, then the plugin will not be loaded, even it was not manually unloaded.
146              * This flag can be defined in plugin's json file using property named 'loadByDefault'.
147              */
148             bool loadByDefault = true;
149 
150             /**
151              * @brief Names of plugnis that this plugin depends on.
152              */
153             QList<PluginDependency> dependencies;
154 
155             /**
156              * @brief Names of plugins that this plugin conflicts with.
157              */
158             QStringList conflicts;
159         };
160 
161         /**
162          * @brief List of plugins, both loaded and unloaded.
163          */
164         typedef QList<PluginContainer*> PluginContainerList;
165 
166         /**
167          * @brief Scans plugin directories to find out available plugins.
168          *
169          * It looks in the following locations:
170          * <ul>
171          * <li> application_directory/plugins/
172          * <li> application_config_directory/plugins/
173          * <li> directory pointed by the SQLITESTUDIO_PLUGINS environment variable
174          * <li> directory compiled in as PLUGINS_DIR parameter of the compilation
175          * </ul>
176          *
177          * The application_directory is a directory where the application executable is.
178          * The application_config_directory can be different, see ConfigImpl::initDbFile() for details.
179          * The SQLITESTUDIO_PLUGINS variable can contain several paths, separated by : (for Unix/Mac) or ; (for Windows).
180          */
181         void scanPlugins();
182 
183         /**
184          * @brief Loads plugins defined in configuration.
185          *
186          * It loads all plugins that are available to the application
187          * and are not marked to not load in the configuration.
188          *
189          * In other words, every plugin will load by default, unless it was
190          * explicitly unloaded previously and that was saved in the configuration
191          * (when application was closing).
192          */
193         void loadPlugins();
194 
195         /**
196          * @brief Loads given plugin.
197          * @param pluginName Name of the plugin to load.
198          * @param alreadyAttempted List of plugin names that were already attempted to be loaded.
199          * @param minVersion Minimum required version of the plugin to load.
200          * @param maxVersion Maximum required version of the plugin to load.
201          * @return true on success, false on failure.
202          *
203          * This is pretty much what the public load() method does, except this one tracks what plugins were already
204          * attempted to be loaded (and failed), so it doesn't warn twice about the same plugin if it failed
205          * to load while it was a dependency for some other plugins.
206          *
207          * It also allows to define minimum and maximum plugin version, so if SQLiteStudio has the plugin available,
208          * but the version is out of required range, it will also fail to load.
209          */
210         bool load(const QString& pluginName, QStringList& alreadyAttempted, int minVersion = 0, int maxVersion = 0);
211 
212         /**
213          * @brief Executes standard routines after plugin was loaded.
214          * @param container Container for the loaded plugin.
215          *
216          * It fills all members of the plugin container and emits loaded() signal.
217          */
218         void pluginLoaded(PluginContainer* container);
219 
220         /**
221          * @brief Stores some specific plugin types in internal collections for faster access.
222          * @param plugin Plugin that was just loaded.
223          *
224          * This is called after we are sure we have a Plugin instance.
225          *
226          * The method stores certain plugin types in internal collections, so they can be accessed
227          * faster, instead of calling getLoadedPlugin<T>(), which is not as fast.
228          *
229          * The internal collections are used for plugins that are likely to be accessed frequently,
230          * like ScriptingPlugin.
231          */
232         void addPluginToCollections(Plugin* plugin);
233 
234         /**
235          * @brief Removes plugin from internal collections.
236          * @param plugin Plugin that is about to be unloaded.
237          *
238          * This is the reverse operation to what addPluginToCollections(Plugin*) does.
239          */
240         void removePluginFromCollections(Plugin* plugin);
241 
242         /**
243          * @brief Reads title, description, author, etc. from the plugin.
244          * @param plugin Plugin to read data from.
245          * @param container Container to put the data to.
246          * @return true on success, false on problems (with details in logs)
247          *
248          * It does the reading by calling all related methods from Plugin interface,
249          * then stores those information in given \p container.
250          *
251          * The built-in plugins define those methods using their class metadata.
252          *
253          * External plugins provide this information in their file metadata
254          * and this method uses QPluginLoader to read this metadata.
255          */
256         bool readMetaData(PluginContainer* container);
257 
258         /**
259          * @brief Creates plugin container and initializes it.
260          * @param loader Qt's plugin framework loader used to load this plugin.
261          *               For built-in plugins (statically linked) this must be null.
262          * @param fileName Plugin's file path. For built-in plugins it's ignored.
263          * @param plugin Plugin object from loaded plugin.
264          * @return true if the initialization succeeded, or false otherwise.
265          *
266          * It assigns plugin type to the plugin, creates plugin container and fills
267          * all necessary data for the plugin. If the plugin was configured to not load,
268          * then this method unloads the file, before plugin was initialized (with Plugin::init()).
269          *
270          * All plugins are loaded at the start, but before they are fully initialized
271          * and enabled, they are simply queried for metadata, then either unloaded
272          * (when configured to not load at startup), or the initialization proceeds.
273          */
274         bool initPlugin(QPluginLoader* loader, const QString& fileName);
275 
276         bool checkPluginRequirements(const QString& pluginName, const QJsonObject& metaObject);
277         bool readDependencies(const QString& pluginName, PluginContainer* container, const QJsonValue& depsValue);
278         bool readConflicts(const QString& pluginName, PluginContainer* container, const QJsonValue& confValue);
279 
280         /**
281          * @brief Creates plugin container and initializes it.
282          * @param plugin Built-in plugin object.
283          * @return true if the initialization succeeded, or false otherwise.
284          *
285          * This is pretty much the same as the other initPlugin() method, but this one is for built-in plugins.
286          */
287         bool initPlugin(Plugin* plugin);
288 
289         /**
290          * @brief Tests if given plugin is configured to be loaded at startup.
291          * @param plugin Tested plugin object.
292          * @return true if plugin should be loaded at startup, or false otherwise.
293          *
294          * This method checks General.LoadedPlugins configuration entry to see if plugin
295          * was explicitly disabled for loading at startup.
296          */
297         bool shouldAutoLoad(const QString& pluginName);
298 
299         /**
300          * @brief List of plugin directories (not necessarily absolute paths).
301          */
302         QStringList pluginDirs;
303 
304         /**
305          * @brief List of registered plugin types.
306          */
307         QList<PluginType*> registeredPluginTypes;
308 
309         /**
310          * @brief Table with plugin types as keys and list of plugins assigned for each type.
311          */
312         QHash<PluginType*,PluginContainerList> pluginCategories;
313 
314         /**
315          * @brief Table with plugin names and containers assigned for each plugin.
316          */
317         QHash<QString,PluginContainer*> pluginContainer;
318 
319         /**
320          * @brief Internal list of scripting plugins, updated on load/unload of plugins.
321          *
322          * Keys are scripting language name. It's a separate table to optimize querying scripting plugins.
323          */
324         QHash<QString,ScriptingPlugin*> scriptingPlugins;
325 
326         bool pluginsAreInitiallyLoaded = false;
327 };
328 
329 #endif // PLUGINMANAGERIMPL_H
330