Akonadi

itemfetchjob.cpp
1/*
2 SPDX-FileCopyrightText: 2006-2007 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "itemfetchjob.h"
8
9#include "akonadicore_debug.h"
10#include "attributefactory.h"
11#include "collection.h"
12#include "itemfetchscope.h"
13#include "job_p.h"
14#include "private/protocol_p.h"
15#include "protocolhelper_p.h"
16#include "session_p.h"
17#include "tagfetchscope.h"
18
19#include <QTimer>
20
21#include <span>
22
23using namespace Akonadi;
24
25class Akonadi::ItemFetchJobPrivate : public JobPrivate
26{
27 static constexpr std::size_t MaxBatchSize = 10'000UL;
28
29public:
30 explicit ItemFetchJobPrivate(ItemFetchJob *parent)
31 : JobPrivate(parent)
32 , mCollection(Collection::root())
33 {
34 mEmitTimer.setSingleShot(true);
35 mEmitTimer.setInterval(std::chrono::milliseconds{100});
36 }
37
38 ~ItemFetchJobPrivate() override
39 {
40 delete mValuePool;
41 }
42
43 void init()
44 {
45 QObject::connect(&mEmitTimer, &QTimer::timeout, q_ptr, [this]() {
46 timeout();
47 });
48 }
49
50 void aboutToFinish() override
51 {
52 timeout();
53 }
54
55 void timeout()
56 {
57 Q_Q(ItemFetchJob);
58
59 mEmitTimer.stop(); // in case we are called by result()
60 if (!mPendingItems.isEmpty()) {
61 if (!q->error()) {
62 Q_EMIT q->itemsReceived(mPendingItems);
63 }
64 mPendingItems.clear();
65 }
66 }
67
68 bool requestBatch()
69 {
70 if (!mRequestedItems.empty()) {
71 // If there are more items to fetch, but we already received the LIMIT number of items,
72 // we are technically done...
73 if (mItemsLimit.limit() > -1 && mCount >= mItemsLimit.limit()) {
74 return true;
75 }
76
77 const auto batchSize = qMin(MaxBatchSize, mRemainingItems.size());
78 if (batchSize == 0) {
79 // If we would fetch an empty batch, we are done.
80 return true;
81 }
82
83 const auto batch = mRemainingItems.subspan(0, batchSize);
84 mRemainingItems = mRemainingItems.subspan(batch.size());
85 const auto batchItems = QList(batch.begin(), batch.end());
86 sendCommand(Protocol::FetchItemsCommandPtr::create(ProtocolHelper::entitySetToScope(batchItems),
87 ProtocolHelper::commandContextToProtocol(mCollection, mCurrentTag, batchItems),
88 ProtocolHelper::itemFetchScopeToProtocol(mFetchScope),
89 ProtocolHelper::tagFetchScopeToProtocol(mFetchScope.tagFetchScope()),
90 mItemsLimit));
91
92 } else { // collection- or tag-based fetch (unbatched)
93 Q_ASSERT(mCount == 0); // can only be called once
94 sendCommand(Protocol::FetchItemsCommandPtr::create(Scope(),
95 ProtocolHelper::commandContextToProtocol(mCollection, mCurrentTag, mRequestedItems),
96 ProtocolHelper::itemFetchScopeToProtocol(mFetchScope),
97 ProtocolHelper::tagFetchScopeToProtocol(mFetchScope.tagFetchScope()),
98 mItemsLimit));
99 }
100
101 return false;
102 }
103
104 QString jobDebuggingString() const override
105 {
106 if (mRequestedItems.isEmpty()) {
107 QString str = QStringLiteral("All items from collection %1").arg(mCollection.id());
108 if (mFetchScope.fetchChangedSince().isValid()) {
109 str += QStringLiteral(" changed since %1").arg(mFetchScope.fetchChangedSince().toString());
110 }
111 return str;
112
113 } else {
114 try {
115 QString itemStr = QStringLiteral("items id: ");
116 bool firstItem = true;
117 for (const Akonadi::Item &item : std::as_const(mRequestedItems)) {
118 if (firstItem) {
119 firstItem = false;
120 } else {
121 itemStr += QStringLiteral(", ");
122 }
123 itemStr += QString::number(item.id());
124 const Akonadi::Collection parentCollection = item.parentCollection();
125 if (parentCollection.isValid()) {
126 itemStr += QStringLiteral(" from collection %1").arg(parentCollection.id());
127 }
128 }
129 return itemStr;
130 } catch (const Exception &e) {
131 return QString::fromUtf8(e.what());
132 }
133 }
134 }
135
136 Q_DECLARE_PUBLIC(ItemFetchJob)
137
138 Collection mCollection;
139 Tag mCurrentTag;
140 Item::List mRequestedItems;
141 Item::List mResultItems;
142 ItemFetchScope mFetchScope;
143 Item::List mPendingItems; // items pending for emitting itemsReceived()
144 QTimer mEmitTimer;
145 ProtocolHelperValuePool *mValuePool = nullptr;
146 ItemFetchJob::DeliveryOptions mDeliveryOptions = ItemFetchJob::Default;
147 int mCount = 0;
148 Protocol::FetchLimit mItemsLimit;
149 std::span<Item> mRemainingItems;
150};
151
153 : Job(new ItemFetchJobPrivate(this), parent)
154{
156 d->init();
157
158 d->mCollection = collection;
159 d->mValuePool = new ProtocolHelperValuePool; // only worth it for lots of results
160}
161
163 : Job(new ItemFetchJobPrivate(this), parent)
164{
166 d->init();
167
168 d->mRequestedItems.append(item);
169}
170
172 : Job(new ItemFetchJobPrivate(this), parent)
173{
175 d->init();
176
177 d->mRequestedItems = items;
178}
179
181 : Job(new ItemFetchJobPrivate(this), parent)
182{
184 d->init();
185
186 d->mRequestedItems.reserve(items.size());
187 for (auto id : items) {
188 d->mRequestedItems.append(Item(id));
189 }
190}
192 : Job(new ItemFetchJobPrivate(this), parent)
193{
195 d->init();
196
197 d->mCurrentTag = tag;
198 d->mValuePool = new ProtocolHelperValuePool;
199}
200
202
204{
206
207 try {
208 d->mRemainingItems = std::span(d->mRequestedItems.begin(), d->mRequestedItems.size());
209 d->requestBatch();
210 } catch (const Akonadi::Exception &e) {
213 emitResult();
214 return;
215 }
216}
217
218bool ItemFetchJob::doHandleResponse(qint64 tag, const Protocol::CommandPtr &response)
219{
221
222 if (!response->isResponse() || response->type() != Protocol::Command::FetchItems) {
223 return Job::doHandleResponse(tag, response);
224 }
225
226 const auto &resp = Protocol::cmdCast<Protocol::FetchItemsResponse>(response);
227 // Invalid ID marks the last part of the response
228 if (resp.id() < 0) {
229 if (d->mRemainingItems.empty()) {
230 return true;
231 }
232 return d->requestBatch();
233 }
234
235 const Item item = ProtocolHelper::parseItemFetchResult(resp, nullptr, d->mValuePool);
236 if (!item.isValid()) {
237 return false;
238 }
239
240 d->mCount++;
241
242 if (d->mDeliveryOptions & ItemGetter) {
243 d->mResultItems.append(item);
244 }
245
246 if (d->mDeliveryOptions & EmitItemsInBatches) {
247 d->mPendingItems.append(item);
248 if (!d->mEmitTimer.isActive()) {
249 d->mEmitTimer.start();
250 }
251 } else if (d->mDeliveryOptions & EmitItemsIndividually) {
253 }
254
255 return false;
256}
257
259{
260 Q_D(const ItemFetchJob);
261
262 return d->mResultItems;
263}
264
266{
268
269 d->mResultItems.clear();
270}
271
273{
275
276 d->mFetchScope = fetchScope;
277}
278
280{
282
283 return d->mFetchScope;
284}
285
287{
289
290 d->mCollection = collection;
291}
292
293void ItemFetchJob::setDeliveryOption(DeliveryOptions options)
294{
296
297 d->mDeliveryOptions = options;
298}
299
300ItemFetchJob::DeliveryOptions ItemFetchJob::deliveryOptions() const
301{
302 Q_D(const ItemFetchJob);
303
304 return d->mDeliveryOptions;
305}
306
308{
309 Q_D(const ItemFetchJob);
310
311 return d->mCount;
312}
313
314void ItemFetchJob::setLimit(int limit, int start, Qt::SortOrder order)
315{
317 d->mItemsLimit.setLimit(limit);
318 d->mItemsLimit.setLimitOffset(start);
319 d->mItemsLimit.setSortOrder(order);
320}
321#include "moc_itemfetchjob.cpp"
Represents a collection of PIM items.
Definition collection.h:62
Base class for exceptions used by the Akonadi library.
const char * what() const noexcept override
Returns the error message associated with this exception.
Definition exception.cpp:65
int count() const
Returns the total number of retrieved items.
bool doHandleResponse(qint64 tag, const Protocol::CommandPtr &response) override
This method should be reimplemented in the concrete jobs in case you want to handle incoming data.
void itemsReceived(const Akonadi::Item::List &items)
This signal is emitted whenever new items have been fetched completely.
void setDeliveryOption(DeliveryOptions options)
Sets the mechanisms by which the items should be fetched.
ItemFetchJob(const Collection &collection, QObject *parent=nullptr)
Creates a new item fetch job that retrieves all items inside the given collection.
void setFetchScope(const ItemFetchScope &fetchScope)
Sets the item fetch scope.
void setLimit(int limit, int start, Qt::SortOrder order=Qt::DescendingOrder)
Sets the limit of fetched items.
DeliveryOptions deliveryOptions() const
Returns the delivery options.
void setCollection(const Collection &collection)
Specifies the collection the item is in.
void doStart() override
This method must be reimplemented in the concrete jobs.
@ EmitItemsInBatches
emitted via signal in bulk (collected and emitted delayed via timer)
@ ItemGetter
items available through items()
@ EmitItemsIndividually
emitted via signal upon reception
ItemFetchScope & fetchScope()
Returns the item fetch scope.
void clearItems()
Save memory by clearing the fetched items.
~ItemFetchJob() override
Destroys the item fetch job.
Item::List items() const
Returns the fetched items.
Specifies which parts of an item should be fetched from the Akonadi storage.
Represents a PIM item stored in Akonadi storage.
Definition item.h:100
bool isValid() const
Returns whether the item is valid.
Definition item.cpp:88
QList< Item > List
Describes a list of items.
Definition item.h:110
virtual bool doHandleResponse(qint64 tag, const Protocol::CommandPtr &response)
This method should be reimplemented in the concrete jobs in case you want to handle incoming data.
Definition job.cpp:381
Job(QObject *parent=nullptr)
Creates a new job.
Definition job.cpp:290
@ Unknown
Unknown error.
Definition job.h:102
void start() override
Jobs are started automatically once entering the event loop again, no need to explicitly call this.
Definition job.cpp:313
An Akonadi Tag.
Definition tag.h:26
void setErrorText(const QString &errorText)
void emitResult()
void setError(int errorCode)
Helper integration between Akonadi and Qt.
KLEO_EXPORT std::unique_ptr< GpgME::DefaultAssuanTransaction > sendCommand(std::shared_ptr< GpgME::Context > &assuanContext, const std::string &command, GpgME::Error &err)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
QString arg(Args &&... args) const const
QString fromUtf8(QByteArrayView str)
QString number(double n, char format, int precision)
SortOrder
void timeout()
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Mar 28 2025 11:53:21 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.