1 #include "pluginmanagerimpl.h"
2 #include "plugins/scriptingplugin.h"
3 #include "plugins/genericplugin.h"
4 #include "services/notifymanager.h"
5 #include "common/unused.h"
6 #include "translations.h"
7 #include <QCoreApplication>
8 #include <QDir>
9 #include <QDebug>
10 #include <QJsonArray>
11 #include <QJsonValue>
12 
PluginManagerImpl()13 PluginManagerImpl::PluginManagerImpl()
14 {
15 }
16 
~PluginManagerImpl()17 PluginManagerImpl::~PluginManagerImpl()
18 {
19 }
20 
init()21 void PluginManagerImpl::init()
22 {
23     if (getDistributionType() != DistributionType::OS_MANAGED)
24         pluginDirs += qApp->applicationDirPath() + "/plugins";
25 
26     pluginDirs += "/usr/local/lib/sqlitestudio";
27 
28     QString envDirs = SQLITESTUDIO->getEnv("SQLITESTUDIO_PLUGINS");
29     if (!envDirs.isNull())
30         pluginDirs += envDirs.split(PATH_LIST_SEPARATOR);
31 
32 #ifdef PLUGINS_DIR
33     pluginDirs += STRINGIFY(PLUGINS_DIR);
34 #endif
35 
36 #ifdef SYS_PLUGINS_DIR
37     pluginDirs += STRINGIFY(SYS_PLUGINS_DIR);
38 #endif
39 
40 #ifdef Q_OS_MACX
41     pluginDirs += QCoreApplication::applicationDirPath()+"/../PlugIns";
42 #endif
43 
44     scanPlugins();
45     loadPlugins();
46 }
47 
deinit()48 void PluginManagerImpl::deinit()
49 {
50     emit aboutToQuit();
51 
52     // Plugin containers and their plugins
53     for (PluginContainer* container : pluginContainer.values())
54     {
55         if (container->builtIn)
56         {
57             container->plugin->deinit();
58             delete container->plugin;
59         }
60         else
61             unload(container->name);
62     }
63 
64     for (PluginContainer* container : pluginContainer.values())
65         delete container;
66 
67     pluginContainer.clear();
68 
69     // Types
70     for (PluginType* type : registeredPluginTypes)
71         delete type;
72 
73     registeredPluginTypes.clear();
74     pluginCategories.clear();
75 }
76 
getPluginTypes() const77 QList<PluginType*> PluginManagerImpl::getPluginTypes() const
78 {
79     return registeredPluginTypes;
80 }
81 
getPluginDirs() const82 QStringList PluginManagerImpl::getPluginDirs() const
83 {
84     return pluginDirs;
85 }
86 
getFilePath(Plugin * plugin) const87 QString PluginManagerImpl::getFilePath(Plugin* plugin) const
88 {
89     if (!pluginContainer.contains(plugin->getName()))
90         return QString();
91 
92     return pluginContainer[plugin->getName()]->filePath;
93 }
94 
loadBuiltInPlugin(Plugin * plugin)95 bool PluginManagerImpl::loadBuiltInPlugin(Plugin* plugin)
96 {
97     bool res = initPlugin(plugin);
98     res &= plugin->init();
99     return res;
100 }
101 
getPluginType(Plugin * plugin) const102 PluginType* PluginManagerImpl::getPluginType(Plugin* plugin) const
103 {
104     if (!pluginContainer.contains(plugin->getName()))
105         return nullptr;
106 
107     return pluginContainer[plugin->getName()]->type;
108 }
109 
scanPlugins()110 void PluginManagerImpl::scanPlugins()
111 {
112     QStringList nameFilters;
113     nameFilters << "*.so" << "*.dll" << "*.dylib";
114 
115     QPluginLoader* loader = nullptr;
116     for (QString pluginDirPath : pluginDirs)
117     {
118         QDir pluginDir(pluginDirPath);
119         for (QString fileName : pluginDir.entryList(nameFilters, QDir::Files))
120         {
121             fileName = pluginDir.absoluteFilePath(fileName);
122             loader = new QPluginLoader(fileName);
123             loader->setLoadHints(QLibrary::ExportExternalSymbolsHint|QLibrary::ResolveAllSymbolsHint);
124 
125             if (!initPlugin(loader, fileName))
126             {
127                 qDebug() << "File" << fileName << "was loaded as plugin, but SQLiteStudio couldn't initialize plugin.";
128                 delete loader;
129             }
130         }
131     }
132 
133     QStringList names;
134     for (PluginContainer* container : pluginContainer.values())
135     {
136         if (!container->builtIn)
137             names << container->name;
138     }
139 
140     qDebug() << "Following plugins found:" << names;
141 }
142 
loadPlugins()143 void PluginManagerImpl::loadPlugins()
144 {
145     QStringList alreadyAttempted;
146     for (const QString& pluginName : pluginContainer.keys())
147     {
148         if (shouldAutoLoad(pluginName))
149             load(pluginName, alreadyAttempted);
150     }
151 
152     pluginsAreInitiallyLoaded = true;
153     emit pluginsInitiallyLoaded();
154 }
155 
initPlugin(QPluginLoader * loader,const QString & fileName)156 bool PluginManagerImpl::initPlugin(QPluginLoader* loader, const QString& fileName)
157 {
158     QJsonObject pluginMetaData = loader->metaData();
159     QString pluginTypeName = pluginMetaData.value("MetaData").toObject().value("type").toString();
160     PluginType* pluginType = nullptr;
161     for (PluginType* type : registeredPluginTypes)
162     {
163         if (type->getName() == pluginTypeName)
164         {
165             pluginType = type;
166             break;
167         }
168     }
169 
170     if (!pluginType)
171     {
172         qWarning() << "Could not load plugin" << fileName << "because its type was not recognized:" << pluginTypeName;
173         return false;
174     }
175 
176     QString pluginName = pluginMetaData.value("className").toString();
177     QJsonObject metaObject = pluginMetaData.value("MetaData").toObject();
178 
179     if (!checkPluginRequirements(pluginName, metaObject))
180         return false;
181 
182     PluginContainer* container = new PluginContainer;
183     container->type = pluginType;
184     container->filePath = fileName;
185     container->loaded = false;
186     container->loader = loader;
187     pluginCategories[pluginType] << container;
188     pluginContainer[pluginName] = container;
189 
190     if (!readDependencies(pluginName, container, metaObject.value("dependencies")))
191         return false;
192 
193     if (!readConflicts(pluginName, container, metaObject.value("conflicts")))
194         return false;
195 
196     if (!readMetaData(container))
197     {
198         delete container;
199         return false;
200     }
201 
202     return true;
203 }
204 
checkPluginRequirements(const QString & pluginName,const QJsonObject & metaObject)205 bool PluginManagerImpl::checkPluginRequirements(const QString& pluginName, const QJsonObject& metaObject)
206 {
207     if (metaObject.value("gui").toBool(false) && !SQLITESTUDIO->isGuiAvailable())
208     {
209         qDebug() << "Plugin" << pluginName << "skipped, because it requires GUI and this is not GUI client running.";
210         return false;
211     }
212 
213     int minVer = metaObject.value("minQtVersion").toInt(0);
214     if (QT_VERSION_CHECK(minVer / 10000, minVer / 100 % 100, minVer % 10000) > QT_VERSION)
215     {
216         qDebug() << "Plugin" << pluginName << "skipped, because it requires at least Qt version" << toPrintableVersion(minVer) << ", but got" << QT_VERSION_STR;
217         return false;
218     }
219 
220     int maxVer = metaObject.value("maxQtVersion").toInt(999999);
221     if (QT_VERSION_CHECK(maxVer / 10000, maxVer / 100 % 100, maxVer % 10000) < QT_VERSION)
222     {
223         qDebug() << "Plugin" << pluginName << "skipped, because it requires at most Qt version" << toPrintableVersion(maxVer) << ", but got" << QT_VERSION_STR;
224         return false;
225     }
226 
227     minVer = metaObject.value("minAppVersion").toInt(0);
228     if (SQLITESTUDIO->getVersion() < minVer)
229     {
230         qDebug() << "Plugin" << pluginName << "skipped, because it requires at least SQLiteStudio version" << toPrintableVersion(minVer) << ", but got"
231                  << SQLITESTUDIO->getVersionString();
232         return false;
233     }
234 
235     maxVer = metaObject.value("maxAppVersion").toInt(999999);
236     if (SQLITESTUDIO->getVersion() > maxVer)
237     {
238         qDebug() << "Plugin" << pluginName << "skipped, because it requires at most SQLiteStudio version" << toPrintableVersion(maxVer) << ", but got"
239                  << SQLITESTUDIO->getVersionString();
240         return false;
241     }
242 
243     return true;
244 }
245 
readDependencies(const QString & pluginName,PluginManagerImpl::PluginContainer * container,const QJsonValue & depsValue)246 bool PluginManagerImpl::readDependencies(const QString& pluginName, PluginManagerImpl::PluginContainer* container, const QJsonValue& depsValue)
247 {
248     if (depsValue.isUndefined())
249         return true;
250 
251     QJsonArray depsArray;
252     if (depsValue.type() == QJsonValue::Array)
253         depsArray = depsValue.toArray();
254     else
255         depsArray.append(depsValue);
256 
257     PluginDependency dep;
258     QJsonObject depObject;
259     for (const QJsonValue& value : depsArray)
260     {
261         if (value.type() == QJsonValue::Object)
262         {
263             depObject = value.toObject();
264             if (!depObject.contains("name"))
265             {
266                 qWarning() << "Invalid dependency entry in plugin" << pluginName << " - doesn't contain 'name' of the dependency.";
267                 return false;
268             }
269 
270             dep.name = depObject.value("name").toString();
271             dep.minVersion = depObject.value("minVersion").toInt(0);
272             dep.maxVersion = depObject.value("maxVersion").toInt(0);
273         }
274         else
275         {
276             dep.maxVersion = 0;
277             dep.minVersion = 0;
278             dep.name = value.toString();
279         }
280         container->dependencies << dep;
281     }
282     return true;
283 }
284 
readConflicts(const QString & pluginName,PluginManagerImpl::PluginContainer * container,const QJsonValue & confValue)285 bool PluginManagerImpl::readConflicts(const QString& pluginName, PluginManagerImpl::PluginContainer* container, const QJsonValue& confValue)
286 {
287     UNUSED(pluginName);
288 
289     if (confValue.isUndefined())
290         return true;
291 
292     QJsonArray confArray;
293     if (confValue.type() == QJsonValue::Array)
294         confArray = confValue.toArray();
295     else
296         confArray.append(confValue);
297 
298     for (const QJsonValue& value : confArray)
299         container->conflicts << value.toString();
300 
301     return true;
302 }
303 
initPlugin(Plugin * plugin)304 bool PluginManagerImpl::initPlugin(Plugin* plugin)
305 {
306     QString pluginName = plugin->getName();
307     PluginType* pluginType = nullptr;
308     for (PluginType* type : registeredPluginTypes)
309     {
310         if (type->test(plugin))
311         {
312             pluginType = type;
313             break;
314         }
315     }
316 
317     if (!pluginType)
318     {
319         qWarning() << "Could not load built-in plugin" << pluginName << "because its type was not recognized.";
320         return false;
321     }
322 
323     PluginContainer* container = new PluginContainer;
324     container->type = pluginType;
325     container->loaded = true;
326     container->builtIn = true;
327     container->plugin = plugin;
328     pluginCategories[pluginType] << container;
329     pluginContainer[pluginName] = container;
330     if (!readMetaData(container))
331     {
332         delete container;
333         return false;
334     }
335 
336     pluginLoaded(container);
337     return true;
338 }
339 
shouldAutoLoad(const QString & pluginName)340 bool PluginManagerImpl::shouldAutoLoad(const QString& pluginName)
341 {
342     QStringList loadedPlugins = CFG_CORE.General.LoadedPlugins.get().split(",",
343 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
344                                                                            Qt::SkipEmptyParts
345 #else
346                                                                            QString::SkipEmptyParts
347 #endif
348                                                                            );
349     QStringList pair;
350     for (const QString& loadedPlugin : loadedPlugins)
351     {
352         pair = loadedPlugin.split("=");
353         if (pair.size() != 2)
354         {
355             qWarning() << "Invalid entry in config General.LoadedPlugins:" << loadedPlugin;
356             continue;
357         }
358 
359         if (pair[0] == pluginName)
360             return (bool)pair[1].toInt();
361     }
362 
363     return pluginContainer[pluginName]->loadByDefault;
364 }
365 
getAllPluginNames(PluginType * type) const366 QStringList PluginManagerImpl::getAllPluginNames(PluginType* type) const
367 {
368     QStringList names;
369     if (!pluginCategories.contains(type))
370         return names;
371 
372     for (PluginContainer* container : pluginCategories[type])
373         names << container->name;
374 
375     return names;
376 }
377 
getAllPluginNames() const378 QStringList PluginManagerImpl::getAllPluginNames() const
379 {
380     return pluginContainer.keys();
381 }
382 
getPluginType(const QString & pluginName) const383 PluginType* PluginManagerImpl::getPluginType(const QString& pluginName) const
384 {
385     if (!pluginContainer.contains(pluginName))
386         return nullptr;
387 
388     return pluginContainer[pluginName]->type;
389 }
390 
getAuthor(const QString & pluginName) const391 QString PluginManagerImpl::getAuthor(const QString& pluginName) const
392 {
393     if (!pluginContainer.contains(pluginName))
394         return QString();
395 
396     return pluginContainer[pluginName]->author;
397 }
398 
getTitle(const QString & pluginName) const399 QString PluginManagerImpl::getTitle(const QString& pluginName) const
400 {
401     if (!pluginContainer.contains(pluginName))
402         return QString();
403 
404     return pluginContainer[pluginName]->title;
405 }
406 
getPrintableVersion(const QString & pluginName) const407 QString PluginManagerImpl::getPrintableVersion(const QString& pluginName) const
408 {
409     if (!pluginContainer.contains(pluginName))
410         return QString();
411 
412     return pluginContainer[pluginName]->printableVersion;
413 }
414 
getVersion(const QString & pluginName) const415 int PluginManagerImpl::getVersion(const QString& pluginName) const
416 {
417     if (!pluginContainer.contains(pluginName))
418         return 0;
419 
420     return pluginContainer[pluginName]->version;
421 }
422 
getDescription(const QString & pluginName) const423 QString PluginManagerImpl::getDescription(const QString& pluginName) const
424 {
425     if (!pluginContainer.contains(pluginName))
426         return QString();
427 
428     return pluginContainer[pluginName]->description;
429 }
430 
unload(Plugin * plugin)431 void PluginManagerImpl::unload(Plugin* plugin)
432 {
433     if (!plugin)
434         return;
435 
436     unload(plugin->getName());
437 }
438 
unload(const QString & pluginName)439 void PluginManagerImpl::unload(const QString& pluginName)
440 {
441     if (!pluginContainer.contains(pluginName))
442     {
443         qWarning() << "No such plugin in containers:" << pluginName << "while trying to unload plugin.";
444         return;
445     }
446 
447     // Checking preconditions
448     PluginContainer* container = pluginContainer[pluginName];
449     if (container->builtIn)
450         return;
451 
452     if (!container->loaded)
453         return;
454 
455     // Unloading depdendent plugins
456     for (PluginContainer* otherContainer : pluginContainer.values())
457     {
458         if (otherContainer == container)
459             continue;
460 
461         for (const PluginDependency& dep : otherContainer->dependencies)
462         {
463             if (dep.name == pluginName)
464             {
465                 unload(otherContainer->name);
466                 break;
467             }
468         }
469     }
470 
471     // Removing from fast-lookup collections
472     removePluginFromCollections(container->plugin);
473 
474     // Deinitializing and unloading plugin
475     emit aboutToUnload(container->plugin, container->type);
476     container->plugin->deinit();
477     unloadTranslation(container->name);
478 
479     QPluginLoader* loader = container->loader;
480     if (!loader->isLoaded())
481     {
482         qWarning() << "QPluginLoader says the plugin is not loaded. Weird.";
483         emit unloaded(container->name, container->type);
484         return;
485     }
486 
487     loader->unload();
488 
489     container->plugin = nullptr;
490     container->loaded = false;
491 
492     emit unloaded(container->name, container->type);
493 
494     qDebug() << pluginName << "unloaded:" << container->filePath;
495 }
496 
load(const QString & pluginName)497 bool PluginManagerImpl::load(const QString& pluginName)
498 {
499     QStringList alreadyAttempted;
500     bool res = load(pluginName, alreadyAttempted);
501     if (!res)
502         emit failedToLoad(pluginName);
503 
504     return res;
505 }
506 
load(const QString & pluginName,QStringList & alreadyAttempted,int minVersion,int maxVersion)507 bool PluginManagerImpl::load(const QString& pluginName, QStringList& alreadyAttempted, int minVersion, int maxVersion)
508 {
509     if (alreadyAttempted.contains(pluginName))
510         return false;
511 
512     // Checking initial conditions
513     if (!pluginContainer.contains(pluginName))
514     {
515         qWarning() << "No such plugin in containers:" << pluginName << "while trying to load plugin.";
516         alreadyAttempted.append(pluginName);
517         return false;
518     }
519 
520     PluginContainer* container = pluginContainer[pluginName];
521 
522     if (minVersion > 0 && container->version < minVersion)
523     {
524         qWarning() << "Requested plugin" << pluginName << "in version at least" << minVersion << "but have:" << container->version;
525         return false;
526     }
527 
528     if (maxVersion > 0 && container->version > maxVersion)
529     {
530         qWarning() << "Requested plugin" << pluginName << "in version at most" << maxVersion << "but have:" << container->version;
531         return false;
532     }
533 
534     if (container->builtIn)
535         return true;
536 
537     QPluginLoader* loader = container->loader;
538     if (loader->isLoaded())
539         return true;
540 
541     // Checking for conflicting plugins
542     for (PluginContainer* otherContainer : pluginContainer.values())
543     {
544         if (!otherContainer->loaded || otherContainer->name == pluginName)
545             continue;
546 
547         if (container->conflicts.contains(otherContainer->name) || otherContainer->conflicts.contains(pluginName))
548         {
549             notifyWarn(tr("Cannot load plugin %1, because it's in conflict with plugin %2.").arg(pluginName, otherContainer->name));
550             alreadyAttempted.append(pluginName);
551             return false;
552         }
553     }
554 
555     // Loading depended plugins
556     for (const PluginDependency& dep : container->dependencies)
557     {
558         if (!load(dep.name, alreadyAttempted, dep.minVersion, dep.maxVersion))
559         {
560             notifyWarn(tr("Cannot load plugin %1, because its dependency was not loaded: %2.").arg(pluginName, dep.name));
561             alreadyAttempted.append(pluginName);
562             return false;
563         }
564     }
565 
566     // Loading pluginName
567     if (!loader->load())
568     {
569         notifyWarn(tr("Cannot load plugin %1. Error details: %2").arg(pluginName, loader->errorString()));
570         alreadyAttempted.append(pluginName);
571         return false;
572     }
573 
574     // Initializing loaded plugin
575     Plugin* plugin = dynamic_cast<Plugin*>(container->loader->instance());
576     GenericPlugin* genericPlugin = dynamic_cast<GenericPlugin*>(plugin);
577     if (genericPlugin)
578     {
579         genericPlugin->loadMetaData(container->loader->metaData());
580     }
581 
582     if (!plugin->init())
583     {
584         loader->unload();
585         notifyWarn(tr("Cannot load plugin %1 (error while initializing plugin).").arg(pluginName));
586         alreadyAttempted.append(pluginName);
587         return false;
588     }
589 
590     pluginLoaded(container);
591 
592     return true;
593 }
594 
pluginLoaded(PluginManagerImpl::PluginContainer * container)595 void PluginManagerImpl::pluginLoaded(PluginManagerImpl::PluginContainer* container)
596 {
597     if (!container->builtIn)
598     {
599         loadTranslation(container->name);
600         container->plugin = dynamic_cast<Plugin*>(container->loader->instance());
601         container->loaded = true;
602     }
603     addPluginToCollections(container->plugin);
604 
605     emit loaded(container->plugin, container->type);
606     if (!container->builtIn)
607         qDebug() << container->name << "loaded:" << container->filePath;
608 }
609 
addPluginToCollections(Plugin * plugin)610 void PluginManagerImpl::addPluginToCollections(Plugin* plugin)
611 {
612     ScriptingPlugin* scriptingPlugin = dynamic_cast<ScriptingPlugin*>(plugin);
613     if (scriptingPlugin)
614         scriptingPlugins[scriptingPlugin->getLanguage()] = scriptingPlugin;
615 }
616 
removePluginFromCollections(Plugin * plugin)617 void PluginManagerImpl::removePluginFromCollections(Plugin* plugin)
618 {
619     ScriptingPlugin* scriptingPlugin = dynamic_cast<ScriptingPlugin*>(plugin);
620     if (scriptingPlugin && scriptingPlugins.contains(scriptingPlugin->getLanguage()))
621         scriptingPlugins.remove(plugin->getName());
622 }
623 
readMetaData(PluginManagerImpl::PluginContainer * container)624 bool PluginManagerImpl::readMetaData(PluginManagerImpl::PluginContainer* container)
625 {
626     if (container->loader)
627     {
628         QHash<QString, QVariant> metaData = readMetaData(container->loader->metaData());
629         container->name = metaData["name"].toString();
630         container->version = metaData["version"].toInt();
631         container->printableVersion = toPrintableVersion(metaData["version"].toInt());
632         container->author = metaData["author"].toString();
633         container->description = metaData["description"].toString();
634         container->title = metaData["title"].toString();
635         container->loadByDefault = metaData.contains("loadByDefault") ? metaData["loadByDefault"].toBool() : true;
636     }
637     else if (container->plugin)
638     {
639         container->name = container->plugin->getName();
640         container->version = container->plugin->getVersion();
641         container->printableVersion = container->plugin->getPrintableVersion();
642         container->author = container->plugin->getAuthor();
643         container->description = container->plugin->getDescription();
644         container->title = container->plugin->getTitle();
645         container->loadByDefault = true;
646     }
647     else
648     {
649         qCritical() << "Could not read metadata for some plugin. It has no loader or plugin object defined.";
650         return false;
651     }
652     return true;
653 }
654 
isLoaded(const QString & pluginName) const655 bool PluginManagerImpl::isLoaded(const QString& pluginName) const
656 {
657     if (!pluginContainer.contains(pluginName))
658     {
659         qWarning() << "No such plugin in containers:" << pluginName << "while trying to get plugin 'loaded' status.";
660         return false;
661     }
662 
663     return pluginContainer[pluginName]->loaded;
664 }
665 
isBuiltIn(const QString & pluginName) const666 bool PluginManagerImpl::isBuiltIn(const QString& pluginName) const
667 {
668     if (!pluginContainer.contains(pluginName))
669     {
670         qWarning() << "No such plugin in containers:" << pluginName << "while trying to get plugin 'builtIn' status.";
671         return false;
672     }
673 
674     return pluginContainer[pluginName]->builtIn;
675 }
676 
getLoadedPlugin(const QString & pluginName) const677 Plugin* PluginManagerImpl::getLoadedPlugin(const QString& pluginName) const
678 {
679     if (!pluginContainer.contains(pluginName))
680         return nullptr;
681 
682     if (!pluginContainer[pluginName]->loaded)
683         return nullptr;
684 
685     return pluginContainer[pluginName]->plugin;
686 }
687 
getLoadedPlugins(PluginType * type) const688 QList<Plugin*> PluginManagerImpl::getLoadedPlugins(PluginType* type) const
689 {
690     QList<Plugin*> list;
691     if (!pluginCategories.contains(type))
692         return list;
693 
694     for (PluginContainer* container : pluginCategories[type])
695     {
696         if (container->loaded)
697             list << container->plugin;
698     }
699 
700     return list;
701 }
702 
getScriptingPlugin(const QString & languageName) const703 ScriptingPlugin* PluginManagerImpl::getScriptingPlugin(const QString& languageName) const
704 {
705     if (scriptingPlugins.contains(languageName))
706         return scriptingPlugins[languageName];
707 
708     return nullptr;
709 }
710 
readMetaData(const QJsonObject & metaData)711 QHash<QString, QVariant> PluginManagerImpl::readMetaData(const QJsonObject& metaData)
712 {
713     QHash<QString, QVariant> results;
714     results["name"] = metaData.value("className").toString();
715 
716     QJsonObject root = metaData.value("MetaData").toObject();
717     for (const QString& k : root.keys()) {
718         results[k] = root.value(k).toVariant();
719     }
720     return results;
721 }
722 
toPrintableVersion(int version) const723 QString PluginManagerImpl::toPrintableVersion(int version) const
724 {
725     static const QString versionStr = QStringLiteral("%1.%2.%3");
726     return versionStr.arg(version / 10000)
727                      .arg(version / 100 % 100)
728                      .arg(version % 100);
729 }
730 
getDependencies(const QString & pluginName) const731 QStringList PluginManagerImpl::getDependencies(const QString& pluginName) const
732 {
733     if (!pluginContainer.contains(pluginName))
734         return QStringList();
735 
736     static const QString verTpl = QStringLiteral(" (%1)");
737     QString minVerTpl = tr("min: %1", "plugin dependency version");
738     QString maxVerTpl = tr("max: %1", "plugin dependency version");
739     QStringList outputList;
740     QString depStr;
741     QStringList depVerList;
742     for (const PluginDependency& dep : pluginContainer[pluginName]->dependencies)
743     {
744         depStr = dep.name;
745         if (dep.minVersion > 0 || dep.maxVersion > 0)
746         {
747             depVerList.clear();
748             if (dep.minVersion > 0)
749                 depVerList << minVerTpl.arg(toPrintableVersion(dep.minVersion));
750 
751             if (dep.maxVersion > 0)
752                 depVerList << minVerTpl.arg(toPrintableVersion(dep.maxVersion));
753 
754             depStr += verTpl.arg(depVerList.join(", "));
755         }
756         outputList << depStr;
757     }
758 
759     return outputList;
760 }
761 
getConflicts(const QString & pluginName) const762 QStringList PluginManagerImpl::getConflicts(const QString& pluginName) const
763 {
764     if (!pluginContainer.contains(pluginName))
765         return QStringList();
766 
767     return pluginContainer[pluginName]->conflicts;
768 }
769 
arePluginsInitiallyLoaded() const770 bool PluginManagerImpl::arePluginsInitiallyLoaded() const
771 {
772     return pluginsAreInitiallyLoaded;
773 }
774 
getLoadedPlugins() const775 QList<Plugin*> PluginManagerImpl::getLoadedPlugins() const
776 {
777     QList<Plugin*> plugins;
778     for (PluginContainer* container : pluginContainer.values())
779     {
780         if (container->loaded)
781             plugins << container->plugin;
782     }
783     return plugins;
784 }
785 
getLoadedPluginNames() const786 QStringList PluginManagerImpl::getLoadedPluginNames() const
787 {
788     QStringList names;
789     for (PluginContainer* container : pluginContainer.values())
790     {
791         if (container->loaded)
792             names << container->name;
793     }
794     return names;
795 }
796 
getAllPluginDetails() const797 QList<PluginManager::PluginDetails> PluginManagerImpl::getAllPluginDetails() const
798 {
799     QList<PluginManager::PluginDetails> results;
800     PluginManager::PluginDetails details;
801     for (PluginContainer* container : pluginContainer.values())
802     {
803         details.name = container->name;
804         details.title = container->title;
805         details.description = container->description;
806         details.builtIn = container->builtIn;
807         details.version = container->version;
808         details.filePath = container->filePath;
809         details.versionString = formatVersion(container->version);
810         results << details;
811     }
812     return results;
813 }
814 
getLoadedPluginDetails() const815 QList<PluginManager::PluginDetails> PluginManagerImpl::getLoadedPluginDetails() const
816 {
817     QList<PluginManager::PluginDetails> results = getAllPluginDetails();
818     QMutableListIterator<PluginManager::PluginDetails> it(results);
819     while (it.hasNext())
820     {
821         if (!isLoaded(it.next().name))
822             it.remove();
823     }
824     return results;
825 }
826 
registerPluginType(PluginType * type)827 void PluginManagerImpl::registerPluginType(PluginType* type)
828 {
829     registeredPluginTypes << type;
830 }
831