/* Copyright (c) 2009 Constantin Berzan This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "specialcollectionsrequestjob.h" #include "specialcollectionattribute_p.h" #include "specialcollections.h" #include "specialcollections_p.h" #include "specialcollectionshelperjobs_p.h" #include "akonadi/agentmanager.h" #include "akonadi/collectioncreatejob.h" #include "akonadi/entitydisplayattribute.h" #include #include using namespace Akonadi; /** @internal */ class Akonadi::SpecialCollectionsRequestJobPrivate { public: SpecialCollectionsRequestJobPrivate(SpecialCollections *collections, SpecialCollectionsRequestJob *qq); bool isEverythingReady(); void lockResult(KJob *job); // slot void releaseLock(); // slot void nextResource(); void resourceScanResult(KJob *job); // slot void createRequestedFolders(ResourceScanJob *job, QHash &requestedFolders); void collectionCreateResult(KJob *job); // slot SpecialCollectionsRequestJob *q; SpecialCollections *mSpecialCollections; int mPendingCreateJobs; QByteArray mRequestedType; AgentInstance mRequestedResource; // Input: QHash mDefaultFolders; bool mRequestingDefaultFolders; QHash< QString, QHash > mFoldersForResource; QString mDefaultResourceType; QVariantMap mDefaultResourceOptions; QList mKnownTypes; QMap mNameForTypeMap; QMap mIconForTypeMap; // Output: QStringList mToForget; QVector< QPair > mToRegister; }; SpecialCollectionsRequestJobPrivate::SpecialCollectionsRequestJobPrivate(SpecialCollections *collections, SpecialCollectionsRequestJob *qq) : q(qq) , mSpecialCollections(collections) , mPendingCreateJobs(0) , mRequestingDefaultFolders(false) { } bool SpecialCollectionsRequestJobPrivate::isEverythingReady() { // check if all requested folders are known already if (mRequestingDefaultFolders) { QHashIterator it(mDefaultFolders); while (it.hasNext()) { it.next(); if (it.value() && !mSpecialCollections->hasDefaultCollection(it.key())) { return false; } } } const QStringList resourceIds = mFoldersForResource.keys(); QHashIterator > resourceIt(mFoldersForResource); while (resourceIt.hasNext()) { resourceIt.next(); const QHash &requested = resourceIt.value(); QHashIterator it(requested); while (it.hasNext()) { it.next(); if (it.value() && !mSpecialCollections->hasCollection(it.key(), AgentManager::self()->instance(resourceIt.key()))) { return false; } } } return true; } void SpecialCollectionsRequestJobPrivate::lockResult(KJob *job) { if (job->error()) { kWarning() << "Failed to get lock:" << job->errorString(); q->setError(job->error()); q->setErrorText(job->errorString()); q->emitResult(); return; } if (mRequestingDefaultFolders) { // If default folders are requested, deal with that first. DefaultResourceJob *resjob = new DefaultResourceJob(mSpecialCollections->d->mSettings, q); resjob->setDefaultResourceType(mDefaultResourceType); resjob->setDefaultResourceOptions(mDefaultResourceOptions); resjob->setTypes(mKnownTypes); resjob->setNameForTypeMap(mNameForTypeMap); resjob->setIconForTypeMap(mIconForTypeMap); QObject::connect(resjob, SIGNAL(result(KJob*)), q, SLOT(resourceScanResult(KJob*))); } else { // If no default folders are requested, go straight to the next step. nextResource(); } } void SpecialCollectionsRequestJobPrivate::releaseLock() { const bool ok = Akonadi::releaseLock(); if (!ok) { kWarning() << "WTF, can't release lock."; } } void SpecialCollectionsRequestJobPrivate::nextResource() { if (mFoldersForResource.isEmpty()) { kDebug() << "All done! Comitting."; mSpecialCollections->d->beginBatchRegister(); // Forget everything we knew before about these resources. foreach (const QString &resourceId, mToForget) { mSpecialCollections->d->forgetFoldersForResource(resourceId); } // Register all the collections that we fetched / created. typedef QPair RegisterPair; foreach (const RegisterPair &pair, mToRegister) { const bool ok = mSpecialCollections->registerCollection(pair.second, pair.first); Q_ASSERT(ok); Q_UNUSED(ok); } mSpecialCollections->d->endBatchRegister(); // Release the lock once the transaction has been committed. QObject::connect(q, SIGNAL(result(KJob*)), q, SLOT(releaseLock())); // We are done! q->commit(); } else { const QString resourceId = mFoldersForResource.keys().first(); kDebug() << "A resource is done," << mFoldersForResource.count() << "more to do. Now doing resource" << resourceId; ResourceScanJob *resjob = new ResourceScanJob(resourceId, mSpecialCollections->d->mSettings, q); QObject::connect(resjob, SIGNAL(result(KJob*)), q, SLOT(resourceScanResult(KJob*))); } } void SpecialCollectionsRequestJobPrivate::resourceScanResult(KJob *job) { ResourceScanJob *resjob = qobject_cast(job); Q_ASSERT(resjob); const QString resourceId = resjob->resourceId(); kDebug() << "resourceId" << resourceId; if (job->error()) { kWarning() << "Failed to request resource" << resourceId << ":" << job->errorString(); return; } if (qobject_cast(job)) { // This is the default resource. if (resourceId != mSpecialCollections->d->defaultResourceId()) { kError() << "Resource id's don't match: " << resourceId << mSpecialCollections->d->defaultResourceId(); Q_ASSERT(false); } //mToForget.append( mSpecialCollections->defaultResourceId() ); createRequestedFolders(resjob, mDefaultFolders); } else { // This is not the default resource. QHash requestedFolders = mFoldersForResource[resourceId]; mFoldersForResource.remove(resourceId); createRequestedFolders(resjob, requestedFolders); } } void SpecialCollectionsRequestJobPrivate::createRequestedFolders(ResourceScanJob *scanJob, QHash &requestedFolders) { // Remove from the request list the folders which already exist. foreach (const Collection &collection, scanJob->specialCollections()) { Q_ASSERT(collection.hasAttribute()); const SpecialCollectionAttribute *attr = collection.attribute(); const QByteArray type = attr->collectionType(); if (!type.isEmpty()) { mToRegister.append(qMakePair(collection, type)); requestedFolders.insert(type, false); } } mToForget.append(scanJob->resourceId()); // Folders left in the request list must be created. Q_ASSERT(mPendingCreateJobs == 0); Q_ASSERT(scanJob->rootResourceCollection().isValid()); QHashIterator it(requestedFolders); while (it.hasNext()) { it.next(); if (it.value()) { Collection collection; collection.setParentCollection(scanJob->rootResourceCollection()); collection.setName(mNameForTypeMap.value(it.key())); setCollectionAttributes(collection, it.key(), mNameForTypeMap, mIconForTypeMap); CollectionCreateJob *createJob = new CollectionCreateJob(collection, q); createJob->setProperty("type", it.key()); QObject::connect(createJob, SIGNAL(result(KJob*)), q, SLOT(collectionCreateResult(KJob*))); mPendingCreateJobs++; } } if (mPendingCreateJobs == 0) { nextResource(); } } void SpecialCollectionsRequestJobPrivate::collectionCreateResult(KJob *job) { if (job->error()) { kWarning() << "Failed CollectionCreateJob." << job->errorString(); return; } CollectionCreateJob *createJob = qobject_cast(job); Q_ASSERT(createJob); const Collection collection = createJob->collection(); mToRegister.append(qMakePair(collection, createJob->property("type").toByteArray())); Q_ASSERT(mPendingCreateJobs > 0); mPendingCreateJobs--; kDebug() << "mPendingCreateJobs now" << mPendingCreateJobs; if (mPendingCreateJobs == 0) { nextResource(); } } // TODO KDE5: do not inherit from TransactionSequence SpecialCollectionsRequestJob::SpecialCollectionsRequestJob(SpecialCollections *collections, QObject *parent) : TransactionSequence(parent) , d(new SpecialCollectionsRequestJobPrivate(collections, this)) { setProperty("transactionsDisabled", true); } SpecialCollectionsRequestJob::~SpecialCollectionsRequestJob() { delete d; } void SpecialCollectionsRequestJob::requestDefaultCollection(const QByteArray &type) { d->mDefaultFolders[type] = true; d->mRequestingDefaultFolders = true; d->mRequestedType = type; } void SpecialCollectionsRequestJob::requestCollection(const QByteArray &type, const AgentInstance &instance) { d->mFoldersForResource[instance.identifier()][type] = true; d->mRequestedType = type; d->mRequestedResource = instance; } Akonadi::Collection SpecialCollectionsRequestJob::collection() const { if (d->mRequestedResource.isValid()) { return d->mSpecialCollections->collection(d->mRequestedType, d->mRequestedResource); } else { return d->mSpecialCollections->defaultCollection(d->mRequestedType); } } void SpecialCollectionsRequestJob::setDefaultResourceType(const QString &type) { d->mDefaultResourceType = type; } void SpecialCollectionsRequestJob::setDefaultResourceOptions(const QVariantMap &options) { d->mDefaultResourceOptions = options; } void SpecialCollectionsRequestJob::setTypes(const QList &types) { d->mKnownTypes = types; } void SpecialCollectionsRequestJob::setNameForTypeMap(const QMap &map) { d->mNameForTypeMap = map; } void SpecialCollectionsRequestJob::setIconForTypeMap(const QMap &map) { d->mIconForTypeMap = map; } void SpecialCollectionsRequestJob::doStart() { if (d->isEverythingReady()) { emitResult(); } else { GetLockJob *lockJob = new GetLockJob(this); connect(lockJob, SIGNAL(result(KJob*)), this, SLOT(lockResult(KJob*))); lockJob->start(); } } void SpecialCollectionsRequestJob::slotResult(KJob *job) { if (job->error()) { // If we failed, let others try. kWarning() << "Failed SpecialCollectionsRequestJob::slotResult" << job->errorString(); d->releaseLock(); } TransactionSequence::slotResult(job); } #include "moc_specialcollectionsrequestjob.cpp"