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 | |
35 | using namespace Akonadi; |
36 | |
37 | /** |
38 | @internal |
39 | */ |
40 | class Akonadi::SpecialCollectionsRequestJobPrivate |
41 | { |
42 | public: |
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 | |
75 | SpecialCollectionsRequestJobPrivate::SpecialCollectionsRequestJobPrivate(SpecialCollections *collections, |
76 | SpecialCollectionsRequestJob *qq) |
77 | : q(qq) |
78 | , mSpecialCollections(collections) |
79 | , mPendingCreateJobs(0) |
80 | , mRequestingDefaultFolders(false) |
81 | { |
82 | } |
83 | |
84 | bool 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 | |
115 | void 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 | |
140 | void SpecialCollectionsRequestJobPrivate::releaseLock() |
141 | { |
142 | const bool ok = Akonadi::releaseLock(); |
143 | if (!ok) { |
144 | kWarning() << "WTF, can't release lock." ; |
145 | } |
146 | } |
147 | |
148 | void 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 | |
185 | void 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 | |
215 | void 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 | |
259 | void 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 |
282 | SpecialCollectionsRequestJob::SpecialCollectionsRequestJob(SpecialCollections *collections, QObject *parent) |
283 | : TransactionSequence(parent) |
284 | , d(new SpecialCollectionsRequestJobPrivate(collections, this)) |
285 | { |
286 | setProperty("transactionsDisabled" , true); |
287 | } |
288 | |
289 | SpecialCollectionsRequestJob::~SpecialCollectionsRequestJob() |
290 | { |
291 | delete d; |
292 | } |
293 | |
294 | void SpecialCollectionsRequestJob::requestDefaultCollection(const QByteArray &type) |
295 | { |
296 | d->mDefaultFolders[type] = true; |
297 | d->mRequestingDefaultFolders = true; |
298 | d->mRequestedType = type; |
299 | } |
300 | |
301 | void 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 | |
308 | Akonadi::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 | |
317 | void SpecialCollectionsRequestJob::setDefaultResourceType(const QString &type) |
318 | { |
319 | d->mDefaultResourceType = type; |
320 | } |
321 | |
322 | void SpecialCollectionsRequestJob::setDefaultResourceOptions(const QVariantMap &options) |
323 | { |
324 | d->mDefaultResourceOptions = options; |
325 | } |
326 | |
327 | void SpecialCollectionsRequestJob::setTypes(const QList<QByteArray> &types) |
328 | { |
329 | d->mKnownTypes = types; |
330 | } |
331 | |
332 | void SpecialCollectionsRequestJob::setNameForTypeMap(const QMap<QByteArray, QString> &map) |
333 | { |
334 | d->mNameForTypeMap = map; |
335 | } |
336 | |
337 | void SpecialCollectionsRequestJob::setIconForTypeMap(const QMap<QByteArray, QString> &map) |
338 | { |
339 | d->mIconForTypeMap = map; |
340 | } |
341 | |
342 | void 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 | |
353 | void 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 | |