Akonadi

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

KDE's Doxygen guidelines are available online.