Akonadi

recursivemover.cpp
1/*
2 SPDX-FileCopyrightText: 2012 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "collectionfetchjob.h"
8#include "collectionfetchscope.h"
9#include "itemfetchjob.h"
10#include "itemfetchscope.h"
11#include "recursivemover_p.h"
12
13using namespace Akonadi;
14
15RecursiveMover::RecursiveMover(AgentBasePrivate *parent)
16 : KCompositeJob(parent)
17 , m_agentBase(parent)
18 , m_currentAction(None)
19{
20}
21
22void RecursiveMover::start()
23{
24 Q_ASSERT(receivers(SIGNAL(result(KJob *))));
25
26 auto job = new CollectionFetchJob(m_movedCollection, CollectionFetchJob::Recursive, this);
27 connect(job, &CollectionFetchJob::finished, this, &RecursiveMover::collectionListResult);
28 addSubjob(job);
29 ++m_runningJobs;
30}
31
32void RecursiveMover::setCollection(const Collection &collection, const Collection &parentCollection)
33{
34 m_movedCollection = collection;
35 m_collections.insert(collection.id(), m_movedCollection);
36 m_collections.insert(parentCollection.id(), parentCollection);
37}
38
39void RecursiveMover::collectionListResult(KJob *job)
40{
41 Q_ASSERT(m_pendingCollections.isEmpty());
42 --m_runningJobs;
43
44 if (job->error()) {
45 return; // error handling is in the base class
46 }
47
48 // build a parent -> children map for the following topological sorting
49 // while we are iterating anyway, also fill m_collections here
50 auto fetchJob = qobject_cast<CollectionFetchJob *>(job);
52 const Akonadi::Collection::List lstCol = fetchJob->collections();
53 for (const Collection &col : lstCol) {
54 colTree[col.parentCollection().id()] << col;
55 m_collections.insert(col.id(), col);
56 }
57
58 // topological sort; BFS traversal of the tree
59 m_pendingCollections.push_back(m_movedCollection);
60 QQueue<Collection> toBeProcessed;
61 toBeProcessed.enqueue(m_movedCollection);
62 while (!toBeProcessed.isEmpty()) {
63 const Collection col = toBeProcessed.dequeue();
64 const Collection::List children = colTree.value(col.id());
65 if (children.isEmpty()) {
66 continue;
67 }
68 m_pendingCollections += children;
69 for (const Collection &child : children) {
70 toBeProcessed.enqueue(child);
71 }
72 }
73
74 replayNextCollection();
75}
76
77void RecursiveMover::collectionFetchResult(KJob *job)
78{
79 Q_ASSERT(m_currentCollection.isValid());
80 --m_runningJobs;
81
82 if (job->error()) {
83 return; // error handling is in the base class
84 }
85
86 auto fetchJob = qobject_cast<CollectionFetchJob *>(job);
87 if (fetchJob->collections().size() == 1) {
88 m_currentCollection = fetchJob->collections().at(0);
89 m_currentCollection.setParentCollection(m_collections.value(m_currentCollection.parentCollection().id()));
90 m_collections.insert(m_currentCollection.id(), m_currentCollection);
91 } else {
92 // already deleted, move on
93 }
94
95 if (!m_runningJobs && m_pendingReplay) {
96 replayNext();
97 }
98}
99
100void RecursiveMover::itemListResult(KJob *job)
101{
102 --m_runningJobs;
103
104 if (job->error()) {
105 return; // error handling is in the base class
106 }
107 const Akonadi::Item::List lstItems = qobject_cast<ItemFetchJob *>(job)->items();
108 for (const Item &item : lstItems) {
109 if (item.remoteId().isEmpty()) {
110 m_pendingItems.push_back(item);
111 }
112 }
113
114 if (!m_runningJobs && m_pendingReplay) {
115 replayNext();
116 }
117}
118
119void RecursiveMover::itemFetchResult(KJob *job)
120{
121 Q_ASSERT(m_currentAction == None);
122 --m_runningJobs;
123
124 if (job->error()) {
125 return; // error handling is in the base class
126 }
127
128 auto fetchJob = qobject_cast<ItemFetchJob *>(job);
129 if (fetchJob->items().size() == 1) {
130 m_currentAction = AddItem;
131 m_agentBase->itemAdded(fetchJob->items().at(0), m_currentCollection);
132 } else {
133 // deleted since we started, skip
134 m_currentItem = Item();
135 replayNextItem();
136 }
137}
138
139void RecursiveMover::replayNextCollection()
140{
141 if (!m_pendingCollections.isEmpty()) {
142 m_currentCollection = m_pendingCollections.takeFirst();
143 auto job = new ItemFetchJob(m_currentCollection, this);
144 connect(job, &ItemFetchJob::result, this, &RecursiveMover::itemListResult);
145 addSubjob(job);
146 ++m_runningJobs;
147
148 if (m_currentCollection.remoteId().isEmpty()) {
149 Q_ASSERT(m_currentAction == None);
150 m_currentAction = AddCollection;
151 m_agentBase->collectionAdded(m_currentCollection, m_collections.value(m_currentCollection.parentCollection().id()));
152 return;
153 } else {
154 // replayNextItem(); - but waiting for the fetch job to finish first
155 m_pendingReplay = true;
156 return;
157 }
158 } else {
159 // nothing left to do
160 emitResult();
161 }
162}
163
164void RecursiveMover::replayNextItem()
165{
166 Q_ASSERT(m_currentCollection.isValid());
167 if (m_pendingItems.isEmpty()) {
168 replayNextCollection(); // all items processed here
169 return;
170 } else {
171 Q_ASSERT(m_currentAction == None);
172 m_currentItem = m_pendingItems.takeFirst();
173 auto job = new ItemFetchJob(m_currentItem, this);
174 job->fetchScope().fetchFullPayload();
175 connect(job, &ItemFetchJob::result, this, &RecursiveMover::itemFetchResult);
176 addSubjob(job);
177 ++m_runningJobs;
178 }
179}
180
181void RecursiveMover::changeProcessed()
182{
183 Q_ASSERT(m_currentAction != None);
184
185 if (m_currentAction == AddCollection) {
186 Q_ASSERT(m_currentCollection.isValid());
187 auto job = new CollectionFetchJob(m_currentCollection, CollectionFetchJob::Base, this);
188 job->fetchScope().setAncestorRetrieval(CollectionFetchScope::All);
189 connect(job, &CollectionFetchJob::result, this, &RecursiveMover::collectionFetchResult);
190 addSubjob(job);
191 ++m_runningJobs;
192 }
193
194 m_currentAction = None;
195}
196
197void RecursiveMover::replayNext()
198{
199 // wait for runnings jobs to finish before actually doing the replay
200 if (m_runningJobs) {
201 m_pendingReplay = true;
202 return;
203 }
204
205 m_pendingReplay = false;
206
207 if (m_currentCollection.isValid()) {
208 replayNextItem();
209 } else {
210 replayNextCollection();
211 }
212}
213
214#include "moc_recursivemover_p.cpp"
Job that fetches collections from the Akonadi storage.
@ Recursive
List all sub-collections.
@ Base
Only fetch the base collection.
Represents a collection of PIM items.
Definition collection.h:62
Job that fetches items from the Akonadi storage.
int error() const
Helper integration between Akonadi and Qt.
iterator insert(const Key &key, const T &value)
T value(const Key &key) const const
bool isEmpty() const const
void push_back(parameter_type value)
T dequeue()
void enqueue(const T &t)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:38 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.