kde-playground/kdepimlibs/akonadi/recursivemover.cpp
2015-04-14 21:49:29 +00:00

229 lines
6.9 KiB
C++

/*
Copyright (c) 2012 Volker Krause <vkrause@kde.org>
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 "recursivemover_p.h"
#include "collectionfetchjob.h"
#include "itemfetchjob.h"
#include "itemfetchscope.h"
#include "collectionfetchscope.h"
using namespace Akonadi;
RecursiveMover::RecursiveMover(AgentBasePrivate *parent)
: KCompositeJob(parent)
, m_agentBase(parent)
, m_currentAction(None)
, m_runningJobs(0)
, m_pendingReplay(false)
{
}
void RecursiveMover::start()
{
Q_ASSERT(receivers(SIGNAL(result(KJob*))));
CollectionFetchJob *job = new CollectionFetchJob(m_movedCollection, CollectionFetchJob::Recursive, this);
connect(job, SIGNAL(finished(KJob*)), SLOT(collectionListResult(KJob*)));
addSubjob(job);
++m_runningJobs;
}
void RecursiveMover::setCollection(const Collection &collection, const Collection &parentCollection)
{
m_movedCollection = collection;
m_collections.insert(collection.id(), m_movedCollection);
m_collections.insert(parentCollection.id(), parentCollection);
}
void RecursiveMover::collectionListResult(KJob *job)
{
Q_ASSERT(m_pendingCollections.isEmpty());
--m_runningJobs;
if (job->error()) {
return; // error handling is in the base class
}
// build a parent -> children map for the following topological sorting
// while we are iterating anyway, also fill m_collections here
CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>(job);
QHash<Collection::Id, Collection::List> colTree;
foreach (const Collection &col, fetchJob->collections()) {
colTree[col.parentCollection().id()] << col;
m_collections.insert(col.id(), col);
}
// topological sort; BFS traversal of the tree
m_pendingCollections.push_back(m_movedCollection);
QQueue<Collection> toBeProcessed;
toBeProcessed.enqueue(m_movedCollection);
while (!toBeProcessed.isEmpty()) {
const Collection col = toBeProcessed.dequeue();
const Collection::List children = colTree.value(col.id());
if (children.isEmpty()) {
continue;
}
m_pendingCollections.append(children);
foreach (const Collection &child, children) {
toBeProcessed.enqueue(child);
}
}
replayNextCollection();
}
void RecursiveMover::collectionFetchResult(KJob *job)
{
Q_ASSERT(m_currentCollection.isValid());
--m_runningJobs;
if (job->error()) {
return; // error handling is in the base class
}
CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>(job);
if (fetchJob->collections().size() == 1) {
m_currentCollection = fetchJob->collections().first();
m_currentCollection.setParentCollection(m_collections.value(m_currentCollection.parentCollection().id()));
m_collections.insert(m_currentCollection.id(), m_currentCollection);
} else {
// already deleted, move on
}
if (!m_runningJobs && m_pendingReplay) {
replayNext();
}
}
void RecursiveMover::itemListResult(KJob *job)
{
--m_runningJobs;
if (job->error()) {
return; // error handling is in the base class
}
foreach (const Item &item, qobject_cast<ItemFetchJob *>(job)->items()) {
if (item.remoteId().isEmpty()) {
m_pendingItems.push_back(item);
}
}
if (!m_runningJobs && m_pendingReplay) {
replayNext();
}
}
void RecursiveMover::itemFetchResult(KJob *job)
{
Q_ASSERT(m_currentAction == None);
--m_runningJobs;
if (job->error()) {
return; // error handling is in the base class
}
ItemFetchJob *fetchJob = qobject_cast<ItemFetchJob *>(job);
if (fetchJob->items().size() == 1) {
m_currentAction = AddItem;
m_agentBase->itemAdded(fetchJob->items().first(), m_currentCollection);
} else {
// deleted since we started, skip
m_currentItem = Item();
replayNextItem();
}
}
void RecursiveMover::replayNextCollection()
{
if (!m_pendingCollections.isEmpty()) {
m_currentCollection = m_pendingCollections.takeFirst();
ItemFetchJob *job = new ItemFetchJob(m_currentCollection, this);
connect(job, SIGNAL(result(KJob*)), SLOT(itemListResult(KJob*)));
addSubjob(job);
++m_runningJobs;
if (m_currentCollection.remoteId().isEmpty()) {
Q_ASSERT(m_currentAction == None);
m_currentAction = AddCollection;
m_agentBase->collectionAdded(m_currentCollection, m_collections.value(m_currentCollection.parentCollection().id()));
return;
} else {
//replayNextItem(); - but waiting for the fetch job to finish first
m_pendingReplay = true;
return;
}
} else {
// nothing left to do
emitResult();
}
}
void RecursiveMover::replayNextItem()
{
Q_ASSERT(m_currentCollection.isValid());
if (m_pendingItems.isEmpty()) {
replayNextCollection(); // all items processed here
return;
} else {
Q_ASSERT(m_currentAction == None);
m_currentItem = m_pendingItems.takeFirst();
ItemFetchJob *job = new ItemFetchJob(m_currentItem, this);
job->fetchScope().fetchFullPayload();
connect(job, SIGNAL(result(KJob*)), SLOT(itemFetchResult(KJob*)));
addSubjob(job);
++m_runningJobs;
}
}
void RecursiveMover::changeProcessed()
{
Q_ASSERT(m_currentAction != None);
if (m_currentAction == AddCollection) {
Q_ASSERT(m_currentCollection.isValid());
CollectionFetchJob *job = new CollectionFetchJob(m_currentCollection, CollectionFetchJob::Base, this);
job->fetchScope().setAncestorRetrieval(CollectionFetchScope::All);
connect(job, SIGNAL(result(KJob*)), SLOT(collectionFetchResult(KJob*)));
addSubjob(job);
++m_runningJobs;
}
m_currentAction = None;
}
void RecursiveMover::replayNext()
{
// wait for runnings jobs to finish before actually doing the replay
if (m_runningJobs) {
m_pendingReplay = true;
return;
}
m_pendingReplay = false;
if (m_currentCollection.isValid()) {
replayNextItem();
} else {
replayNextCollection();
}
}
#include "moc_recursivemover_p.cpp"