File indexing completed on 2025-01-05 04:46:24
0001 /* 0002 SPDX-FileCopyrightText: 2012 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "collectionfetchjob.h" 0008 #include "collectionfetchscope.h" 0009 #include "itemfetchjob.h" 0010 #include "itemfetchscope.h" 0011 #include "recursivemover_p.h" 0012 0013 using namespace Akonadi; 0014 0015 RecursiveMover::RecursiveMover(AgentBasePrivate *parent) 0016 : KCompositeJob(parent) 0017 , m_agentBase(parent) 0018 , m_currentAction(None) 0019 { 0020 } 0021 0022 void RecursiveMover::start() 0023 { 0024 Q_ASSERT(receivers(SIGNAL(result(KJob *)))); 0025 0026 auto job = new CollectionFetchJob(m_movedCollection, CollectionFetchJob::Recursive, this); 0027 connect(job, &CollectionFetchJob::finished, this, &RecursiveMover::collectionListResult); 0028 addSubjob(job); 0029 ++m_runningJobs; 0030 } 0031 0032 void RecursiveMover::setCollection(const Collection &collection, const Collection &parentCollection) 0033 { 0034 m_movedCollection = collection; 0035 m_collections.insert(collection.id(), m_movedCollection); 0036 m_collections.insert(parentCollection.id(), parentCollection); 0037 } 0038 0039 void RecursiveMover::collectionListResult(KJob *job) 0040 { 0041 Q_ASSERT(m_pendingCollections.isEmpty()); 0042 --m_runningJobs; 0043 0044 if (job->error()) { 0045 return; // error handling is in the base class 0046 } 0047 0048 // build a parent -> children map for the following topological sorting 0049 // while we are iterating anyway, also fill m_collections here 0050 auto fetchJob = qobject_cast<CollectionFetchJob *>(job); 0051 QHash<Collection::Id, Collection::List> colTree; 0052 const Akonadi::Collection::List lstCol = fetchJob->collections(); 0053 for (const Collection &col : lstCol) { 0054 colTree[col.parentCollection().id()] << col; 0055 m_collections.insert(col.id(), col); 0056 } 0057 0058 // topological sort; BFS traversal of the tree 0059 m_pendingCollections.push_back(m_movedCollection); 0060 QQueue<Collection> toBeProcessed; 0061 toBeProcessed.enqueue(m_movedCollection); 0062 while (!toBeProcessed.isEmpty()) { 0063 const Collection col = toBeProcessed.dequeue(); 0064 const Collection::List children = colTree.value(col.id()); 0065 if (children.isEmpty()) { 0066 continue; 0067 } 0068 m_pendingCollections += children; 0069 for (const Collection &child : children) { 0070 toBeProcessed.enqueue(child); 0071 } 0072 } 0073 0074 replayNextCollection(); 0075 } 0076 0077 void RecursiveMover::collectionFetchResult(KJob *job) 0078 { 0079 Q_ASSERT(m_currentCollection.isValid()); 0080 --m_runningJobs; 0081 0082 if (job->error()) { 0083 return; // error handling is in the base class 0084 } 0085 0086 auto fetchJob = qobject_cast<CollectionFetchJob *>(job); 0087 if (fetchJob->collections().size() == 1) { 0088 m_currentCollection = fetchJob->collections().at(0); 0089 m_currentCollection.setParentCollection(m_collections.value(m_currentCollection.parentCollection().id())); 0090 m_collections.insert(m_currentCollection.id(), m_currentCollection); 0091 } else { 0092 // already deleted, move on 0093 } 0094 0095 if (!m_runningJobs && m_pendingReplay) { 0096 replayNext(); 0097 } 0098 } 0099 0100 void RecursiveMover::itemListResult(KJob *job) 0101 { 0102 --m_runningJobs; 0103 0104 if (job->error()) { 0105 return; // error handling is in the base class 0106 } 0107 const Akonadi::Item::List lstItems = qobject_cast<ItemFetchJob *>(job)->items(); 0108 for (const Item &item : lstItems) { 0109 if (item.remoteId().isEmpty()) { 0110 m_pendingItems.push_back(item); 0111 } 0112 } 0113 0114 if (!m_runningJobs && m_pendingReplay) { 0115 replayNext(); 0116 } 0117 } 0118 0119 void RecursiveMover::itemFetchResult(KJob *job) 0120 { 0121 Q_ASSERT(m_currentAction == None); 0122 --m_runningJobs; 0123 0124 if (job->error()) { 0125 return; // error handling is in the base class 0126 } 0127 0128 auto fetchJob = qobject_cast<ItemFetchJob *>(job); 0129 if (fetchJob->items().size() == 1) { 0130 m_currentAction = AddItem; 0131 m_agentBase->itemAdded(fetchJob->items().at(0), m_currentCollection); 0132 } else { 0133 // deleted since we started, skip 0134 m_currentItem = Item(); 0135 replayNextItem(); 0136 } 0137 } 0138 0139 void RecursiveMover::replayNextCollection() 0140 { 0141 if (!m_pendingCollections.isEmpty()) { 0142 m_currentCollection = m_pendingCollections.takeFirst(); 0143 auto job = new ItemFetchJob(m_currentCollection, this); 0144 connect(job, &ItemFetchJob::result, this, &RecursiveMover::itemListResult); 0145 addSubjob(job); 0146 ++m_runningJobs; 0147 0148 if (m_currentCollection.remoteId().isEmpty()) { 0149 Q_ASSERT(m_currentAction == None); 0150 m_currentAction = AddCollection; 0151 m_agentBase->collectionAdded(m_currentCollection, m_collections.value(m_currentCollection.parentCollection().id())); 0152 return; 0153 } else { 0154 // replayNextItem(); - but waiting for the fetch job to finish first 0155 m_pendingReplay = true; 0156 return; 0157 } 0158 } else { 0159 // nothing left to do 0160 emitResult(); 0161 } 0162 } 0163 0164 void RecursiveMover::replayNextItem() 0165 { 0166 Q_ASSERT(m_currentCollection.isValid()); 0167 if (m_pendingItems.isEmpty()) { 0168 replayNextCollection(); // all items processed here 0169 return; 0170 } else { 0171 Q_ASSERT(m_currentAction == None); 0172 m_currentItem = m_pendingItems.takeFirst(); 0173 auto job = new ItemFetchJob(m_currentItem, this); 0174 job->fetchScope().fetchFullPayload(); 0175 connect(job, &ItemFetchJob::result, this, &RecursiveMover::itemFetchResult); 0176 addSubjob(job); 0177 ++m_runningJobs; 0178 } 0179 } 0180 0181 void RecursiveMover::changeProcessed() 0182 { 0183 Q_ASSERT(m_currentAction != None); 0184 0185 if (m_currentAction == AddCollection) { 0186 Q_ASSERT(m_currentCollection.isValid()); 0187 auto job = new CollectionFetchJob(m_currentCollection, CollectionFetchJob::Base, this); 0188 job->fetchScope().setAncestorRetrieval(CollectionFetchScope::All); 0189 connect(job, &CollectionFetchJob::result, this, &RecursiveMover::collectionFetchResult); 0190 addSubjob(job); 0191 ++m_runningJobs; 0192 } 0193 0194 m_currentAction = None; 0195 } 0196 0197 void RecursiveMover::replayNext() 0198 { 0199 // wait for runnings jobs to finish before actually doing the replay 0200 if (m_runningJobs) { 0201 m_pendingReplay = true; 0202 return; 0203 } 0204 0205 m_pendingReplay = false; 0206 0207 if (m_currentCollection.isValid()) { 0208 replayNextItem(); 0209 } else { 0210 replayNextCollection(); 0211 } 0212 } 0213 0214 #include "moc_recursivemover_p.cpp"