1/*
2 This file is part of libkresources.
3
4 Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
5 Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
6 Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22*/
23/**
24 @file
25 This file is part of the KDE resource framework and defines the
26 Factory class.
27
28 @brief
29 A class for loading resource plugins.
30
31 @author Tobias Koenig
32 @author Jan-Pascal van Best
33 @author Cornelius Schumacher
34*/
35
36#include "factory.h"
37
38#include <QtCore/QFile>
39
40#include <kdebug.h>
41#include <klocalizedstring.h>
42#include <kconfig.h>
43#include <kconfiggroup.h>
44#include <kprocess.h>
45#include <kservicetypetrader.h>
46#include <kpluginloader.h>
47
48#include "resource.h"
49
50using namespace KRES;
51
52class Factory::Private
53{
54 public:
55 Resource *resourceInternal ( const QString &type, const KConfigGroup *group );
56 QString mResourceFamily;
57 QMap<QString, KService::Ptr> mTypeMap;
58};
59
60class FactoryMap : public QMap<QString, Factory*>
61{
62 public:
63 ~FactoryMap() { qDeleteAll( *this ); }
64};
65
66K_GLOBAL_STATIC( FactoryMap, mSelves )
67
68Factory *Factory::self( const QString &resourceFamily )
69{
70 kDebug();
71
72 Factory *factory = 0;
73
74 factory = mSelves->value( resourceFamily, 0 );
75
76 if ( !factory ) {
77 factory = new Factory( resourceFamily );
78 mSelves->insert( resourceFamily, factory );
79
80 // Akonadi migration
81 KConfig config( QLatin1String("kres-migratorrc") );
82 KConfigGroup migrationCfg( &config, "Migration" );
83 const bool enabled = migrationCfg.readEntry( "Enabled", false );
84 const bool setupClientBrige = migrationCfg.readEntry( "SetupClientBridge", true );
85 const int currentVersion = migrationCfg.readEntry( QLatin1String("Version-") + resourceFamily, 0 );
86 const int targetVersion = migrationCfg.readEntry( "TargetVersion", 0 );
87 if ( enabled && currentVersion < targetVersion ) {
88 kDebug() << "Performing Akonadi migration. Good luck!";
89 KProcess proc;
90 QStringList args = QStringList() << QLatin1String("--interactive-on-change") << QLatin1String("--type") << resourceFamily;
91 if ( !setupClientBrige ) {
92 args << QLatin1String("--omit-client-bridge");
93 }
94 proc.setProgram( QLatin1String("kres-migrator"), args );
95 proc.start();
96 bool result = proc.waitForStarted();
97 if ( result ) {
98 result = proc.waitForFinished();
99 }
100 if ( result && proc.exitCode() == 0 ) {
101 kDebug() << "Akonadi migration has been successful";
102 migrationCfg.writeEntry( QLatin1String("Version-") + resourceFamily, targetVersion );
103 migrationCfg.sync();
104 } else if ( !result || proc.exitCode() != 1 ) {
105 // exit code 1 means it is already running, so we are probably called by a migrator instance
106 kError() << "Akonadi migration failed!";
107 kError() << "command was: " << proc.program();
108 kError() << "exit code: " << proc.exitCode();
109 kError() << "stdout: " << proc.readAllStandardOutput();
110 kError() << "stderr: " << proc.readAllStandardError();
111 }
112 }
113
114 }
115
116 return factory;
117}
118
119Factory::Factory( const QString &resourceFamily ) :
120 d( new KRES::Factory::Private )
121{
122 d->mResourceFamily = resourceFamily;
123 reloadConfig();
124}
125
126void Factory::reloadConfig()
127{
128 d->mTypeMap.clear();
129 const KService::List plugins =
130 KServiceTypeTrader::self()->query(
131 QLatin1String("KResources/Plugin"),
132 QString::fromLatin1( "[X-KDE-ResourceFamily] == '%1'" ).arg( d->mResourceFamily ) );
133
134 KService::List::ConstIterator it;
135 for ( it = plugins.begin(); it != plugins.end(); ++it ) {
136 const QVariant type = ( *it )->property( QLatin1String("X-KDE-ResourceType") );
137 if ( !type.toString().isEmpty() ) {
138 d->mTypeMap.insert( type.toString(), *it );
139 }
140 }
141}
142
143Factory::~Factory()
144{
145 delete d;
146}
147
148QStringList Factory::typeNames() const
149{
150 return d->mTypeMap.keys();
151}
152
153ConfigWidget *Factory::configWidget( const QString &type, QWidget *parent )
154{
155 if ( type.isEmpty() || !d->mTypeMap.contains( type ) ) {
156 return 0;
157 }
158
159 KService::Ptr ptr = d->mTypeMap[ type ];
160 KPluginLoader loader( ptr->library() );
161 KPluginFactory *factory = loader.factory();
162 if ( !factory ) {
163 kDebug() << "Factory creation failed: " << loader.errorString();
164 return 0;
165 }
166
167 PluginFactoryBase *pluginFactory = static_cast<PluginFactoryBase *>( factory );
168
169 if ( !pluginFactory ) {
170 kDebug() << "no plugin factory.";
171 return 0;
172 }
173
174 ConfigWidget *wdg = pluginFactory->configWidget( parent );
175 if ( !wdg ) {
176 kDebug() << "'" << ptr->library() << "' doesn't provide a ConfigWidget";
177 return 0;
178 }
179
180 return wdg;
181}
182
183QString Factory::typeName( const QString &type ) const
184{
185 if ( type.isEmpty() || !d->mTypeMap.contains( type ) ) {
186 return QString();
187 }
188
189 KService::Ptr ptr = d->mTypeMap[ type ];
190 return ptr->name();
191}
192
193QString Factory::typeDescription( const QString &type ) const
194{
195 if ( type.isEmpty() || !d->mTypeMap.contains( type ) ) {
196 return QString();
197 }
198
199 KService::Ptr ptr = d->mTypeMap[ type ];
200 return ptr->comment();
201}
202
203Resource *Factory::Private::resourceInternal( const QString &type, const KConfigGroup *group )
204{
205 kDebug() << "(" << type << ", config )";
206
207 if ( type.isEmpty() || !mTypeMap.contains( type ) ) {
208 kDebug() << "no such type" << type;
209 return 0;
210 }
211
212 KService::Ptr ptr = mTypeMap[ type ];
213 KPluginLoader loader( ptr->library() );
214 KPluginFactory *factory = loader.factory();
215 if ( !factory ) {
216 kDebug() << "Factory creation failed" << loader.errorString();
217 return 0;
218 }
219
220 PluginFactoryBase *pluginFactory = static_cast<PluginFactoryBase *>( factory );
221
222 if ( !pluginFactory ) {
223 kDebug() << "no plugin factory.";
224 return 0;
225 }
226
227 Resource *resource;
228 if ( group ) {
229 resource = pluginFactory->resource( *group );
230 } else {
231 resource = pluginFactory->resource();
232 }
233
234 if ( !resource ) {
235 kDebug() << "'" << ptr->library()
236 << "' is not a" << mResourceFamily << "plugin.";
237 return 0;
238 }
239
240 resource->setType( type );
241
242 return resource;
243}
244
245Resource *Factory::resource( const QString &type, const KConfigGroup &group )
246{
247 return d->resourceInternal( type, &group );
248}
249
250Resource *Factory::resource( const QString &type )
251{
252 return d->resourceInternal( type, 0 );
253}
254