1/* -*- c++ -*-
2 Copyright (c) 2008 Tobias Koenig <tokoe@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20#include "pluginloader_p.h"
21
22#include <kconfiggroup.h>
23#include <kdebug.h>
24#include <kglobal.h>
25#include <klocale.h>
26#include <klocalizedstring.h>
27#include <kstandarddirs.h>
28#include <KPluginLoader>
29
30#include <QtCore/QDebug>
31
32using namespace Akonadi;
33
34PluginMetaData::PluginMetaData()
35{
36}
37
38PluginMetaData::PluginMetaData(const QString &lib, const QString &name, const QString &comment, const QString &cname)
39 : library(lib)
40 , nameLabel(name)
41 , descriptionLabel(comment)
42 , className(cname)
43 , loaded(false)
44{
45}
46
47PluginLoader *PluginLoader::mSelf = 0;
48
49PluginLoader::PluginLoader()
50{
51 scan();
52}
53
54PluginLoader::~PluginLoader()
55{
56 qDeleteAll(mPluginLoaders);
57 mPluginLoaders.clear();
58}
59
60PluginLoader *PluginLoader::self()
61{
62 if (!mSelf) {
63 mSelf = new PluginLoader();
64 }
65
66 return mSelf;
67}
68
69QStringList PluginLoader::names() const
70{
71 return mPluginInfos.keys();
72}
73
74QObject *PluginLoader::createForName(const QString &name)
75{
76 if (!mPluginInfos.contains(name)) {
77 kWarning(5300) << "plugin name \"" << name << "\" is unknown to the plugin loader." << endl;
78 return 0;
79 }
80
81 PluginMetaData &info = mPluginInfos[name];
82
83 //First try to load it staticly
84 foreach (QObject *plugin, QPluginLoader::staticInstances()) {
85 if (QLatin1String(plugin->metaObject()->className()) == info.className) {
86 info.loaded = true;
87 return plugin;
88 break;
89 }
90 }
91
92 if (!info.loaded) {
93 KPluginLoader *loader = new KPluginLoader(info.library);
94 if (loader->fileName().isEmpty()) {
95 kWarning(5300) << loader->errorString();
96 delete loader;
97 return 0;
98 }
99
100 mPluginLoaders.insert(name, loader);
101 info.loaded = true;
102 }
103
104 QPluginLoader *loader = mPluginLoaders.value(name);
105 Q_ASSERT(loader);
106
107 QObject *object = loader->instance();
108 if (!object) {
109 kWarning(5300) << "unable to load plugin for plugin name \"" << name << "\"." << endl;
110 kWarning(5300) << "Error was:\"" << loader->errorString() << "\"." << endl;
111 return 0;
112 }
113
114 return object;
115}
116
117PluginMetaData PluginLoader::infoForName(const QString &name) const
118{
119 if (!mPluginInfos.contains(name)) {
120 return PluginMetaData();
121 }
122
123 return mPluginInfos.value(name);
124}
125
126void PluginLoader::scan()
127{
128 const QStringList list = KGlobal::dirs()->findAllResources("data", QLatin1String("akonadi/plugins/serializer/*.desktop"),
129 KStandardDirs::Recursive | KStandardDirs::NoDuplicates);
130 for (int i = 0; i < list.count(); ++i) {
131 const QString entry = list.at(i);
132
133 KConfig config(entry, KConfig::SimpleConfig);
134 if (config.hasGroup("Misc") && config.hasGroup("Plugin")) {
135 KConfigGroup group(&config, "Plugin");
136
137 const QString type = group.readEntry("Type").toLower();
138 if (type.isEmpty()) {
139 kWarning(5300) << "missing or empty [Plugin]Type value in \"" << entry << "\" - skipping" << endl;
140 continue;
141 }
142
143 // read Class entry as a list so that types like QPair<A,B> are
144 // properly escaped and don't end up being split into QPair<A
145 // and B>.
146 const QStringList classes = group.readXdgListEntry("X-Akonadi-Class");
147 if (classes.isEmpty()) {
148 kWarning(5300) << "missing or empty [Plugin]X-Akonadi-Class value in \"" << entry << "\" - skipping" << endl;
149 continue;
150 }
151
152 const QString library = group.readEntry("X-KDE-Library");
153 if (library.isEmpty()) {
154 kWarning(5300) << "missing or empty [Plugin]X-KDE-Library value in \"" << entry << "\" - skipping" << endl;
155 continue;
156 }
157
158 KConfigGroup group2(&config, "Misc");
159
160 QString name = group2.readEntry("Name");
161 if (name.isEmpty()) {
162 kWarning(5300) << "missing or empty [Misc]Name value in \"" << entry << "\" - inserting default name" << endl;
163 name = i18n("Unnamed plugin");
164 }
165
166 QString comment = group2.readEntry("Comment");
167 if (comment.isEmpty()) {
168 kWarning(5300) << "missing or empty [Misc]Comment value in \"" << entry << "\" - inserting default name" << endl;
169 comment = i18n("No description available");
170 }
171
172 QString cname = group.readEntry("X-KDE-ClassName");
173 if (cname.isEmpty()) {
174 kWarning(5300) << "missing or empty X-KDE-ClassName value in \"" << entry << "\"" << endl;
175 }
176
177 const QStringList mimeTypes = type.split(QLatin1Char(','), QString::SkipEmptyParts);
178
179 kDebug(5300) << "registering Desktop file" << entry << "for" << mimeTypes << '@' << classes;
180 Q_FOREACH (const QString &mimeType, mimeTypes) {
181 Q_FOREACH (const QString &classType, classes) {
182 mPluginInfos.insert(mimeType + QLatin1Char('@') + classType, PluginMetaData(library, name, comment, cname));
183 }
184 }
185
186 } else {
187 kWarning(5300) << "Desktop file \"" << entry << "\" doesn't seem to describe a plugin " << "(misses Misc and/or Plugin group)" << endl;
188 }
189 }
190}
191