Akonadi

recursivemover.cpp
1 /*
2  SPDX-FileCopyrightText: 2012 Volker Krause <[email protected]>
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 
13 using namespace Akonadi;
14 
15 RecursiveMover::RecursiveMover(AgentBasePrivate *parent)
16  : KCompositeJob(parent)
17  , m_agentBase(parent)
18  , m_currentAction(None)
19 {
20 }
21 
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 
32 void 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 
39 void 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 
77 void 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 
100 void 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 
119 void 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 
139 void 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 
164 void 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 
181 void 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 
197 void 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"
const T value(const Key &key) const const
T dequeue()
bool isEmpty() const const
void finished(KJob *job)
void result(KJob *job)
@ All
Retrieve all ancestors, up to Collection::root()
Q_SCRIPTABLE Q_NOREPLY void start()
void push_back(const T &value)
Represents a collection of PIM items.
Definition: collection.h:61
QHash::iterator insert(const Key &key, const T &value)
Job that fetches collections from the Akonadi storage.
bool isEmpty() const const
@ Base
Only fetch the base collection.
Job that fetches items from the Akonadi storage.
Definition: itemfetchjob.h:69
@ Recursive
List all sub-collections.
int error() const
Represents a PIM item stored in Akonadi storage.
Definition: item.h:104
Helper integration between Akonadi and Qt.
void enqueue(const T &t)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jun 25 2022 06:00:32 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.