1/*
2 Copyright (c) 2009 Constantin Berzan <exit3219@gmail.com>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 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 the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#include "specialcollectionsrequestjob.h"
21
22#include "specialcollectionattribute_p.h"
23#include "specialcollections.h"
24#include "specialcollections_p.h"
25#include "specialcollectionshelperjobs_p.h"
26
27#include "akonadi/agentmanager.h"
28#include "akonadi/collectioncreatejob.h"
29#include "akonadi/entitydisplayattribute.h"
30
31#include <KDebug>
32
33#include <QtCore/QVariant>
34
35using namespace Akonadi;
36
37/**
38 @internal
39*/
40class Akonadi::SpecialCollectionsRequestJobPrivate
41{
42public:
43 SpecialCollectionsRequestJobPrivate(SpecialCollections *collections, SpecialCollectionsRequestJob *qq);
44
45 bool isEverythingReady();
46 void lockResult(KJob *job); // slot
47 void releaseLock(); // slot
48 void nextResource();
49 void resourceScanResult(KJob *job); // slot
50 void createRequestedFolders(ResourceScanJob *job, QHash<QByteArray, bool> &requestedFolders);
51 void collectionCreateResult(KJob *job); // slot
52
53 SpecialCollectionsRequestJob *q;
54 SpecialCollections *mSpecialCollections;
55 int mPendingCreateJobs;
56
57 QByteArray mRequestedType;
58 AgentInstance mRequestedResource;
59
60 // Input:
61 QHash<QByteArray, bool> mDefaultFolders;
62 bool mRequestingDefaultFolders;
63 QHash< QString, QHash<QByteArray, bool> > mFoldersForResource;
64 QString mDefaultResourceType;
65 QVariantMap mDefaultResourceOptions;
66 QList<QByteArray> mKnownTypes;
67 QMap<QByteArray, QString> mNameForTypeMap;
68 QMap<QByteArray, QString> mIconForTypeMap;
69
70 // Output:
71 QStringList mToForget;
72 QVector< QPair<Collection, QByteArray> > mToRegister;
73};
74
75SpecialCollectionsRequestJobPrivate::SpecialCollectionsRequestJobPrivate(SpecialCollections *collections,
76 SpecialCollectionsRequestJob *qq)
77 : q(qq)
78 , mSpecialCollections(collections)
79 , mPendingCreateJobs(0)
80 , mRequestingDefaultFolders(false)
81{
82}
83
84bool SpecialCollectionsRequestJobPrivate::isEverythingReady()
85{
86 // check if all requested folders are known already
87 if (mRequestingDefaultFolders) {
88 QHashIterator<QByteArray, bool> it(mDefaultFolders);
89 while (it.hasNext()) {
90 it.next();
91 if (it.value() && !mSpecialCollections->hasDefaultCollection(it.key())) {
92 return false;
93 }
94 }
95 }
96
97 const QStringList resourceIds = mFoldersForResource.keys();
98 QHashIterator<QString, QHash<QByteArray, bool> > resourceIt(mFoldersForResource);
99 while (resourceIt.hasNext()) {
100 resourceIt.next();
101
102 const QHash<QByteArray, bool> &requested = resourceIt.value();
103 QHashIterator<QByteArray, bool> it(requested);
104 while (it.hasNext()) {
105 it.next();
106 if (it.value() && !mSpecialCollections->hasCollection(it.key(), AgentManager::self()->instance(resourceIt.key()))) {
107 return false;
108 }
109 }
110 }
111
112 return true;
113}
114
115void SpecialCollectionsRequestJobPrivate::lockResult(KJob *job)
116{
117 if (job->error()) {
118 kWarning() << "Failed to get lock:" << job->errorString();
119 q->setError(job->error());
120 q->setErrorText(job->errorString());
121 q->emitResult();
122 return;
123 }
124
125 if (mRequestingDefaultFolders) {
126 // If default folders are requested, deal with that first.
127 DefaultResourceJob *resjob = new DefaultResourceJob(mSpecialCollections->d->mSettings, q);
128 resjob->setDefaultResourceType(mDefaultResourceType);
129 resjob->setDefaultResourceOptions(mDefaultResourceOptions);
130 resjob->setTypes(mKnownTypes);
131 resjob->setNameForTypeMap(mNameForTypeMap);
132 resjob->setIconForTypeMap(mIconForTypeMap);
133 QObject::connect(resjob, SIGNAL(result(KJob*)), q, SLOT(resourceScanResult(KJob*)));
134 } else {
135 // If no default folders are requested, go straight to the next step.
136 nextResource();
137 }
138}
139
140void SpecialCollectionsRequestJobPrivate::releaseLock()
141{
142 const bool ok = Akonadi::releaseLock();
143 if (!ok) {
144 kWarning() << "WTF, can't release lock.";
145 }
146}
147
148void SpecialCollectionsRequestJobPrivate::nextResource()
149{
150 if (mFoldersForResource.isEmpty()) {
151 kDebug() << "All done! Comitting.";
152
153 mSpecialCollections->d->beginBatchRegister();
154
155 // Forget everything we knew before about these resources.
156 foreach (const QString &resourceId, mToForget) {
157 mSpecialCollections->d->forgetFoldersForResource(resourceId);
158 }
159
160 // Register all the collections that we fetched / created.
161 typedef QPair<Collection, QByteArray> RegisterPair;
162 foreach (const RegisterPair &pair, mToRegister) {
163 const bool ok = mSpecialCollections->registerCollection(pair.second, pair.first);
164 Q_ASSERT(ok);
165 Q_UNUSED(ok);
166 }
167
168 mSpecialCollections->d->endBatchRegister();
169
170 // Release the lock once the transaction has been committed.
171 QObject::connect(q, SIGNAL(result(KJob*)), q, SLOT(releaseLock()));
172
173 // We are done!
174 q->commit();
175
176 } else {
177 const QString resourceId = mFoldersForResource.keys().first();
178 kDebug() << "A resource is done," << mFoldersForResource.count()
179 << "more to do. Now doing resource" << resourceId;
180 ResourceScanJob *resjob = new ResourceScanJob(resourceId, mSpecialCollections->d->mSettings, q);
181 QObject::connect(resjob, SIGNAL(result(KJob*)), q, SLOT(resourceScanResult(KJob*)));
182 }
183}
184
185void SpecialCollectionsRequestJobPrivate::resourceScanResult(KJob *job)
186{
187 ResourceScanJob *resjob = qobject_cast<ResourceScanJob *>(job);
188 Q_ASSERT(resjob);
189
190 const QString resourceId = resjob->resourceId();
191 kDebug() << "resourceId" << resourceId;
192
193 if (job->error()) {
194 kWarning() << "Failed to request resource" << resourceId << ":" << job->errorString();
195 return;
196 }
197
198 if (qobject_cast<DefaultResourceJob *>(job)) {
199 // This is the default resource.
200 if (resourceId != mSpecialCollections->d->defaultResourceId()) {
201 kError() << "Resource id's don't match: " << resourceId
202 << mSpecialCollections->d->defaultResourceId();
203 Q_ASSERT(false);
204 }
205 //mToForget.append( mSpecialCollections->defaultResourceId() );
206 createRequestedFolders(resjob, mDefaultFolders);
207 } else {
208 // This is not the default resource.
209 QHash<QByteArray, bool> requestedFolders = mFoldersForResource[resourceId];
210 mFoldersForResource.remove(resourceId);
211 createRequestedFolders(resjob, requestedFolders);
212 }
213}
214
215void SpecialCollectionsRequestJobPrivate::createRequestedFolders(ResourceScanJob *scanJob,
216 QHash<QByteArray, bool> &requestedFolders)
217{
218 // Remove from the request list the folders which already exist.
219 foreach (const Collection &collection, scanJob->specialCollections()) {
220 Q_ASSERT(collection.hasAttribute<SpecialCollectionAttribute>());
221 const SpecialCollectionAttribute *attr = collection.attribute<SpecialCollectionAttribute>();
222 const QByteArray type = attr->collectionType();
223
224 if (!type.isEmpty()) {
225 mToRegister.append(qMakePair(collection, type));
226 requestedFolders.insert(type, false);
227 }
228 }
229 mToForget.append(scanJob->resourceId());
230
231 // Folders left in the request list must be created.
232 Q_ASSERT(mPendingCreateJobs == 0);
233 Q_ASSERT(scanJob->rootResourceCollection().isValid());
234
235 QHashIterator<QByteArray, bool> it(requestedFolders);
236 while (it.hasNext()) {
237 it.next();
238
239 if (it.value()) {
240 Collection collection;
241 collection.setParentCollection(scanJob->rootResourceCollection());
242 collection.setName(mNameForTypeMap.value(it.key()));
243
244 setCollectionAttributes(collection, it.key(), mNameForTypeMap, mIconForTypeMap);
245
246 CollectionCreateJob *createJob = new CollectionCreateJob(collection, q);
247 createJob->setProperty("type", it.key());
248 QObject::connect(createJob, SIGNAL(result(KJob*)), q, SLOT(collectionCreateResult(KJob*)));
249
250 mPendingCreateJobs++;
251 }
252 }
253
254 if (mPendingCreateJobs == 0) {
255 nextResource();
256 }
257}
258
259void SpecialCollectionsRequestJobPrivate::collectionCreateResult(KJob *job)
260{
261 if (job->error()) {
262 kWarning() << "Failed CollectionCreateJob." << job->errorString();
263 return;
264 }
265
266 CollectionCreateJob *createJob = qobject_cast<CollectionCreateJob *>(job);
267 Q_ASSERT(createJob);
268
269 const Collection collection = createJob->collection();
270 mToRegister.append(qMakePair(collection, createJob->property("type").toByteArray()));
271
272 Q_ASSERT(mPendingCreateJobs > 0);
273 mPendingCreateJobs--;
274 kDebug() << "mPendingCreateJobs now" << mPendingCreateJobs;
275
276 if (mPendingCreateJobs == 0) {
277 nextResource();
278 }
279}
280
281// TODO KDE5: do not inherit from TransactionSequence
282SpecialCollectionsRequestJob::SpecialCollectionsRequestJob(SpecialCollections *collections, QObject *parent)
283 : TransactionSequence(parent)
284 , d(new SpecialCollectionsRequestJobPrivate(collections, this))
285{
286 setProperty("transactionsDisabled", true);
287}
288
289SpecialCollectionsRequestJob::~SpecialCollectionsRequestJob()
290{
291 delete d;
292}
293
294void SpecialCollectionsRequestJob::requestDefaultCollection(const QByteArray &type)
295{
296 d->mDefaultFolders[type] = true;
297 d->mRequestingDefaultFolders = true;
298 d->mRequestedType = type;
299}
300
301void SpecialCollectionsRequestJob::requestCollection(const QByteArray &type, const AgentInstance &instance)
302{
303 d->mFoldersForResource[instance.identifier()][type] = true;
304 d->mRequestedType = type;
305 d->mRequestedResource = instance;
306}
307
308Akonadi::Collection SpecialCollectionsRequestJob::collection() const
309{
310 if (d->mRequestedResource.isValid()) {
311 return d->mSpecialCollections->collection(d->mRequestedType, d->mRequestedResource);
312 } else {
313 return d->mSpecialCollections->defaultCollection(d->mRequestedType);
314 }
315}
316
317void SpecialCollectionsRequestJob::setDefaultResourceType(const QString &type)
318{
319 d->mDefaultResourceType = type;
320}
321
322void SpecialCollectionsRequestJob::setDefaultResourceOptions(const QVariantMap &options)
323{
324 d->mDefaultResourceOptions = options;
325}
326
327void SpecialCollectionsRequestJob::setTypes(const QList<QByteArray> &types)
328{
329 d->mKnownTypes = types;
330}
331
332void SpecialCollectionsRequestJob::setNameForTypeMap(const QMap<QByteArray, QString> &map)
333{
334 d->mNameForTypeMap = map;
335}
336
337void SpecialCollectionsRequestJob::setIconForTypeMap(const QMap<QByteArray, QString> &map)
338{
339 d->mIconForTypeMap = map;
340}
341
342void SpecialCollectionsRequestJob::doStart()
343{
344 if (d->isEverythingReady()) {
345 emitResult();
346 } else {
347 GetLockJob *lockJob = new GetLockJob(this);
348 connect(lockJob, SIGNAL(result(KJob*)), this, SLOT(lockResult(KJob*)));
349 lockJob->start();
350 }
351}
352
353void SpecialCollectionsRequestJob::slotResult(KJob *job)
354{
355 if (job->error()) {
356 // If we failed, let others try.
357 kWarning() << "Failed SpecialCollectionsRequestJob::slotResult" << job->errorString();
358
359 d->releaseLock();
360 }
361 TransactionSequence::slotResult(job);
362}
363
364#include "moc_specialcollectionsrequestjob.cpp"
365