1 /*!
2 * \copyright Copyright (c) 2015-2021 Governikus GmbH & Co. KG, Germany
3 */
4
5 #include "UILoader.h"
6
7 #include <QLoggingCategory>
8 #include <QPluginLoader>
9 #include <QThread>
10
11 Q_DECLARE_LOGGING_CATEGORY(gui)
12
13 using namespace governikus;
14
15 namespace
16 {
getPrefixUi()17 QString getPrefixUi()
18 {
19 return QStringLiteral("UIPlugIn");
20 }
21
22
23 } // namespace
24
25 QVector<UIPlugInName> governikus::UILoader::cDefault = UILoader::getInitialDefault();
26
27
UILoader()28 UILoader::UILoader()
29 : mLoadedPlugIns()
30 {
31 }
32
33
~UILoader()34 UILoader::~UILoader()
35 {
36 }
37
38
getInitialDefault()39 QVector<UIPlugInName> UILoader::getInitialDefault()
40 {
41 QVector<UIPlugInName> list({UIPlugInName::UIPlugInQml});
42
43 #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
44 list << UIPlugInName::UIPlugInWebSocket;
45 #endif
46
47 return list;
48 }
49
50
isLoaded() const51 bool UILoader::isLoaded() const
52 {
53 return !mLoadedPlugIns.isEmpty();
54 }
55
56
load()57 bool UILoader::load()
58 {
59 bool any = false;
60 for (auto entry : qAsConst(cDefault))
61 {
62 any = load(entry) || any;
63 }
64 return any;
65 }
66
67
load(UIPlugInName pUi)68 bool UILoader::load(UIPlugInName pUi)
69 {
70 Q_ASSERT(QObject::thread() == QThread::currentThread());
71
72 if (mLoadedPlugIns.contains(pUi))
73 {
74 return true;
75 }
76
77 const auto& name = getEnumName(pUi);
78 qCDebug(gui) << "Try to load UI plugin:" << name;
79
80 const auto& allPlugins = QPluginLoader::staticPlugins();
81 for (auto& plugin : allPlugins)
82 {
83 auto metaData = plugin.metaData();
84 if (isPlugIn(metaData) && hasName(metaData, name))
85 {
86 qCDebug(gui) << "Load plugin:" << metaData;
87 auto instance = qobject_cast<UIPlugIn*>(plugin.instance());
88 if (instance)
89 {
90 mLoadedPlugIns.insert(pUi, instance);
91 Q_EMIT fireLoadedPlugin(instance);
92 return true;
93 }
94 else
95 {
96 qCWarning(gui) << "Cannot cast to plugin instance:" << plugin.instance();
97 }
98 }
99 }
100
101 qCCritical(gui) << "Cannot find UI plugin:" << name;
102 return false;
103 }
104
105
getDefault()106 QStringList UILoader::getDefault()
107 {
108 QStringList list;
109 for (auto entry : qAsConst(cDefault))
110 {
111 list << getName(entry);
112 }
113 return list;
114 }
115
116
setDefault(const QStringList & pDefault)117 void UILoader::setDefault(const QStringList& pDefault)
118 {
119 QVector<UIPlugInName> selectedPlugins;
120 const auto& availablePlugins = Enum<UIPlugInName>::getList();
121
122 for (const auto& parsedUiOption : pDefault)
123 {
124 for (auto availablePluginEntry : availablePlugins)
125 {
126 if (parsedUiOption.compare(QString(getEnumName(availablePluginEntry)).remove(getPrefixUi()), Qt::CaseInsensitive) == 0)
127 {
128 selectedPlugins << availablePluginEntry;
129 }
130 }
131 }
132
133 if (!selectedPlugins.isEmpty())
134 {
135 cDefault = selectedPlugins;
136 }
137 }
138
139
getLoaded(UIPlugInName pName) const140 UIPlugIn* UILoader::getLoaded(UIPlugInName pName) const
141 {
142 return mLoadedPlugIns.value(pName);
143 }
144
145
shutdown()146 void UILoader::shutdown()
147 {
148 qCDebug(gui) << "Shutdown UILoader";
149 const QList<UIPlugInName> keys = mLoadedPlugIns.keys();
150 for (UIPlugInName key : keys)
151 {
152 UIPlugIn* const plugin = mLoadedPlugIns.value(key);
153
154 connect(plugin, &QObject::destroyed, this, [this, key] {
155 qCDebug(gui) << "Shutdown UI:" << key;
156 mLoadedPlugIns.remove(key);
157 if (mLoadedPlugIns.isEmpty())
158 {
159 Q_EMIT fireShutdownComplete();
160 }
161 }, Qt::QueuedConnection);
162
163 // Plugins and therefore their members are not auto destructed due to a bug in Qt.
164 // https://bugreports.qt.io/browse/QTBUG-17458
165 plugin->deleteLater();
166 }
167 }
168
169
hasName(const QJsonObject & pJson,const QString & pName) const170 bool UILoader::hasName(const QJsonObject& pJson, const QString& pName) const
171 {
172 return pJson.value(QStringLiteral("className")).toString() == pName;
173 }
174
175
getName(UIPlugInName pPlugin)176 QString UILoader::getName(UIPlugInName pPlugin)
177 {
178 return QString(getEnumName(pPlugin)).remove(getPrefixUi());
179 }
180
181
isPlugIn(const QJsonObject & pJson) const182 bool UILoader::isPlugIn(const QJsonObject& pJson) const
183 {
184 return pJson.value(QStringLiteral("IID")).toString() == QLatin1String("governikus.UIPlugIn");
185 }
186